diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-08-24 13:29:07 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-08-24 13:29:07 +0100 |
commit | f4e8428b9a6ea440bba057ac03ba0355cd87a72f (patch) | |
tree | ca65b40483416b57411a1415563e245538e3e926 | |
parent | 6b699ae1be9f257478d5eca7ef65dcea270a2796 (diff) | |
parent | 239cb6feb298a31faa40b7e97ced107bf9c2f2bf (diff) | |
download | qemu-f4e8428b9a6ea440bba057ac03ba0355cd87a72f.zip qemu-f4e8428b9a6ea440bba057ac03ba0355cd87a72f.tar.gz qemu-f4e8428b9a6ea440bba057ac03ba0355cd87a72f.tar.bz2 |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180824-1' into staging
target-arm queue:
* Fix rounding errors in scaling float-to-int and int-to-float operations
* Connect virtualization-related IRQs and memory regions of GICv2
in boards that use Cortex-A7 or Cortex-A15
* Support taking exceptions to AArch32 Hyp mode
* Clear CPSR.IL and CPSR.J on 32-bit exception entry
(a minor bug fix that won't affect non-buggy guest code)
* mps2-an505: Implement various missing devices:
dual timer, watchdogs, counters in the FPGAIO registers,
some missing ID/control registers, TrustZone Master Security
Controllers, PL081 DMA controllers, PL022 SPI controllers
* correct ID register values for mps2-an385, -an511, -an505
* fix some hardcoded tabs in untouched backwaters of the
target/arm codebase
* raspi: Refactor framebuffer property handling code and implement
support for the virtual framebuffer/viewport
# gpg: Signature made Fri 24 Aug 2018 13:19:04 BST
# gpg: using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg: aka "Peter Maydell <pmaydell@gmail.com>"
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20180824-1: (52 commits)
hw/arm/mps2: Fix ID register errors on AN511 and AN385
hw/display/bcm2835_fb: Validate bcm2835_fb_mbox_push() config
hw/display/bcm2835_fb: Validate config settings
hw/display/bcm2835_fb: Fix handling of virtual framebuffer
hw/display/bcm2835_fb: Abstract out calculation of pitch, size
hw/display/bcm2835_fb: Reset resolution, etc correctly
hw/display/bcm2835_fb: Drop unused size and pitch fields
hw/misc/bcm2835_property: Track fb settings using BCM2835FBConfig
hw/misc/bcm2835_fb: Move config fields to their own struct
target/arm: Remove a handful of stray tabs
target/arm: Untabify iwmmxt_helper.c
target/arm: Untabify translate.c
hw/arm/mps2-tz: Fix MPS2 SCC config register values
hw/arm/mps2-tz: Instantiate SPI controllers
hw/ssi/pl022: Correct wrong DMACR and ICR handling
hw/ssi/pl022: Correct wrong value for PL022_INT_RT
hw/ssi/pl022: Use DeviceState::realize rather than SysBusDevice::init
hw/ssi/pl022: Don't directly call vmstate_register()
hw/ssi/pl022: Set up reset function in class init
hw/ssi/pl022: Allow use as embedded-struct device
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
41 files changed, 3381 insertions, 694 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 68bc92e..53a8b93 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -451,10 +451,14 @@ F: hw/gpio/pl061.c F: hw/input/pl050.c F: hw/intc/pl190.c F: hw/sd/pl181.c +F: hw/ssi/pl022.c +F: include/hw/ssi/pl022.h F: hw/timer/pl031.c F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h +F: hw/timer/cmsdk-apb-dualtimer.c +F: include/hw/timer/cmsdk-apb-dualtimer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h F: hw/watchdog/cmsdk-apb-watchdog.c @@ -463,6 +467,8 @@ F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h F: hw/misc/tz-mpc.c F: include/hw/misc/tz-mpc.h +F: hw/misc/tz-msc.c +F: include/hw/misc/tz-msc.h ARM cores M: Peter Maydell <peter.maydell@linaro.org> @@ -537,6 +543,10 @@ F: hw/misc/mps2-*.c F: include/hw/misc/mps2-*.h F: hw/arm/iotkit.c F: include/hw/arm/iotkit.h +F: hw/misc/iotkit-sysctl.c +F: include/hw/misc/iotkit-sysctl.h +F: hw/misc/iotkit-sysinfo.c +F: include/hw/misc/iotkit-sysinfo.h Musicpal M: Jan Kiszka <jan.kiszka@web.de> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index a92bc34..0483d54 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -103,6 +103,7 @@ CONFIG_STM32F2XX_SPI=y CONFIG_STM32F205_SOC=y CONFIG_CMSDK_APB_TIMER=y +CONFIG_CMSDK_APB_DUALTIMER=y CONFIG_CMSDK_APB_UART=y CONFIG_CMSDK_APB_WATCHDOG=y @@ -110,9 +111,12 @@ CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y CONFIG_TZ_MPC=y +CONFIG_TZ_MSC=y CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y +CONFIG_IOTKIT_SYSCTL=y +CONFIG_IOTKIT_SYSINFO=y CONFIG_VERSATILE=y CONFIG_VERSATILE_PCI=y diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 7d63cff..59ca356 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1293,19 +1293,23 @@ float32 float64_to_float32(float64 a, float_status *s) * Arithmetic. */ -static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) +static FloatParts round_to_int(FloatParts a, int rmode, + int scale, float_status *s) { - if (is_nan(a.cls)) { + switch (a.cls) { + case float_class_qnan: + case float_class_snan: return return_nan(a, s); - } - switch (a.cls) { case float_class_zero: case float_class_inf: - case float_class_qnan: /* already "integral" */ break; + case float_class_normal: + scale = MIN(MAX(scale, -0x10000), 0x10000); + a.exp += scale; + if (a.exp >= DECOMPOSED_BINARY_POINT) { /* already integral */ break; @@ -1314,7 +1318,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) bool one; /* all fractional */ s->float_exception_flags |= float_flag_inexact; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: one = a.exp == -1 && a.frac > DECOMPOSED_IMPLICIT_BIT; break; @@ -1347,7 +1351,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) uint64_t rnd_mask = rnd_even_mask >> 1; uint64_t inc; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: inc = ((a.frac & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0); break; @@ -1387,28 +1391,28 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) float16 float16_round_to_int(float16 a, float_status *s) { FloatParts pa = float16_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float16_round_pack_canonical(pr, s); } float32 float32_round_to_int(float32 a, float_status *s) { FloatParts pa = float32_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float32_round_pack_canonical(pr, s); } float64 float64_round_to_int(float64 a, float_status *s) { FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float64_round_pack_canonical(pr, s); } float64 float64_trunc_to_int(float64 a, float_status *s) { FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, float_round_to_zero, s); + FloatParts pr = round_to_int(pa, float_round_to_zero, 0, s); return float64_round_pack_canonical(pr, s); } @@ -1423,13 +1427,13 @@ float64 float64_trunc_to_int(float64 a, float_status *s) * is returned. */ -static int64_t round_to_int_and_pack(FloatParts in, int rmode, +static int64_t round_to_int_and_pack(FloatParts in, int rmode, int scale, int64_t min, int64_t max, float_status *s) { uint64_t r; int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); switch (p.cls) { case float_class_snan: @@ -1469,38 +1473,158 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, } } -#define FLOAT_TO_INT(fsz, isz) \ -int ## isz ## _t float ## fsz ## _to_int ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, s->float_rounding_mode, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ -} \ - \ -int ## isz ## _t float ## fsz ## _to_int ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, float_round_to_zero, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ +int16_t float16_to_int16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float16_to_int32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float16_to_int64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float32_to_int16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float32_to_int32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float32_to_int64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float64_to_int16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float64_to_int32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float64_to_int64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float16_to_int16(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float16_to_int32(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float16_to_int64(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float32_to_int16(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float32_to_int32(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float32_to_int64(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float64_to_int16(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float64_to_int32(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float64_to_int64(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float16_to_int16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float16_to_int32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float16_to_int64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, float_round_to_zero, 0, s); } -FLOAT_TO_INT(16, 16) -FLOAT_TO_INT(16, 32) -FLOAT_TO_INT(16, 64) +int16_t float32_to_int16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float32_to_int32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, float_round_to_zero, 0, s); +} -FLOAT_TO_INT(32, 16) -FLOAT_TO_INT(32, 32) -FLOAT_TO_INT(32, 64) +int64_t float32_to_int64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, float_round_to_zero, 0, s); +} -FLOAT_TO_INT(64, 16) -FLOAT_TO_INT(64, 32) -FLOAT_TO_INT(64, 64) +int16_t float64_to_int16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, float_round_to_zero, 0, s); +} -#undef FLOAT_TO_INT +int32_t float64_to_int32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float64_to_int64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, float_round_to_zero, 0, s); +} /* * Returns the result of converting the floating-point value `a' to @@ -1515,11 +1639,12 @@ FLOAT_TO_INT(64, 64) * flag. */ -static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, - float_status *s) +static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, int scale, + uint64_t max, float_status *s) { int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); + uint64_t r; switch (p.cls) { case float_class_snan: @@ -1532,8 +1657,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, case float_class_zero: return 0; case float_class_normal: - { - uint64_t r; if (p.sign) { s->float_exception_flags = orig_flags | float_flag_invalid; return 0; @@ -1555,45 +1678,165 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, if (r > max) { s->float_exception_flags = orig_flags | float_flag_invalid; return max; - } else { - return r; } - } + return r; default: g_assert_not_reached(); } } -#define FLOAT_TO_UINT(fsz, isz) \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, s->float_rounding_mode, \ - UINT ## isz ## _MAX, s); \ -} \ - \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, float_round_to_zero, \ - UINT ## isz ## _MAX, s); \ +uint16_t float16_to_uint16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float16_to_uint32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float16_to_uint64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float32_to_uint16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float32_to_uint32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float32_to_uint64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float64_to_uint16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float64_to_uint32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); } -FLOAT_TO_UINT(16, 16) -FLOAT_TO_UINT(16, 32) -FLOAT_TO_UINT(16, 64) +uint64_t float64_to_uint64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float16_to_uint16(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float16_to_uint32(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float16_to_uint64(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float32_to_uint16(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float32_to_uint32(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float32_to_uint64(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float64_to_uint16(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float64_to_uint32(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} -FLOAT_TO_UINT(32, 16) -FLOAT_TO_UINT(32, 32) -FLOAT_TO_UINT(32, 64) +uint64_t float64_to_uint64(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} -FLOAT_TO_UINT(64, 16) -FLOAT_TO_UINT(64, 32) -FLOAT_TO_UINT(64, 64) +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} -#undef FLOAT_TO_UINT +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float32_to_uint16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float32_to_uint32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float32_to_uint64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float64_to_uint16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float64_to_uint32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} /* * Integer to float conversions @@ -1603,81 +1846,122 @@ FLOAT_TO_UINT(64, 64) * to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts int_to_float(int64_t a, float_status *status) +static FloatParts int_to_float(int64_t a, int scale, float_status *status) { - FloatParts r = {}; + FloatParts r = { .sign = false }; + if (a == 0) { r.cls = float_class_zero; - r.sign = false; - } else if (a == (1ULL << 63)) { - r.cls = float_class_normal; - r.sign = true; - r.frac = DECOMPOSED_IMPLICIT_BIT; - r.exp = 63; } else { - uint64_t f; + uint64_t f = a; + int shift; + + r.cls = float_class_normal; if (a < 0) { - f = -a; + f = -f; r.sign = true; - } else { - f = a; - r.sign = false; } - int shift = clz64(f) - 1; - r.cls = float_class_normal; - r.exp = (DECOMPOSED_BINARY_POINT - shift); - r.frac = f << shift; + shift = clz64(f) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); + + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift); } return r; } -float16 int64_to_float16(int64_t a, float_status *status) +float16 int64_to_float16_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float16_round_pack_canonical(pa, status); } +float16 int32_to_float16_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + +float16 int16_to_float16_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + +float16 int64_to_float16(int64_t a, float_status *status) +{ + return int64_to_float16_scalbn(a, 0, status); +} + float16 int32_to_float16(int32_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); } float16 int16_to_float16(int16_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); } -float32 int64_to_float32(int64_t a, float_status *status) +float32 int64_to_float32_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float32_round_pack_canonical(pa, status); } +float32 int32_to_float32_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); +} + +float32 int16_to_float32_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); +} + +float32 int64_to_float32(int64_t a, float_status *status) +{ + return int64_to_float32_scalbn(a, 0, status); +} + float32 int32_to_float32(int32_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); } float32 int16_to_float32(int16_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); } -float64 int64_to_float64(int64_t a, float_status *status) +float64 int64_to_float64_scalbn(int64_t a, int scale, float_status *status) { - FloatParts pa = int_to_float(a, status); + FloatParts pa = int_to_float(a, scale, status); return float64_round_pack_canonical(pa, status); } +float64 int32_to_float64_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); +} + +float64 int16_to_float64_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); +} + +float64 int64_to_float64(int64_t a, float_status *status) +{ + return int64_to_float64_scalbn(a, 0, status); +} + float64 int32_to_float64(int32_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } float64 int16_to_float64(int16_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } @@ -1689,73 +1973,120 @@ float64 int16_to_float64(int16_t a, float_status *status) * IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts uint_to_float(uint64_t a, float_status *status) +static FloatParts uint_to_float(uint64_t a, int scale, float_status *status) { - FloatParts r = { .sign = false}; + FloatParts r = { .sign = false }; if (a == 0) { r.cls = float_class_zero; } else { - int spare_bits = clz64(a) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); r.cls = float_class_normal; - r.exp = DECOMPOSED_BINARY_POINT - spare_bits; - if (spare_bits < 0) { - shift64RightJamming(a, -spare_bits, &a); + if ((int64_t)a < 0) { + r.exp = DECOMPOSED_BINARY_POINT + 1 + scale; + shift64RightJamming(a, 1, &a); r.frac = a; } else { - r.frac = a << spare_bits; + int shift = clz64(a) - 1; + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = a << shift; } } return r; } -float16 uint64_to_float16(uint64_t a, float_status *status) +float16 uint64_to_float16_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float16_round_pack_canonical(pa, status); } +float16 uint32_to_float16_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + +float16 uint16_to_float16_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + +float16 uint64_to_float16(uint64_t a, float_status *status) +{ + return uint64_to_float16_scalbn(a, 0, status); +} + float16 uint32_to_float16(uint32_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); } float16 uint16_to_float16(uint16_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); } -float32 uint64_to_float32(uint64_t a, float_status *status) +float32 uint64_to_float32_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float32_round_pack_canonical(pa, status); } +float32 uint32_to_float32_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); +} + +float32 uint16_to_float32_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); +} + +float32 uint64_to_float32(uint64_t a, float_status *status) +{ + return uint64_to_float32_scalbn(a, 0, status); +} + float32 uint32_to_float32(uint32_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); } float32 uint16_to_float32(uint16_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); } -float64 uint64_to_float64(uint64_t a, float_status *status) +float64 uint64_to_float64_scalbn(uint64_t a, int scale, float_status *status) { - FloatParts pa = uint_to_float(a, status); + FloatParts pa = uint_to_float(a, scale, status); return float64_round_pack_canonical(pa, status); } +float64 uint32_to_float64_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); +} + +float64 uint16_to_float64_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); +} + +float64 uint64_to_float64(uint64_t a, float_status *status) +{ + return uint64_to_float64_scalbn(a, 0, status); +} + float64 uint32_to_float64(uint32_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } float64 uint16_to_float64(uint16_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } /* Float Min/Max */ diff --git a/hw/arm/boot.c b/hw/arm/boot.c index ca9467e..20c71d7 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -736,6 +736,17 @@ static void do_cpu_reset(void *opaque) } } + if (!env->aarch64 && !info->secure_boot && + arm_feature(env, ARM_FEATURE_EL2)) { + /* + * This is an AArch32 boot not to Secure state, and + * we have Hyp mode available, so boot the kernel into + * Hyp mode. This is not how the CPU comes out of reset, + * so we need to manually put it there. + */ + cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw); + } + if (cs == first_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 258f470..4b56bfa 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -207,6 +207,10 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); sysbus_connect_irq(sbd, i, irq); sysbus_connect_irq(sbd, i + smp_cpus, qdev_get_gpio_in(d, ARM_CPU_FIQ)); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VIRQ)); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VFIQ)); } /* diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index d5e2685..7663ad6 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -209,6 +209,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, i, irq); irq = qdev_get_gpio_in(d, ARM_CPU_FIQ); sysbus_connect_irq(sbd, i + smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, irq); } /* diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 6d42fce..fb9efa0 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -243,6 +243,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) int n; qemu_irq cpu_irq[4]; qemu_irq cpu_fiq[4]; + qemu_irq cpu_virq[4]; + qemu_irq cpu_vfiq[4]; MemoryRegion *sysram; MemoryRegion *dram; MemoryRegion *sysmem; @@ -282,6 +284,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) object_property_set_bool(cpuobj, true, "realized", &error_fatal); cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ); cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ); + cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ); + cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ); } sysmem = get_system_memory(); @@ -329,6 +333,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); } for (n = 0; n < 128; n++) { diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index 8cadc8b..8742200 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -16,9 +16,11 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/arm/iotkit.h" -#include "hw/misc/unimp.h" #include "hw/arm/arm.h" +/* Clock frequency in HZ of the 32KHz "slow clock" */ +#define S32KCLK (32 * 1000) + /* Create an alias region of @size bytes starting at @base * which mirrors the memory starting at @orig. */ @@ -138,8 +140,23 @@ static void iotkit_init(Object *obj) TYPE_CMSDK_APB_TIMER); sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_CMSDK_APB_TIMER); sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_UNIMPLEMENTED_DEVICE); + TYPE_CMSDK_APB_DUALTIMER); + sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, + sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, + sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, + sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl, + sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); + sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo, + sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); + object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, + sizeof(s->nmi_orgate), TYPE_OR_IRQ, + &error_abort, NULL); object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, &error_abort, NULL); @@ -154,8 +171,6 @@ static void iotkit_init(Object *obj) TYPE_SPLIT_IRQ, &error_abort, NULL); g_free(name); } - sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_UNIMPLEMENTED_DEVICE); } static void iotkit_exp_irq(void *opaque, int n, int level) @@ -390,13 +405,15 @@ static void iotkit_realize(DeviceState *dev, Error **errp) return; } - qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer"); - qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000); + + qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); if (err) { error_propagate(errp, err); return; } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); if (err) { @@ -462,13 +479,14 @@ static void iotkit_realize(DeviceState *dev, Error **errp) /* Devices behind APB PPC1: * 0x4002f000: S32K timer */ - qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER"); - qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000); + qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); if (err) { error_propagate(errp, err); return; } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); if (err) { @@ -501,19 +519,66 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(dev_apb_ppc1, "cfg_sec_resp", 0)); - /* Using create_unimplemented_device() maps the stub into the - * system address space rather than into our container, but the - * overall effect to the guest is the same. - */ - create_unimplemented_device("SYSINFO", 0x40020000, 0x1000); + object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* System information registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); + /* System control registers */ + object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); - create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000); - create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000); + /* This OR gate wires together outputs from the secure watchdogs to NMI */ + object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + 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); + object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ - create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); - create_unimplemented_device("S watchdog", 0x50081000, 0x1000); + qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); + + qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); @@ -602,6 +667,21 @@ static void iotkit_realize(DeviceState *dev, Error **errp) iotkit_forward_sec_resp_cfg(s); + /* Forward the MSC related signals */ + qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); + qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); + + /* + * Expose our container region to the board model; this corresponds + * to the AHB Slave Expansion ports which allow bus master devices + * (eg DMA controllers) in the board model to make transactions into + * devices in the IoTKit. + */ + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); + system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; } diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index dc0f34a..6dd02ae 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -45,7 +45,10 @@ #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" #include "hw/misc/tz-mpc.h" +#include "hw/misc/tz-msc.h" #include "hw/arm/iotkit.h" +#include "hw/dma/pl080.h" +#include "hw/ssi/pl022.h" #include "hw/devices.h" #include "net/net.h" #include "hw/core/split-irq.h" @@ -71,12 +74,13 @@ typedef struct { MPS2FPGAIO fpgaio; TZPPC ppc[5]; TZMPC ssram_mpc[3]; - UnimplementedDeviceState spi[5]; + PL022State spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; - UnimplementedDeviceState dma[4]; UnimplementedDeviceState gfx; + PL080State dma[4]; + TZMSC msc[4]; CMSDKAPBUART uart[5]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; @@ -188,7 +192,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, sccdev = DEVICE(scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); @@ -263,6 +267,89 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); } +static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL080State *dma = opaque; + int i = dma - &mms->dma[0]; + SysBusDevice *s; + char *mscname = g_strdup_printf("%s-msc", name); + TZMSC *msc = &mms->msc[i]; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + MemoryRegion *msc_upstream; + MemoryRegion *msc_downstream; + + /* + * Each DMA device is a PL081 whose transaction master interface + * is guarded by a Master Security Controller. The downstream end of + * the MSC connects to the IoTKit AHB Slave Expansion port, so the + * DMA devices can see all devices and memory that the CPU does. + */ + sysbus_init_child_obj(OBJECT(mms), mscname, msc, sizeof(*msc), TYPE_TZ_MSC); + msc_downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(&mms->iotkit), 0); + object_property_set_link(OBJECT(msc), OBJECT(msc_downstream), + "downstream", &error_fatal); + object_property_set_link(OBJECT(msc), OBJECT(mms), + "idau", &error_fatal); + object_property_set_bool(OBJECT(msc), true, "realized", &error_fatal); + + qdev_connect_gpio_out_named(DEVICE(msc), "irq", 0, + qdev_get_gpio_in_named(iotkitdev, + "mscexp_status", i)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_clear", i, + qdev_get_gpio_in_named(DEVICE(msc), + "irq_clear", 0)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_ns", i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_nonsec", 0)); + qdev_connect_gpio_out(DEVICE(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_sec_resp", 0)); + msc_upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(msc), 0); + + sysbus_init_child_obj(OBJECT(mms), name, dma, sizeof(*dma), TYPE_PL081); + object_property_set_link(OBJECT(dma), OBJECT(msc_upstream), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(dma), true, "realized", &error_fatal); + + s = SYS_BUS_DEVICE(dma); + /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ + sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 58 + i * 3)); + sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 56 + i * 3)); + sysbus_connect_irq(s, 2, qdev_get_gpio_in_named(iotkitdev, + "EXP_IRQ", 57 + i * 3)); + + return sysbus_mmio_get_region(s, 0); +} + +static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * The AN505 has five PL022 SPI controllers. + * One of these should have the LCD controller behind it; the others + * are connected only to the FPGA's "general purpose SPI connector" + * or "shield" expansion connectors. + * Note that if we do implement devices behind SPI, the chip select + * lines are set via the "MISC" register in the MPS2 FPGAIO device. + */ + PL022State *spi = opaque; + int i = spi - &mms->spi[0]; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + SysBusDevice *s; + + sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]), + TYPE_PL022); + object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(spi); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 51 + i)); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -289,13 +376,14 @@ static void mps2tz_common_init(MachineState *machine) &error_fatal); /* The sec_resp_cfg output from the IoTKit must be split into multiple - * lines, one for each of the PPCs we create here. + * lines, one for each of the PPCs we create here, plus one per MSC. */ object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), TYPE_SPLIT_IRQ); object_property_add_child(OBJECT(machine), "sec-resp-splitter", OBJECT(&mms->sec_resp_splitter), &error_abort); - object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5, + object_property_set_int(OBJECT(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + ARRAY_SIZE(mms->msc), "num-lines", &error_fatal); object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, "realized", &error_fatal); @@ -360,11 +448,11 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "apb_ppcexp1", .ports = { - { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 }, - { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 }, - { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 }, - { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 }, - { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 }, + { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 }, + { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 }, + { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 }, + { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 }, + { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 }, { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 }, { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 }, { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, @@ -396,10 +484,10 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "ahb_ppcexp1", .ports = { - { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 }, - { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 }, - { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 }, - { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 }, + { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 }, + { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 }, + { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 }, + { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 }, }, }, }; @@ -480,12 +568,32 @@ static void mps2tz_common_init(MachineState *machine) armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); } +static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* + * The MPS2 TZ FPGA images have IDAUs in them which are connected to + * the Master Security Controllers. Thes have the same logic as + * is used by the IoTKit for the IDAU connected to the CPU, except + * that MSCs don't care about the NSC attribute. + */ + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = false; + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + static void mps2tz_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); mc->init = mps2tz_common_init; mc->max_cpus = 1; + iic->check = mps2_tz_idau_check; } static void mps2tz_an505_class_init(ObjectClass *oc, void *data) @@ -496,7 +604,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); - mmc->scc_id = 0x41040000 | (505 << 4); + mmc->scc_id = 0x41045050; } static const TypeInfo mps2tz_info = { @@ -506,6 +614,10 @@ static const TypeInfo mps2tz_info = { .instance_size = sizeof(MPS2TZMachineState), .class_size = sizeof(MPS2TZMachineClass), .class_init = mps2tz_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + }, }; static const TypeInfo mps2tz_an505_info = { diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 0a0ae86..e3d698b 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -34,6 +34,7 @@ #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" #include "hw/misc/mps2-scc.h" #include "hw/devices.h" #include "net/net.h" @@ -64,6 +65,7 @@ typedef struct { MemoryRegion blockram_m3; MemoryRegion sram; MPS2SCC scc; + CMSDKAPBDualTimer dualtimer; } MPS2MachineState; #define TYPE_MPS2_MACHINE "mps2" @@ -297,11 +299,20 @@ static void mps2_common_init(MachineState *machine) 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); + sysbus_init_child_obj(OBJECT(mms), "dualtimer", &mms->dualtimer, + sizeof(mms->dualtimer), TYPE_CMSDK_APB_DUALTIMER); + qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->dualtimer), true, "realized", + &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(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC); sccdev = DEVICE(&mms->scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(&mms->scc), true, "realized", &error_fatal); @@ -336,7 +347,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x41040000 | (385 << 4); + mmc->scc_id = 0x41043850; } static void mps2_an511_class_init(ObjectClass *oc, void *data) @@ -347,7 +358,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x4104000 | (511 << 4); + mmc->scc_id = 0x41045110; } static const TypeInfo mps2_info = { diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 5bfe2e4..c02d18e 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -172,6 +172,7 @@ typedef struct { typedef struct { MachineState parent; bool secure; + bool virt; } VexpressMachineState; #define TYPE_VEXPRESS_MACHINE "vexpress" @@ -203,7 +204,7 @@ struct VEDBoardInfo { }; static void init_cpus(const char *cpu_type, const char *privdev, - hwaddr periphbase, qemu_irq *pic, bool secure) + hwaddr periphbase, qemu_irq *pic, bool secure, bool virt) { DeviceState *dev; SysBusDevice *busdev; @@ -216,6 +217,11 @@ static void init_cpus(const char *cpu_type, const char *privdev, if (!secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } + if (!virt) { + if (object_property_find(cpuobj, "has_el2", NULL)) { + object_property_set_bool(cpuobj, false, "has_el2", NULL); + } + } if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, periphbase, @@ -251,6 +257,10 @@ static void init_cpus(const char *cpu_type, const char *privdev, sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(busdev, n + smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } } @@ -285,7 +295,8 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x60000000, ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ - init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, + vms->secure, vms->virt); /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ @@ -366,7 +377,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x80000000, ram); /* 0x2c000000 A15MPCore private memory region (GIC) */ - init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure, + vms->virt); /* A15 daughterboard peripherals: */ @@ -701,8 +713,8 @@ static void vexpress_common_init(MachineState *machine) daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30; daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr; daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb; - /* Indicate that when booting Linux we should be in secure state */ - daughterboard->bootinfo.secure_boot = true; + /* When booting Linux we should be in secure state if the CPU has one. */ + daughterboard->bootinfo.secure_boot = vms->secure; arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo); } @@ -720,6 +732,20 @@ static void vexpress_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool vexpress_get_virt(Object *obj, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + return vms->virt; +} + +static void vexpress_set_virt(Object *obj, bool value, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + vms->virt = value; +} + static void vexpress_instance_init(Object *obj) { VexpressMachineState *vms = VEXPRESS_MACHINE(obj); @@ -734,6 +760,32 @@ static void vexpress_instance_init(Object *obj) NULL); } +static void vexpress_a15_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* + * For the vexpress-a15, EL2 is by default enabled if EL3 is, + * but can also be specifically set to on or off. + */ + vms->virt = true; + object_property_add_bool(obj, "virtualization", vexpress_get_virt, + vexpress_set_virt, NULL); + object_property_set_description(obj, "virtualization", + "Set on/off to enable/disable the ARM " + "Virtualization Extensions " + "(defaults to same as 'secure')", + NULL); +} + +static void vexpress_a9_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* The A9 doesn't have the virt extensions */ + vms->virt = false; +} + static void vexpress_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -780,12 +832,14 @@ static const TypeInfo vexpress_a9_info = { .name = TYPE_VEXPRESS_A9_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a9_class_init, + .instance_init = vexpress_a9_instance_init, }; static const TypeInfo vexpress_a15_info = { .name = TYPE_VEXPRESS_A15_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a15_class_init, + .instance_init = vexpress_a15_instance_init, }; static void vexpress_machine_init(void) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 43c1079..5649843 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -53,6 +53,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) int i; Error *err = NULL; bool has_el3; + bool has_el2 = false; Object *cpuobj; gicdev = DEVICE(&s->gic); @@ -67,6 +68,10 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) has_el3 = object_property_find(cpuobj, "has_el3", NULL) && object_property_get_bool(cpuobj, "has_el3", &error_abort); qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); + /* Similarly for virtualization support */ + has_el2 = object_property_find(cpuobj, "has_el2", NULL) && + object_property_get_bool(cpuobj, "has_el2", &error_abort); + qdev_prop_set_bit(gicdev, "has-virtualization-extensions", has_el2); } object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); @@ -103,20 +108,40 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, ppibase + timer_irq[irq])); } + if (has_el2) { + /* Connect the GIC maintenance interrupt to PPI ID 25 */ + sysbus_connect_irq(SYS_BUS_DEVICE(gicdev), i + 4 * s->num_cpu, + qdev_get_gpio_in(gicdev, ppibase + 25)); + } } /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor * 0x2000-0x3fff -- GIC CPU interface - * 0x4000-0x4fff -- GIC virtual interface control (not modelled) - * 0x5000-0x5fff -- GIC virtual interface control (not modelled) - * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) + * 0x4000-0x4fff -- GIC virtual interface control for this CPU + * 0x5000-0x51ff -- GIC virtual interface control for CPU 0 + * 0x5200-0x53ff -- GIC virtual interface control for CPU 1 + * 0x5400-0x55ff -- GIC virtual interface control for CPU 2 + * 0x5600-0x57ff -- GIC virtual interface control for CPU 3 + * 0x6000-0x7fff -- GIC virtual CPU interface */ memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(busdev, 0)); memory_region_add_subregion(&s->container, 0x2000, sysbus_mmio_get_region(busdev, 1)); + if (has_el2) { + memory_region_add_subregion(&s->container, 0x4000, + sysbus_mmio_get_region(busdev, 2)); + memory_region_add_subregion(&s->container, 0x6000, + sysbus_mmio_get_region(busdev, 3)); + for (i = 0; i < s->num_cpu; i++) { + hwaddr base = 0x5000 + i * 0x200; + MemoryRegion *mr = sysbus_mmio_get_region(busdev, + 4 + s->num_cpu + i); + memory_region_add_subregion(&s->container, base, mr); + } + } } static Property a15mp_priv_properties[] = { diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 3355f4c..d534d00 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -34,6 +34,13 @@ #define DEFAULT_VCRAM_SIZE 0x4000000 #define BCM2835_FB_OFFSET 0x00100000 +/* Maximum permitted framebuffer size; experimentally determined on an rpi2 */ +#define XRES_MAX 3840 +#define YRES_MAX 2560 +/* Framebuffer size used if guest requests zero size */ +#define XRES_SMALL 592 +#define YRES_SMALL 488 + static void fb_invalidate_display(void *opaque) { BCM2835FBState *s = BCM2835_FB(opaque); @@ -52,7 +59,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, int bpp = surface_bits_per_pixel(surface); while (width--) { - switch (s->bpp) { + switch (s->config.bpp) { case 8: /* lookup palette starting at video ram base * TODO: cache translation, rather than doing this each time! @@ -91,7 +98,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, break; } - if (s->pixo == 0) { + if (s->config.pixo == 0) { /* swap to BGR pixel format */ uint8_t tmp = r; r = b; @@ -126,6 +133,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, } } +static bool fb_use_offsets(BCM2835FBConfig *config) +{ + /* + * Return true if we should use the viewport offsets. + * Experimentally, the hardware seems to do this only if the + * viewport size is larger than the physical screen. (It doesn't + * prevent the guest setting this silly viewport setting, though...) + */ + return config->xres_virtual > config->xres && + config->yres_virtual > config->yres; +} + static void fb_update_display(void *opaque) { BCM2835FBState *s = opaque; @@ -134,13 +153,19 @@ static void fb_update_display(void *opaque) int last = 0; int src_width = 0; int dest_width = 0; + uint32_t xoff = 0, yoff = 0; - if (s->lock || !s->xres) { + if (s->lock || !s->config.xres) { return; } - src_width = s->xres * (s->bpp >> 3); - dest_width = s->xres; + src_width = bcm2835_fb_get_pitch(&s->config); + if (fb_use_offsets(&s->config)) { + xoff = s->config.xoffset; + yoff = s->config.yoffset; + } + + dest_width = s->config.xres; switch (surface_bits_per_pixel(surface)) { case 0: @@ -165,89 +190,104 @@ static void fb_update_display(void *opaque) } if (s->invalidate) { - framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, - s->yres, src_width); + hwaddr base = s->config.base + xoff + yoff * src_width; + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, + base, + s->config.yres, src_width); } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + framebuffer_update_display(surface, &s->fbsection, + s->config.xres, s->config.yres, src_width, dest_width, 0, s->invalidate, draw_line_src16, s, &first, &last); if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1); + dpy_gfx_update(s->con, 0, first, s->config.xres, + last - first + 1); } s->invalidate = false; } -static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +void bcm2835_fb_validate_config(BCM2835FBConfig *config) { - value &= ~0xf; - - s->lock = true; - - s->xres = ldl_le_phys(&s->dma_as, value); - s->yres = ldl_le_phys(&s->dma_as, value + 4); - s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8); - s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12); - s->bpp = ldl_le_phys(&s->dma_as, value + 20); - s->xoffset = ldl_le_phys(&s->dma_as, value + 24); - s->yoffset = ldl_le_phys(&s->dma_as, value + 28); - - s->base = s->vcram_base | (value & 0xc0000000); - s->base += BCM2835_FB_OFFSET; + /* + * Validate the config, and clip any bogus values into range, + * as the hardware does. Note that fb_update_display() relies on + * this happening to prevent it from performing out-of-range + * accesses on redraw. + */ + config->xres = MIN(config->xres, XRES_MAX); + config->xres_virtual = MIN(config->xres_virtual, XRES_MAX); + config->yres = MIN(config->yres, YRES_MAX); + config->yres_virtual = MIN(config->yres_virtual, YRES_MAX); + + /* + * These are not minima: a 40x40 framebuffer will be accepted. + * They're only used as defaults if the guest asks for zero size. + */ + if (config->xres == 0) { + config->xres = XRES_SMALL; + } + if (config->yres == 0) { + config->yres = YRES_SMALL; + } + if (config->xres_virtual == 0) { + config->xres_virtual = config->xres; + } + if (config->yres_virtual == 0) { + config->yres_virtual = config->yres; + } - /* TODO - Manage properly virtual resolution */ + if (fb_use_offsets(config)) { + /* Clip the offsets so the viewport is within the physical screen */ + config->xoffset = MIN(config->xoffset, + config->xres_virtual - config->xres); + config->yoffset = MIN(config->yoffset, + config->yres_virtual - config->yres); + } +} - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig) +{ + s->lock = true; - stl_le_phys(&s->dma_as, value + 16, s->pitch); - stl_le_phys(&s->dma_as, value + 32, s->base); - stl_le_phys(&s->dma_as, value + 36, s->size); + s->config = *newconfig; s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); + qemu_console_resize(s->con, s->config.xres, s->config.yres); s->lock = false; } -void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, - uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, - uint32_t *pixo, uint32_t *alpha) +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) { - s->lock = true; + uint32_t pitch; + uint32_t size; + BCM2835FBConfig newconf; - /* TODO: input validation! */ - if (xres) { - s->xres = *xres; - } - if (yres) { - s->yres = *yres; - } - if (xoffset) { - s->xoffset = *xoffset; - } - if (yoffset) { - s->yoffset = *yoffset; - } - if (bpp) { - s->bpp = *bpp; - } - if (pixo) { - s->pixo = *pixo; - } - if (alpha) { - s->alpha = *alpha; - } + value &= ~0xf; - /* TODO - Manage properly virtual resolution */ + newconf.xres = ldl_le_phys(&s->dma_as, value); + newconf.yres = ldl_le_phys(&s->dma_as, value + 4); + newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + newconf.bpp = ldl_le_phys(&s->dma_as, value + 20); + newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24); + newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28); - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; + newconf.base = s->vcram_base | (value & 0xc0000000); + newconf.base += BCM2835_FB_OFFSET; - s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); - s->lock = false; + bcm2835_fb_validate_config(&newconf); + + pitch = bcm2835_fb_get_pitch(&newconf); + size = bcm2835_fb_get_size(&newconf); + + stl_le_phys(&s->dma_as, value + 16, pitch); + stl_le_phys(&s->dma_as, value + 32, newconf.base); + stl_le_phys(&s->dma_as, value + 36, size); + + bcm2835_fb_reconfigure(s, &newconf); } static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) @@ -312,18 +352,17 @@ static const VMStateDescription vmstate_bcm2835_fb = { VMSTATE_BOOL(lock, BCM2835FBState), VMSTATE_BOOL(invalidate, BCM2835FBState), VMSTATE_BOOL(pending, BCM2835FBState), - VMSTATE_UINT32(xres, BCM2835FBState), - VMSTATE_UINT32(yres, BCM2835FBState), - VMSTATE_UINT32(xres_virtual, BCM2835FBState), - VMSTATE_UINT32(yres_virtual, BCM2835FBState), - VMSTATE_UINT32(xoffset, BCM2835FBState), - VMSTATE_UINT32(yoffset, BCM2835FBState), - VMSTATE_UINT32(bpp, BCM2835FBState), - VMSTATE_UINT32(base, BCM2835FBState), - VMSTATE_UINT32(pitch, BCM2835FBState), - VMSTATE_UINT32(size, BCM2835FBState), - VMSTATE_UINT32(pixo, BCM2835FBState), - VMSTATE_UINT32(alpha, BCM2835FBState), + VMSTATE_UINT32(config.xres, BCM2835FBState), + VMSTATE_UINT32(config.yres, BCM2835FBState), + VMSTATE_UINT32(config.xres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.yres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.xoffset, BCM2835FBState), + VMSTATE_UINT32(config.yoffset, BCM2835FBState), + VMSTATE_UINT32(config.bpp, BCM2835FBState), + VMSTATE_UINT32(config.base, BCM2835FBState), + VMSTATE_UNUSED(8), /* Was pitch and size */ + VMSTATE_UINT32(config.pixo, BCM2835FBState), + VMSTATE_UINT32(config.alpha, BCM2835FBState), VMSTATE_END_OF_LIST() } }; @@ -349,13 +388,7 @@ static void bcm2835_fb_reset(DeviceState *dev) s->pending = false; - s->xres_virtual = s->xres; - s->yres_virtual = s->yres; - s->xoffset = 0; - s->yoffset = 0; - s->base = s->vcram_base + BCM2835_FB_OFFSET; - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; + s->config = s->initial_config; s->invalidate = true; s->lock = false; @@ -379,24 +412,33 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) return; } + /* Fill in the parts of initial_config that are not set by QOM properties */ + s->initial_config.xres_virtual = s->initial_config.xres; + s->initial_config.yres_virtual = s->initial_config.yres; + s->initial_config.xoffset = 0; + s->initial_config.yoffset = 0; + s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET; + s->dma_mr = MEMORY_REGION(obj); address_space_init(&s->dma_as, s->dma_mr, NULL); bcm2835_fb_reset(dev); s->con = graphic_console_init(dev, 0, &vgafb_ops, s); - qemu_console_resize(s->con, s->xres, s->yres); + qemu_console_resize(s->con, s->config.xres, s->config.yres); } static Property bcm2835_fb_props[] = { DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, DEFAULT_VCRAM_SIZE), - DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640), - DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480), - DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16), - DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */ - DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */ + DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, + initial_config.pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, + initial_config.alpha, 2), /* alpha ignored */ DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index c1b35fc..542b4b9 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -2084,7 +2084,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_cpu; i++) { memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s), &gic_viface_ops, &s->backref[i], - "gic_viface", 0x1000); + "gic_viface", 0x200); sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]); } } diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 22714b0..6d50b03 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -64,8 +64,11 @@ obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o obj-$(CONFIG_TZ_MPC) += tz-mpc.o +obj-$(CONFIG_TZ_MSC) += tz-msc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o +obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o +obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 70eaafd..145427a 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -21,11 +21,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tmp; int n; uint32_t offset, length, color; - uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; - uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; - uint32_t tmp_bpp, tmp_pixo, tmp_alpha; - uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, - *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; + + /* + * Copy the current state of the framebuffer config; we will update + * this copy as we process tags and then ask the framebuffer to use + * it at the end. + */ + BCM2835FBConfig fbconfig = s->fbdev->config; + bool fbconfig_updated = false; value &= ~0xf; @@ -141,12 +144,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Frame buffer */ case 0x00040001: /* Allocate buffer */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, fbconfig.base); stl_le_phys(&s->dma_as, value + 16, - tmp_xres * tmp_yres * tmp_bpp / 8); + bcm2835_fb_get_size(&fbconfig)); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -155,86 +155,85 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040002: /* Blank screen */ resplen = 4; break; - case 0x00040003: /* Get display width/height */ - case 0x00040004: - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - stl_le_phys(&s->dma_as, value + 12, tmp_xres); - stl_le_phys(&s->dma_as, value + 16, tmp_yres); + case 0x00044003: /* Test physical display width/height */ + case 0x00044004: /* Test virtual display width/height */ resplen = 8; break; - case 0x00044003: /* Test display width/height */ - case 0x00044004: + case 0x00048003: /* Set physical display width/height */ + fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040003: /* Get physical display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); resplen = 8; break; - case 0x00048003: /* Set display width/height */ - case 0x00048004: - xres = ldl_le_phys(&s->dma_as, value + 12); - newxres = &xres; - yres = ldl_le_phys(&s->dma_as, value + 16); - newyres = &yres; + case 0x00048004: /* Set virtual display width/height */ + fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040004: /* Get virtual display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00040005: /* Get depth */ - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_bpp); - resplen = 4; - break; case 0x00044005: /* Test depth */ resplen = 4; break; case 0x00048005: /* Set depth */ - bpp = ldl_le_phys(&s->dma_as, value + 12); - newbpp = &bpp; - resplen = 4; - break; - case 0x00040006: /* Get pixel order */ - tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; - stl_le_phys(&s->dma_as, value + 12, tmp_pixo); + fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); resplen = 4; break; case 0x00044006: /* Test pixel order */ resplen = 4; break; case 0x00048006: /* Set pixel order */ - pixo = ldl_le_phys(&s->dma_as, value + 12); - newpixo = &pixo; - resplen = 4; - break; - case 0x00040007: /* Get alpha */ - tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; - stl_le_phys(&s->dma_as, value + 12, tmp_alpha); + fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ resplen = 4; break; case 0x00048007: /* Set alpha */ - alpha = ldl_le_phys(&s->dma_as, value + 12); - newalpha = α + fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); resplen = 4; break; case 0x00040008: /* Get pitch */ - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); + stl_le_phys(&s->dma_as, value + 12, + bcm2835_fb_get_pitch(&fbconfig)); resplen = 4; break; - case 0x00040009: /* Get virtual offset */ - tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; - tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; - stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); - stl_le_phys(&s->dma_as, value + 16, tmp_yoffset); - resplen = 8; - break; case 0x00044009: /* Test virtual offset */ resplen = 8; break; case 0x00048009: /* Set virtual offset */ - xoffset = ldl_le_phys(&s->dma_as, value + 12); - newxoffset = &xoffset; - yoffset = ldl_le_phys(&s->dma_as, value + 16); - newyoffset = &yoffset; + fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); resplen = 8; break; case 0x0004000a: /* Get/Test/Set overscan */ @@ -285,10 +284,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) } /* Reconfigure framebuffer if required */ - if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo - || newalpha) { - bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, - newyoffset, newbpp, newpixo, newalpha); + if (fbconfig_updated) { + bcm2835_fb_reconfigure(s->fbdev, &fbconfig); } /* Buffer response code */ diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index de4fd8e..2222b3e 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -190,12 +190,13 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; case A_SECMSCINTSTAT: + r = s->secmscintstat; + break; case A_SECMSCINTEN: + r = s->secmscinten; + break; case A_NSMSCEXP: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block read: " - "unimplemented offset 0x%x\n", offset); - r = 0; + r = s->nsmscexp; break; case A_PID4: case A_PID5: @@ -291,6 +292,23 @@ static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); } +static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value) +{ + int i; + + for (i = 0; i < IOTS_NUM_EXP_MSC; i++) { + qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1)); + } +} + +static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s) +{ + /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */ + bool level = s->secmscintstat & s->secmscinten; + + qemu_set_irq(s->msc_irq, level); +} + static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) @@ -370,10 +388,15 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, iotkit_secctl_ppc_sp_write(ppc, value); break; case A_SECMSCINTCLR: + iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value); + break; case A_SECMSCINTEN: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block write: " - "unimplemented offset 0x%x\n", offset); + s->secmscinten = value; + iotkit_secctl_update_msc_irq(s); + break; + case A_NSMSCEXP: + s->nsmscexp = value; + iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value); break; case A_SECMPCINTSTATUS: case A_SECPPCINTSTAT: @@ -381,7 +404,6 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, case A_BRGINTSTAT: case A_AHBNSPPC0: case A_AHBSPPPC0: - case A_NSMSCEXP: case A_PID4: case A_PID5: case A_PID6: @@ -588,6 +610,14 @@ static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); } +static void iotkit_secctl_mscexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level); + iotkit_secctl_update_msc_irq(s); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -660,6 +690,14 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); + qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status, + "mscexp_status", IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -690,6 +728,24 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { } }; +static bool needed_always(void *opaque) +{ + return true; +} + +static const VMStateDescription iotkit_secctl_msc_vmstate = { + .name = "iotkit-secctl/msc", + .version_id = 1, + .minimum_version_id = 1, + .needed = needed_always, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), + VMSTATE_UINT32(secmscinten, IoTKitSecCtl), + VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -710,6 +766,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { }, .subsections = (const VMStateDescription*[]) { &iotkit_secctl_mpcintstatus_vmstate, + &iotkit_secctl_msc_vmstate, NULL }, }; diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c new file mode 100644 index 0000000..a21d8bd --- /dev/null +++ b/hw/misc/iotkit-sysctl.c @@ -0,0 +1,261 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system control register" blocks. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysctl.h" + +REG32(SECDBGSTAT, 0x0) +REG32(SECDBGSET, 0x4) +REG32(SECDBGCLR, 0x8) +REG32(RESET_SYNDROME, 0x100) +REG32(RESET_MASK, 0x104) +REG32(SWRESET, 0x108) + FIELD(SWRESET, SWRESETREQ, 9, 1) +REG32(GRETREG, 0x10c) +REG32(INITSVRTOR0, 0x110) +REG32(CPUWAIT, 0x118) +REG32(BUSWAIT, 0x11c) +REG32(WICCTRL, 0x120) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysctl_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + uint64_t r; + + switch (offset) { + case A_SECDBGSTAT: + r = s->secure_debug; + break; + case A_RESET_SYNDROME: + r = s->reset_syndrome; + break; + case A_RESET_MASK: + r = s->reset_mask; + break; + case A_GRETREG: + r = s->gretreg; + break; + case A_INITSVRTOR0: + r = s->initsvrtor0; + break; + case A_CPUWAIT: + r = s->cpuwait; + break; + case A_BUSWAIT: + /* In IoTKit BUSWAIT is reserved, R/O, zero */ + r = 0; + break; + case A_WICCTRL: + r = s->wicctrl; + break; + case A_PID4 ... A_CID3: + r = sysctl_id[(offset - A_PID4) / 4]; + break; + case A_SECDBGSET: + case A_SECDBGCLR: + case A_SWRESET: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: read of WO offset %x\n", + (int)offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysctl_read(offset, r, size); + return r; +} + +static void iotkit_sysctl_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + + trace_iotkit_sysctl_write(offset, value, size); + + /* + * Most of the state here has to do with control of reset and + * similar kinds of power up -- for instance the guest can ask + * what the reason for the last reset was, or forbid reset for + * some causes (like the non-secure watchdog). Most of this is + * not relevant to QEMU, which doesn't really model anything other + * than a full power-on reset. + * We just model the registers as reads-as-written. + */ + + switch (offset) { + case A_RESET_SYNDROME: + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl RESET_SYNDROME unimplemented\n"); + s->reset_syndrome = value; + break; + case A_RESET_MASK: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl RESET_MASK unimplemented\n"); + s->reset_mask = value; + break; + case A_GRETREG: + /* + * General retention register, which is only reset by a power-on + * reset. Technically this implementation is complete, since + * QEMU only supports power-on resets... + */ + s->gretreg = value; + break; + case A_INITSVRTOR0: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl INITSVRTOR0 unimplemented\n"); + s->initsvrtor0 = value; + break; + case A_CPUWAIT: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CPUWAIT unimplemented\n"); + s->cpuwait = value; + break; + case A_WICCTRL: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n"); + s->wicctrl = value; + break; + case A_SECDBGSET: + /* write-1-to-set */ + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SECDBGSET unimplemented\n"); + s->secure_debug |= value; + break; + case A_SECDBGCLR: + /* write-1-to-clear */ + s->secure_debug &= ~value; + break; + case A_SWRESET: + /* One w/o bit to request a reset; all other bits reserved */ + if (value & R_SWRESET_SWRESETREQ_MASK) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; + case A_BUSWAIT: /* In IoTKit BUSWAIT is reserved, R/O, zero */ + case A_SECDBGSTAT: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: write of RO offset %x\n", + (int)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: bad offset %x\n", (int)offset); + break; + } +} + +static const MemoryRegionOps iotkit_sysctl_ops = { + .read = iotkit_sysctl_read, + .write = iotkit_sysctl_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void iotkit_sysctl_reset(DeviceState *dev) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(dev); + + trace_iotkit_sysctl_reset(); + s->secure_debug = 0; + s->reset_syndrome = 1; + s->reset_mask = 0; + s->gretreg = 0; + s->initsvrtor0 = 0x10000000; + s->cpuwait = 0; + s->wicctrl = 0; +} + +static void iotkit_sysctl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysCtl *s = IOTKIT_SYSCTL(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysctl_ops, + s, "iotkit-sysctl", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription iotkit_sysctl_vmstate = { + .name = "iotkit-sysctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secure_debug, IoTKitSysCtl), + VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl), + VMSTATE_UINT32(reset_mask, IoTKitSysCtl), + VMSTATE_UINT32(gretreg, IoTKitSysCtl), + VMSTATE_UINT32(initsvrtor0, IoTKitSysCtl), + VMSTATE_UINT32(cpuwait, IoTKitSysCtl), + VMSTATE_UINT32(wicctrl, IoTKitSysCtl), + VMSTATE_END_OF_LIST() + } +}; + +static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &iotkit_sysctl_vmstate; + dc->reset = iotkit_sysctl_reset; +} + +static const TypeInfo iotkit_sysctl_info = { + .name = TYPE_IOTKIT_SYSCTL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysCtl), + .instance_init = iotkit_sysctl_init, + .class_init = iotkit_sysctl_class_init, +}; + +static void iotkit_sysctl_register_types(void) +{ + type_register_static(&iotkit_sysctl_info); +} + +type_init(iotkit_sysctl_register_types); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c new file mode 100644 index 0000000..78955bc --- /dev/null +++ b/hw/misc/iotkit-sysinfo.c @@ -0,0 +1,128 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * It consists of 2 read-only version/config registers, plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysinfo.h" + +REG32(SYS_VERSION, 0x0) +REG32(SYS_CONFIG, 0x4) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint64_t r; + + switch (offset) { + case A_SYS_VERSION: + r = 0x41743; + break; + + case A_SYS_CONFIG: + r = 0x31; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysinfo_read(offset, r, size); + return r; +} + +static void iotkit_sysinfo_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_iotkit_sysinfo_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps iotkit_sysinfo_ops = { + .read = iotkit_sysinfo_read, + .write = iotkit_sysinfo_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void iotkit_sysinfo_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysInfo *s = IOTKIT_SYSINFO(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysinfo_ops, + s, "iotkit-sysinfo", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) +{ + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ +} + +static const TypeInfo iotkit_sysinfo_info = { + .name = TYPE_IOTKIT_SYSINFO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysInfo), + .instance_init = iotkit_sysinfo_init, + .class_init = iotkit_sysinfo_class_init, +}; + +static void iotkit_sysinfo_register_types(void) +{ + type_register_static(&iotkit_sysinfo_info); +} + +type_init(iotkit_sysinfo_register_types); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 7394a05..5cf10eb 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/misc/mps2-fpgaio.h" +#include "qemu/timer.h" REG32(LED0, 0) REG32(BUTTON, 8) @@ -32,10 +33,92 @@ REG32(PRESCALE, 0x1c) REG32(PSCNTR, 0x20) REG32(MISC, 0x4c) +static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq) +{ + return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND); +} + +static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq) +{ + return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq); +} + +static void resync_counter(MPS2FPGAIO *s) +{ + /* + * Update s->counter and s->pscntr to their true current values + * by calculating how many times PSCNTR has ticked since the + * last time we did a resync. + */ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t elapsed = now - s->pscntr_sync_ticks; + + /* + * Round elapsed down to a whole number of PSCNTR ticks, so we don't + * lose time if we do multiple resyncs in a single tick. + */ + uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND); + + /* + * Work out what PSCNTR and COUNTER have moved to. We assume that + * PSCNTR reloads from PRESCALE one tick-period after it hits zero, + * and that COUNTER increments at the same moment. + */ + if (ticks == 0) { + /* We haven't ticked since the last time we were asked */ + return; + } else if (ticks < s->pscntr) { + /* We haven't yet reached zero, just reduce the PSCNTR */ + s->pscntr -= ticks; + } else { + if (s->prescale == 0) { + /* + * If the reload value is zero then the PSCNTR will stick + * at zero once it reaches it, and so we will increment + * COUNTER every tick after that. + */ + s->counter += ticks - s->pscntr; + s->pscntr = 0; + } else { + /* + * This is the complicated bit. This ASCII art diagram gives an + * example with PRESCALE==5 PSCNTR==7: + * + * ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + * PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5 + * cinc 1 2 + * y 0 1 2 3 4 5 6 7 8 9 10 11 12 + * x 0 1 2 3 4 5 0 1 2 3 4 5 0 + * + * where x = y % (s->prescale + 1) + * and so PSCNTR = s->prescale - x + * and COUNTER is incremented by y / (s->prescale + 1) + * + * The case where PSCNTR < PRESCALE works out the same, + * though we must be careful to calculate y as 64-bit unsigned + * for all parts of the expression. + * y < 0 is not possible because that implies ticks < s->pscntr. + */ + uint64_t y = ticks - s->pscntr + s->prescale; + s->pscntr = s->prescale - (y % (s->prescale + 1)); + s->counter += y / (s->prescale + 1); + } + } + + /* + * Only advance the sync time to the timestamp of the last PSCNTR tick, + * not all the way to 'now', so we don't lose time if we do multiple + * resyncs in a single tick. + */ + s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND, + s->prescale_clk); +} + static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); uint64_t r; + int64_t now; switch (offset) { case A_LED0: @@ -54,12 +137,20 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) r = s->misc; break; case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1); + break; case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100); + break; case A_COUNTER: + resync_counter(s); + r = s->counter; + break; case A_PSCNTR: - /* These are all upcounters of various frequencies. */ - qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n"); - r = 0; + resync_counter(s); + r = s->pscntr; break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -76,6 +167,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + int64_t now; trace_mps2_fpgaio_write(offset, value, size); @@ -89,6 +181,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, s->led0 = value & 0x3; break; case A_PRESCALE: + resync_counter(s); s->prescale = value; break; case A_MISC: @@ -100,6 +193,22 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, "MPS2 FPGAIO: MISC control bits unimplemented\n"); s->misc = value; break; + case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1); + break; + case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100); + break; + case A_COUNTER: + resync_counter(s); + s->counter = value; + break; + case A_PSCNTR: + resync_counter(s); + s->pscntr = value; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset); @@ -116,11 +225,17 @@ static const MemoryRegionOps mps2_fpgaio_ops = { static void mps2_fpgaio_reset(DeviceState *dev) { MPS2FPGAIO *s = MPS2_FPGAIO(dev); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); trace_mps2_fpgaio_reset(); s->led0 = 0; s->prescale = 0; s->misc = 0; + s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1); + s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100); + s->counter = 0; + s->pscntr = 0; + s->pscntr_sync_ticks = now; } static void mps2_fpgaio_init(Object *obj) @@ -133,6 +248,27 @@ static void mps2_fpgaio_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } +static bool mps2_fpgaio_counters_needed(void *opaque) +{ + /* Currently vmstate.c insists all subsections have a 'needed' function */ + return true; +} + +static const VMStateDescription mps2_fpgaio_counters_vmstate = { + .name = "mps2-fpgaio/counters", + .version_id = 2, + .minimum_version_id = 2, + .needed = mps2_fpgaio_counters_needed, + .fields = (VMStateField[]) { + VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO), + VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO), + VMSTATE_UINT32(counter, MPS2FPGAIO), + VMSTATE_UINT32(pscntr, MPS2FPGAIO), + VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription mps2_fpgaio_vmstate = { .name = "mps2-fpgaio", .version_id = 1, @@ -142,6 +278,10 @@ static const VMStateDescription mps2_fpgaio_vmstate = { VMSTATE_UINT32(prescale, MPS2FPGAIO), VMSTATE_UINT32(misc, MPS2FPGAIO), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &mps2_fpgaio_counters_vmstate, + NULL } }; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 1341508..52466c7 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -92,6 +92,15 @@ tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secur tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 +# hw/misc/tz-msc.c +tz_msc_reset(void) "TZ MSC: reset" +tz_msc_cfg_nonsec(int level) "TZ MSC: cfg_nonsec = %d" +tz_msc_cfg_sec_resp(int level) "TZ MSC: cfg_sec_resp = %d" +tz_msc_irq_enable(int level) "TZ MSC: int_enable = %d" +tz_msc_irq_clear(int level) "TZ MSC: int_clear = %d" +tz_msc_update_irq(int level) "TZ MSC: setting irq line to %d" +tz_msc_access_blocked(uint64_t offset) "TZ MSC: offset 0x%" PRIx64 " access blocked" + # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" @@ -116,3 +125,10 @@ ccm_freq(uint32_t freq) "freq = %d\n" ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d\n" ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 "\n" ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 "\n" + +# hw/misc/iotkit-sysctl.c +iotkit_sysinfo_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c new file mode 100644 index 0000000..9e35204 --- /dev/null +++ b/hw/misc/tz-msc.c @@ -0,0 +1,308 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-msc.h" + +static void tz_msc_update_irq(TZMSC *s) +{ + bool level = s->irq_status; + + trace_tz_msc_update_irq(level); + qemu_set_irq(s->irq, level); +} + +static void tz_msc_cfg_nonsec(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_nonsec(level); + s->cfg_nonsec = level; +} + +static void tz_msc_cfg_sec_resp(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_sec_resp(level); + s->cfg_sec_resp = level; +} + +static void tz_msc_irq_clear(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_irq_clear(level); + + s->irq_clear = level; + if (level) { + s->irq_status = false; + tz_msc_update_irq(s); + } +} + +/* The MSC may either block a transaction by aborting it, block a + * transaction by making it RAZ/WI, allow it through with + * MemTxAttrs indicating a secure transaction, or allow it with + * MemTxAttrs indicating a non-secure transaction. + */ +typedef enum MSCAction { + MSCBlockAbort, + MSCBlockRAZWI, + MSCAllowSecure, + MSCAllowNonSecure, +} MSCAction; + +static MSCAction tz_msc_check(TZMSC *s, hwaddr addr) +{ + /* + * Check whether to allow an access from the bus master, returning + * an MSCAction indicating the required behaviour. If the transaction + * is blocked, the caller must check cfg_sec_resp to determine + * whether to abort or RAZ/WI the transaction. + */ + IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(s->idau); + IDAUInterface *ii = IDAU_INTERFACE(s->idau); + bool idau_exempt = false, idau_ns = true, idau_nsc = true; + int idau_region = IREGION_NOTVALID; + + iic->check(ii, addr, &idau_region, &idau_exempt, &idau_ns, &idau_nsc); + + if (idau_exempt) { + /* + * Uncheck region -- OK, transaction type depends on + * whether bus master is configured as Secure or NonSecure + */ + return s->cfg_nonsec ? MSCAllowNonSecure : MSCAllowSecure; + } + + if (idau_ns) { + /* NonSecure region -- always forward as NS transaction */ + return MSCAllowNonSecure; + } + + if (!s->cfg_nonsec) { + /* Access to Secure region by Secure bus master: OK */ + return MSCAllowSecure; + } + + /* Attempted access to Secure region by NS bus master: block */ + trace_tz_msc_access_blocked(addr); + if (!s->cfg_sec_resp) { + return MSCBlockRAZWI; + } + + /* + * The TRM isn't clear on behaviour if irq_clear is high when a + * transaction is blocked. We assume that the MSC behaves like the + * PPC, where holding irq_clear high suppresses the interrupt. + */ + if (!s->irq_clear) { + s->irq_status = true; + tz_msc_update_irq(s); + } + return MSCBlockAbort; +} + +static MemTxResult tz_msc_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + uint64_t data; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + *pdata = 0; + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + data = address_space_ldub(as, addr, attrs, &res); + break; + case 2: + data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 4: + data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 8: + data = address_space_ldq_le(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + *pdata = data; + return res; +} + +static MemTxResult tz_msc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + address_space_stb(as, addr, val, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, val, attrs, &res); + break; + case 4: + address_space_stl_le(as, addr, val, attrs, &res); + break; + case 8: + address_space_stq_le(as, addr, val, attrs, &res); + break; + default: + g_assert_not_reached(); + } + return res; +} + +static const MemoryRegionOps tz_msc_ops = { + .read_with_attrs = tz_msc_read, + .write_with_attrs = tz_msc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void tz_msc_reset(DeviceState *dev) +{ + TZMSC *s = TZ_MSC(dev); + + trace_tz_msc_reset(); + s->cfg_sec_resp = false; + s->cfg_nonsec = false; + s->irq_clear = 0; + s->irq_status = 0; +} + +static void tz_msc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMSC *s = TZ_MSC(obj); + + qdev_init_gpio_in_named(dev, tz_msc_cfg_nonsec, "cfg_nonsec", 1); + qdev_init_gpio_in_named(dev, tz_msc_cfg_sec_resp, "cfg_sec_resp", 1); + qdev_init_gpio_in_named(dev, tz_msc_irq_clear, "irq_clear", 1); + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_msc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMSC *s = TZ_MSC(dev); + const char *name = "tz-msc-downstream"; + uint64_t size; + + /* + * We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the + * code with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MSC 'downstream' link not set"); + return; + } + if (!s->idau) { + error_setg(errp, "MSC 'idau' link not set"); + return; + } + + size = memory_region_size(s->downstream); + address_space_init(&s->downstream_as, s->downstream, name); + memory_region_init_io(&s->upstream, obj, &tz_msc_ops, s, name, size); + sysbus_init_mmio(sbd, &s->upstream); +} + +static const VMStateDescription tz_msc_vmstate = { + .name = "tz-msc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(cfg_nonsec, TZMSC), + VMSTATE_BOOL(cfg_sec_resp, TZMSC), + VMSTATE_BOOL(irq_clear, TZMSC), + VMSTATE_BOOL(irq_status, TZMSC), + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_msc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMSC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("idau", TZMSC, idau, + TYPE_IDAU_INTERFACE, IDAUInterface *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_msc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_msc_realize; + dc->vmsd = &tz_msc_vmstate; + dc->reset = tz_msc_reset; + dc->props = tz_msc_properties; +} + +static const TypeInfo tz_msc_info = { + .name = TYPE_TZ_MSC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMSC), + .instance_init = tz_msc_init, + .class_init = tz_msc_class_init, +}; + +static void tz_msc_register_types(void) +{ + type_register_static(&tz_msc_info); +} + +type_init(tz_msc_register_types); diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index c136801..e582475 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "hw/ssi/pl022.h" #include "hw/ssi/ssi.h" #include "qemu/log.h" @@ -37,35 +38,10 @@ do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0) #define PL022_SR_BSY 0x10 #define PL022_INT_ROR 0x01 -#define PL022_INT_RT 0x04 +#define PL022_INT_RT 0x02 #define PL022_INT_RX 0x04 #define PL022_INT_TX 0x08 -#define TYPE_PL022 "pl022" -#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) - -typedef struct PL022State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t cr0; - uint32_t cr1; - uint32_t bitmask; - uint32_t sr; - uint32_t cpsr; - uint32_t is; - uint32_t im; - /* The FIFO head points to the next empty entry. */ - int tx_fifo_head; - int rx_fifo_head; - int tx_fifo_len; - int rx_fifo_len; - uint16_t tx_fifo[8]; - uint16_t rx_fifo[8]; - qemu_irq irq; - SSIBus *ssi; -} PL022State; - static const unsigned char pl022_id[8] = { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -170,7 +146,7 @@ static uint64_t pl022_read(void *opaque, hwaddr offset, return s->is; case 0x1c: /* MIS */ return s->im & s->is; - case 0x20: /* DMACR */ + case 0x24: /* DMACR */ /* Not implemented. */ return 0; default: @@ -216,7 +192,15 @@ static void pl022_write(void *opaque, hwaddr offset, s->im = value; pl022_update(s); break; - case 0x20: /* DMACR */ + case 0x20: /* ICR */ + /* + * write-1-to-clear: bit 0 clears ROR, bit 1 clears RT; + * RX and TX interrupts cannot be cleared this way. + */ + value &= PL022_INT_ROR | PL022_INT_RT; + s->is &= ~value; + break; + case 0x24: /* DMACR */ if (value) { qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n"); } @@ -227,8 +211,10 @@ static void pl022_write(void *opaque, hwaddr offset, } } -static void pl022_reset(PL022State *s) +static void pl022_reset(DeviceState *dev) { + PL022State *s = PL022(dev); + s->rx_fifo_len = 0; s->tx_fifo_len = 0; s->im = 0; @@ -292,25 +278,24 @@ static const VMStateDescription vmstate_pl022 = { } }; -static int pl022_init(SysBusDevice *sbd) +static void pl022_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PL022State *s = PL022(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); s->ssi = ssi_create_bus(dev, "ssi"); - pl022_reset(s); - vmstate_register(dev, -1, &vmstate_pl022, s); - return 0; } static void pl022_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pl022_init; + dc->reset = pl022_reset; + dc->vmsd = &vmstate_pl022; + dc->realize = pl022_realize; } static const TypeInfo pl022_info = { diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index e16b2b9..b32194d 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -44,4 +44,5 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o +common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c new file mode 100644 index 0000000..ccd4975 --- /dev/null +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -0,0 +1,515 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" + +REG32(TIMER1LOAD, 0x0) +REG32(TIMER1VALUE, 0x4) +REG32(TIMER1CONTROL, 0x8) + FIELD(CONTROL, ONESHOT, 0, 1) + FIELD(CONTROL, SIZE, 1, 1) + FIELD(CONTROL, PRESCALE, 2, 2) + FIELD(CONTROL, INTEN, 5, 1) + FIELD(CONTROL, MODE, 6, 1) + FIELD(CONTROL, ENABLE, 7, 1) +#define R_CONTROL_VALID_MASK (R_CONTROL_ONESHOT_MASK | R_CONTROL_SIZE_MASK | \ + R_CONTROL_PRESCALE_MASK | R_CONTROL_INTEN_MASK | \ + R_CONTROL_MODE_MASK | R_CONTROL_ENABLE_MASK) +REG32(TIMER1INTCLR, 0xc) +REG32(TIMER1RIS, 0x10) +REG32(TIMER1MIS, 0x14) +REG32(TIMER1BGLOAD, 0x18) +REG32(TIMER2LOAD, 0x20) +REG32(TIMER2VALUE, 0x24) +REG32(TIMER2CONTROL, 0x28) +REG32(TIMER2INTCLR, 0x2c) +REG32(TIMER2RIS, 0x30) +REG32(TIMER2MIS, 0x34) +REG32(TIMER2BGLOAD, 0x38) +REG32(TIMERITCR, 0xf00) + FIELD(TIMERITCR, ENABLE, 0, 1) +#define R_TIMERITCR_VALID_MASK R_TIMERITCR_ENABLE_MASK +REG32(TIMERITOP, 0xf04) + FIELD(TIMERITOP, TIMINT1, 0, 1) + FIELD(TIMERITOP, TIMINT2, 1, 1) +#define R_TIMERITOP_VALID_MASK (R_TIMERITOP_TIMINT1_MASK | \ + R_TIMERITOP_TIMINT2_MASK) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int timer_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x23, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule *m) +{ + /* Return masked interrupt status for the timer module */ + return m->intstatus && (m->control & R_CONTROL_INTEN_MASK); +} + +static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s) +{ + bool timint1, timint2, timintc; + + if (s->timeritcr) { + /* Integration test mode: outputs driven directly from TIMERITOP bits */ + timint1 = s->timeritop & R_TIMERITOP_TIMINT1_MASK; + timint2 = s->timeritop & R_TIMERITOP_TIMINT2_MASK; + } else { + timint1 = cmsdk_dualtimermod_intstatus(&s->timermod[0]); + timint2 = cmsdk_dualtimermod_intstatus(&s->timermod[1]); + } + + timintc = timint1 || timint2; + + qemu_set_irq(s->timermod[0].timerint, timint1); + qemu_set_irq(s->timermod[1].timerint, timint2); + qemu_set_irq(s->timerintc, timintc); +} + +static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, + uint32_t newctrl) +{ + /* Handle a write to the CONTROL register */ + uint32_t changed; + + newctrl &= R_CONTROL_VALID_MASK; + + changed = m->control ^ newctrl; + + if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) { + /* ENABLE cleared, stop timer before any further changes */ + ptimer_stop(m->timer); + } + + if (changed & R_CONTROL_PRESCALE_MASK) { + int divisor; + + switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) { + case 0: + divisor = 1; + break; + case 1: + divisor = 16; + break; + case 2: + divisor = 256; + break; + case 3: + /* UNDEFINED; complain, and arbitrarily treat like 2 */ + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11" + " is undefined behaviour\n"); + divisor = 256; + break; + default: + g_assert_not_reached(); + } + ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor); + } + + if (changed & R_CONTROL_MODE_MASK) { + uint32_t load; + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic: the limit is the LOAD register value */ + load = m->load; + } else { + /* Free-running: counter wraps around */ + load = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load = deposit32(m->load, 0, 16, load); + } + m->load = load; + load = 0xffffffff; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load &= 0xffff; + } + ptimer_set_limit(m->timer, load, 0); + } + + if (changed & R_CONTROL_SIZE_MASK) { + /* Timer switched between 16 and 32 bit count */ + uint32_t value, load; + + value = ptimer_get_count(m->timer); + load = ptimer_get_limit(m->timer); + if (newctrl & R_CONTROL_SIZE_MASK) { + /* 16 -> 32, top half of VALUE is in struct field */ + value = deposit32(m->value, 0, 16, value); + } else { + /* 32 -> 16: save top half to struct field and truncate */ + m->value = value; + value &= 0xffff; + } + + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic, timer limit has LOAD value */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = deposit32(m->load, 0, 16, load); + } else { + m->load = load; + load &= 0xffff; + } + } else { + /* Free-running, timer limit is set to give wraparound */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = 0xffffffff; + } else { + load = 0xffff; + } + } + ptimer_set_count(m->timer, value); + ptimer_set_limit(m->timer, load, 0); + } + + if (newctrl & R_CONTROL_ENABLE_MASK) { + /* + * ENABLE is set; start the timer after all other changes. + * We start it even if the ENABLE bit didn't actually change, + * in case the timer was an expired one-shot timer that has + * now been changed into a free-running or periodic timer. + */ + ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK)); + } + + m->control = newctrl; +} + +static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset, + unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + uint64_t r; + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + r = s->timeritcr; + break; + case A_PID4 ... A_CID3: + r = timer_id[(offset - A_PID4) / 4]; + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer read: bad offset %x\n", + (int) offset); + r = 0; + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + case A_TIMER1BGLOAD: + if (m->control & R_CONTROL_MODE_MASK) { + /* + * Periodic: the ptimer limit is the LOAD register value, (or + * just the low 16 bits of it if the timer is in 16-bit mode) + */ + r = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->load, 0, 16, r); + } + } else { + /* Free-running: LOAD register value is just in m->load */ + r = m->load; + } + break; + case A_TIMER1VALUE: + r = ptimer_get_count(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->value, 0, 16, r); + } + break; + case A_TIMER1CONTROL: + r = m->control; + break; + case A_TIMER1RIS: + r = m->intstatus; + break; + case A_TIMER1MIS: + r = cmsdk_dualtimermod_intstatus(m); + break; + default: + goto bad_offset; + } + } + + trace_cmsdk_apb_dualtimer_read(offset, r, size); + return r; +} + +static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + + trace_cmsdk_apb_dualtimer_write(offset, value, size); + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + s->timeritcr = value & R_TIMERITCR_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + case A_TIMERITOP: + s->timeritop = value & R_TIMERITOP_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer write: bad offset %x\n", + (int) offset); + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + /* Set the limit, and immediately reload the count from it */ + m->load = value; + m->value = value; + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* + * In free-running mode this won't set the limit but will + * still change the current count value. + */ + ptimer_set_count(m->timer, value); + } else { + if (!value) { + ptimer_stop(m->timer); + } + ptimer_set_limit(m->timer, value, 1); + if (value && (m->control & R_CONTROL_ENABLE_MASK)) { + /* Force possibly-expired oneshot timer to restart */ + ptimer_run(m->timer, 1); + } + } + break; + case A_TIMER1BGLOAD: + /* Set the limit, but not the current count */ + m->load = value; + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* In free-running mode there is no limit */ + break; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + ptimer_set_limit(m->timer, value, 0); + break; + case A_TIMER1CONTROL: + cmsdk_dualtimermod_write_control(m, value); + cmsdk_apb_dualtimer_update(s); + break; + case A_TIMER1INTCLR: + m->intstatus = 0; + cmsdk_apb_dualtimer_update(s); + break; + default: + goto bad_offset; + } + } +} + +static const MemoryRegionOps cmsdk_apb_dualtimer_ops = { + .read = cmsdk_apb_dualtimer_read, + .write = cmsdk_apb_dualtimer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void cmsdk_dualtimermod_tick(void *opaque) +{ + CMSDKAPBDualTimerModule *m = opaque; + + m->intstatus = 1; + cmsdk_apb_dualtimer_update(m->parent); +} + +static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) +{ + m->control = R_CONTROL_INTEN_MASK; + m->intstatus = 0; + m->load = 0; + m->value = 0xffffffff; + ptimer_stop(m->timer); + /* + * We start in free-running mode, with VALUE at 0xffffffff, and + * in 16-bit counter mode. This means that the ptimer count and + * 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); +} + +static void cmsdk_apb_dualtimer_reset(DeviceState *dev) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); + int i; + + trace_cmsdk_apb_dualtimer_reset(); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + cmsdk_dualtimermod_reset(&s->timermod[i]); + } + s->timeritcr = 0; + s->timeritop = 0; +} + +static void cmsdk_apb_dualtimer_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops, + s, "cmsdk-apb-dualtimer", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->timerintc); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + sysbus_init_irq(sbd, &s->timermod[i].timerint); + } +} + +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"); + return; + } + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + CMSDKAPBDualTimerModule *m = &s->timermod[i]; + QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m); + + m->parent = s; + m->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + } +} + +static const VMStateDescription cmsdk_dualtimermod_vmstate = { + .name = "cmsdk-apb-dualtimer-module", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(load, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(value, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(control, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { + .name = "cmsdk-apb-dualtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer, + CMSDK_APB_DUALTIMER_NUM_MODULES, + 1, cmsdk_dualtimermod_vmstate, + CMSDKAPBDualTimerModule), + VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer), + VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer), + VMSTATE_END_OF_LIST() + } +}; + +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); + + dc->realize = cmsdk_apb_dualtimer_realize; + dc->vmsd = &cmsdk_apb_dualtimer_vmstate; + dc->reset = cmsdk_apb_dualtimer_reset; + dc->props = cmsdk_apb_dualtimer_properties; +} + +static const TypeInfo cmsdk_apb_dualtimer_info = { + .name = TYPE_CMSDK_APB_DUALTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBDualTimer), + .instance_init = cmsdk_apb_dualtimer_init, + .class_init = cmsdk_apb_dualtimer_class_init, +}; + +static void cmsdk_apb_dualtimer_register_types(void) +{ + type_register_static(&cmsdk_apb_dualtimer_info); +} + +type_init(cmsdk_apb_dualtimer_register_types); diff --git a/hw/timer/trace-events b/hw/timer/trace-events index e6e042f..fa4213d 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -61,5 +61,10 @@ cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB t cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset" +# hw/timer/cmsdk_apb_dualtimer.c +cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" + # hw/timer/xlnx-zynqmp-rtc.c xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 69f4dbc..cc1b58b 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -190,49 +190,88 @@ enum { /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ + +float16 int16_to_float16_scalbn(int16_t a, int, float_status *status); +float16 int32_to_float16_scalbn(int32_t a, int, float_status *status); +float16 int64_to_float16_scalbn(int64_t a, int, float_status *status); +float16 uint16_to_float16_scalbn(uint16_t a, int, float_status *status); +float16 uint32_to_float16_scalbn(uint32_t a, int, float_status *status); +float16 uint64_to_float16_scalbn(uint64_t a, int, float_status *status); + +float16 int16_to_float16(int16_t a, float_status *status); +float16 int32_to_float16(int32_t a, float_status *status); +float16 int64_to_float16(int64_t a, float_status *status); +float16 uint16_to_float16(uint16_t a, float_status *status); +float16 uint32_to_float16(uint32_t a, float_status *status); +float16 uint64_to_float16(uint64_t a, float_status *status); + +float32 int16_to_float32_scalbn(int16_t, int, float_status *status); +float32 int32_to_float32_scalbn(int32_t, int, float_status *status); +float32 int64_to_float32_scalbn(int64_t, int, float_status *status); +float32 uint16_to_float32_scalbn(uint16_t, int, float_status *status); +float32 uint32_to_float32_scalbn(uint32_t, int, float_status *status); +float32 uint64_to_float32_scalbn(uint64_t, int, float_status *status); + float32 int16_to_float32(int16_t, float_status *status); float32 int32_to_float32(int32_t, float_status *status); -float64 int16_to_float64(int16_t, float_status *status); -float64 int32_to_float64(int32_t, float_status *status); +float32 int64_to_float32(int64_t, float_status *status); float32 uint16_to_float32(uint16_t, float_status *status); float32 uint32_to_float32(uint32_t, float_status *status); +float32 uint64_to_float32(uint64_t, float_status *status); + +float64 int16_to_float64_scalbn(int16_t, int, float_status *status); +float64 int32_to_float64_scalbn(int32_t, int, float_status *status); +float64 int64_to_float64_scalbn(int64_t, int, float_status *status); +float64 uint16_to_float64_scalbn(uint16_t, int, float_status *status); +float64 uint32_to_float64_scalbn(uint32_t, int, float_status *status); +float64 uint64_to_float64_scalbn(uint64_t, int, float_status *status); + +float64 int16_to_float64(int16_t, float_status *status); +float64 int32_to_float64(int32_t, float_status *status); +float64 int64_to_float64(int64_t, float_status *status); float64 uint16_to_float64(uint16_t, float_status *status); float64 uint32_to_float64(uint32_t, float_status *status); +float64 uint64_to_float64(uint64_t, float_status *status); + floatx80 int32_to_floatx80(int32_t, float_status *status); -float128 int32_to_float128(int32_t, float_status *status); -float32 int64_to_float32(int64_t, float_status *status); -float64 int64_to_float64(int64_t, float_status *status); floatx80 int64_to_floatx80(int64_t, float_status *status); + +float128 int32_to_float128(int32_t, float_status *status); float128 int64_to_float128(int64_t, float_status *status); -float32 uint64_to_float32(uint64_t, float_status *status); -float64 uint64_to_float64(uint64_t, float_status *status); float128 uint64_to_float128(uint64_t, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ + float16 float32_to_float16(float32, bool ieee, float_status *status); float32 float16_to_float32(float16, bool ieee, float_status *status); float16 float64_to_float16(float64 a, bool ieee, float_status *status); float64 float16_to_float64(float16 a, bool ieee, float_status *status); + +int16_t float16_to_int16_scalbn(float16, int, int, float_status *status); +int32_t float16_to_int32_scalbn(float16, int, int, float_status *status); +int64_t float16_to_int64_scalbn(float16, int, int, float_status *status); + int16_t float16_to_int16(float16, float_status *status); -uint16_t float16_to_uint16(float16 a, float_status *status); -int16_t float16_to_int16_round_to_zero(float16, float_status *status); -uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); int32_t float16_to_int32(float16, float_status *status); -uint32_t float16_to_uint32(float16 a, float_status *status); -int32_t float16_to_int32_round_to_zero(float16, float_status *status); -uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); int64_t float16_to_int64(float16, float_status *status); -uint64_t float16_to_uint64(float16 a, float_status *status); + +int16_t float16_to_int16_round_to_zero(float16, float_status *status); +int32_t float16_to_int32_round_to_zero(float16, float_status *status); int64_t float16_to_int64_round_to_zero(float16, float_status *status); + +uint16_t float16_to_uint16_scalbn(float16 a, int, int, float_status *status); +uint32_t float16_to_uint32_scalbn(float16 a, int, int, float_status *status); +uint64_t float16_to_uint64_scalbn(float16 a, int, int, float_status *status); + +uint16_t float16_to_uint16(float16 a, float_status *status); +uint32_t float16_to_uint32(float16 a, float_status *status); +uint64_t float16_to_uint64(float16 a, float_status *status); + +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *status); -float16 int16_to_float16(int16_t a, float_status *status); -float16 int32_to_float16(int32_t a, float_status *status); -float16 int64_to_float16(int64_t a, float_status *status); -float16 uint16_to_float16(uint16_t a, float_status *status); -float16 uint32_to_float16(uint32_t a, float_status *status); -float16 uint64_to_float16(uint64_t a, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision operations. @@ -321,18 +360,31 @@ float16 float16_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float32_to_int16_scalbn(float32, int, int, float_status *status); +int32_t float32_to_int32_scalbn(float32, int, int, float_status *status); +int64_t float32_to_int64_scalbn(float32, int, int, float_status *status); + int16_t float32_to_int16(float32, float_status *status); -uint16_t float32_to_uint16(float32, float_status *status); -int16_t float32_to_int16_round_to_zero(float32, float_status *status); -uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); int32_t float32_to_int32(float32, float_status *status); +int64_t float32_to_int64(float32, float_status *status); + +int16_t float32_to_int16_round_to_zero(float32, float_status *status); int32_t float32_to_int32_round_to_zero(float32, float_status *status); +int64_t float32_to_int64_round_to_zero(float32, float_status *status); + +uint16_t float32_to_uint16_scalbn(float32, int, int, float_status *status); +uint32_t float32_to_uint32_scalbn(float32, int, int, float_status *status); +uint64_t float32_to_uint64_scalbn(float32, int, int, float_status *status); + +uint16_t float32_to_uint16(float32, float_status *status); uint32_t float32_to_uint32(float32, float_status *status); -uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); -int64_t float32_to_int64(float32, float_status *status); uint64_t float32_to_uint64(float32, float_status *status); + +uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); +uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); uint64_t float32_to_uint64_round_to_zero(float32, float_status *status); -int64_t float32_to_int64_round_to_zero(float32, float_status *status); + float64 float32_to_float64(float32, float_status *status); floatx80 float32_to_floatx80(float32, float_status *status); float128 float32_to_float128(float32, float_status *status); @@ -450,18 +502,31 @@ float32 float32_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float64_to_int16_scalbn(float64, int, int, float_status *status); +int32_t float64_to_int32_scalbn(float64, int, int, float_status *status); +int64_t float64_to_int64_scalbn(float64, int, int, float_status *status); + int16_t float64_to_int16(float64, float_status *status); -uint16_t float64_to_uint16(float64, float_status *status); -int16_t float64_to_int16_round_to_zero(float64, float_status *status); -uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); int32_t float64_to_int32(float64, float_status *status); +int64_t float64_to_int64(float64, float_status *status); + +int16_t float64_to_int16_round_to_zero(float64, float_status *status); int32_t float64_to_int32_round_to_zero(float64, float_status *status); +int64_t float64_to_int64_round_to_zero(float64, float_status *status); + +uint16_t float64_to_uint16_scalbn(float64, int, int, float_status *status); +uint32_t float64_to_uint32_scalbn(float64, int, int, float_status *status); +uint64_t float64_to_uint64_scalbn(float64, int, int, float_status *status); + +uint16_t float64_to_uint16(float64, float_status *status); uint32_t float64_to_uint32(float64, float_status *status); +uint64_t float64_to_uint64(float64, float_status *status); + +uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); uint32_t float64_to_uint32_round_to_zero(float64, float_status *status); -int64_t float64_to_int64(float64, float_status *status); -int64_t float64_to_int64_round_to_zero(float64, float_status *status); -uint64_t float64_to_uint64(float64 a, float_status *status); -uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *status); +uint64_t float64_to_uint64_round_to_zero(float64, float_status *status); + float32 float64_to_float32(float64, float_status *status); floatx80 float64_to_floatx80(float64, float_status *status); float128 float64_to_float128(float64, float_status *status); diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index 2cddde5..3a8ee63 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -28,6 +28,9 @@ * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which * are wired to the NVIC lines 32 .. n+32 + * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows + * bus master devices in the board model to make transactions into + * all the devices and memory areas in the IoTKit * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit * might provide: * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] @@ -45,6 +48,11 @@ * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] */ #ifndef IOTKIT_H @@ -56,7 +64,10 @@ #include "hw/misc/tz-ppc.h" #include "hw/misc/tz-mpc.h" #include "hw/timer/cmsdk-apb-timer.h" -#include "hw/misc/unimp.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" +#include "hw/misc/iotkit-sysctl.h" +#include "hw/misc/iotkit-sysinfo.h" #include "hw/or-irq.h" #include "hw/core/split-irq.h" @@ -81,14 +92,22 @@ typedef struct IoTKit { TZMPC mpc; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; + CMSDKAPBTIMER s32ktimer; qemu_or_irq ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; qemu_or_irq mpc_irq_orgate; + qemu_or_irq nmi_orgate; + + CMSDKAPBDualTimer dualtimer; + + CMSDKAPBWatchdog s32kwatchdog; + CMSDKAPBWatchdog nswatchdog; + CMSDKAPBWatchdog swatchdog; - UnimplementedDeviceState dualtimer; - UnimplementedDeviceState s32ktimer; + IoTKitSysCtl sysctl; + IoTKitSysCtl sysinfo; MemoryRegion container; MemoryRegion alias1; diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index ae0a380..228988b 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -17,6 +17,20 @@ #define TYPE_BCM2835_FB "bcm2835-fb" #define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) +/* + * Configuration information about the fb which the guest can program + * via the mailbox property interface. + */ +typedef struct { + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base; + uint32_t pixo; + uint32_t alpha; +} BCM2835FBConfig; + typedef struct { /*< private >*/ SysBusDevice busdev; @@ -31,16 +45,43 @@ typedef struct { qemu_irq mbox_irq; bool lock, invalidate, pending; - uint32_t xres, yres; - uint32_t xres_virtual, yres_virtual; - uint32_t xoffset, yoffset; - uint32_t bpp; - uint32_t base, pitch, size; - uint32_t pixo, alpha; + + BCM2835FBConfig config; + BCM2835FBConfig initial_config; } BCM2835FBState; -void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, - uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, - uint32_t *pixo, uint32_t *alpha); +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig); + +/** + * bcm2835_fb_get_pitch: return number of bytes per line of the framebuffer + * @config: configuration info for the framebuffer + * + * Return the number of bytes per line of the framebuffer, ie the number + * that must be added to a pixel address to get the address of the pixel + * directly below it on screen. + */ +static inline uint32_t bcm2835_fb_get_pitch(BCM2835FBConfig *config) +{ + uint32_t xres = MAX(config->xres, config->xres_virtual); + return xres * (config->bpp >> 3); +} + +/** + * bcm2835_fb_get_size: return total size of framebuffer in bytes + * @config: configuration info for the framebuffer + */ +static inline uint32_t bcm2835_fb_get_size(BCM2835FBConfig *config) +{ + uint32_t yres = MAX(config->yres, config->yres_virtual); + return yres * bcm2835_fb_get_pitch(config); +} + +/** + * bcm2835_fb_validate_config: check provided config + * + * Validates the configuration information provided by the guest and + * adjusts it if necessary. + */ +void bcm2835_fb_validate_config(BCM2835FBConfig *config); #endif diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index 082c14c..1a193b3 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -19,6 +19,7 @@ * + named GPIO output "sec_resp_cfg" indicating whether blocked accesses * should RAZ/WI or bus error * + named GPIO output "nsc_cfg" whose value tracks the NSCCFG register value + * + named GPIO output "msc_irq" for the combined IRQ line from the MSCs * Controlling the 2 APB PPCs in the IoTKit: * + named GPIO outputs apb_ppc0_nonsec[0..2] and apb_ppc1_nonsec * + named GPIO outputs apb_ppc0_ap[0..2] and apb_ppc1_ap @@ -44,6 +45,11 @@ * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -62,6 +68,7 @@ #define IOTS_NUM_AHB_EXP_PPC 4 #define IOTS_NUM_EXP_MPC 16 #define IOTS_NUM_MPC 1 +#define IOTS_NUM_EXP_MSC 16 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -103,6 +110,13 @@ struct IoTKitSecCtl { uint32_t brginten; uint32_t mpcintstatus; + uint32_t secmscintstat; + uint32_t secmscinten; + uint32_t nsmscexp; + qemu_irq mscexp_clear[IOTS_NUM_EXP_MSC]; + qemu_irq mscexp_ns[IOTS_NUM_EXP_MSC]; + qemu_irq msc_irq; + IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; IoTKitSecCtlPPC ahbexp[IOTS_NUM_APB_EXP_PPC]; diff --git a/include/hw/misc/iotkit-sysctl.h b/include/hw/misc/iotkit-sysctl.h new file mode 100644 index 0000000..e36613c --- /dev/null +++ b/include/hw/misc/iotkit-sysctl.h @@ -0,0 +1,49 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system information block" and + * "system control register" blocks. + * + * QEMU interface: + * + sysbus MMIO region 0: the system information register bank + * + sysbus MMIO region 1: the system control register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSCTL_H +#define HW_MISC_IOTKIT_SYSCTL_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSCTL "iotkit-sysctl" +#define IOTKIT_SYSCTL(obj) OBJECT_CHECK(IoTKitSysCtl, (obj), \ + TYPE_IOTKIT_SYSCTL) + +typedef struct IoTKitSysCtl { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t secure_debug; + uint32_t reset_syndrome; + uint32_t reset_mask; + uint32_t gretreg; + uint32_t initsvrtor0; + uint32_t cpuwait; + uint32_t wicctrl; +} IoTKitSysCtl; + +#endif diff --git a/include/hw/misc/iotkit-sysinfo.h b/include/hw/misc/iotkit-sysinfo.h new file mode 100644 index 0000000..7b2e1a5 --- /dev/null +++ b/include/hw/misc/iotkit-sysinfo.h @@ -0,0 +1,37 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * QEMU interface: + * + sysbus MMIO region 0: the system information register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSINFO_H +#define HW_MISC_IOTKIT_SYSINFO_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSINFO "iotkit-sysinfo" +#define IOTKIT_SYSINFO(obj) OBJECT_CHECK(IoTKitSysInfo, (obj), \ + TYPE_IOTKIT_SYSINFO) + +typedef struct IoTKitSysInfo { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; +} IoTKitSysInfo; + +#endif diff --git a/include/hw/misc/mps2-fpgaio.h b/include/hw/misc/mps2-fpgaio.h index eedf17e..69e265c 100644 --- a/include/hw/misc/mps2-fpgaio.h +++ b/include/hw/misc/mps2-fpgaio.h @@ -37,7 +37,17 @@ typedef struct { uint32_t prescale; uint32_t misc; + /* QEMU_CLOCK_VIRTUAL time at which counter and pscntr were last synced */ + int64_t pscntr_sync_ticks; + /* Values of COUNTER and PSCNTR at time pscntr_sync_ticks */ + uint32_t counter; + uint32_t pscntr; + uint32_t prescale_clk; + + /* These hold the CLOCK_VIRTUAL ns tick when the CLK1HZ/CLK100HZ was zero */ + int64_t clk1hz_tick_offset; + int64_t clk100hz_tick_offset; } MPS2FPGAIO; #endif diff --git a/include/hw/misc/tz-msc.h b/include/hw/misc/tz-msc.h new file mode 100644 index 0000000..116b96a --- /dev/null +++ b/include/hw/misc/tz-msc.h @@ -0,0 +1,79 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the TrustZone master security controller (MSC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MSC sits in front of a device which can be a bus master (such as + * a DMA controller) and allows secure software to configure it to either + * pass through or reject transactions made by that bus master. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MSC has no register interface -- it is configured purely by a + * collection of input signals from other hardware in the system. Typically + * they are either hardwired or exposed in an ad-hoc register interface by + * the SoC that uses the MSC. + * + * We don't currently implement the irq_enable GPIO input, because on + * the MPS2 FPGA images it is always tied high, which is awkward to + * implement in QEMU. + * + * QEMU interface: + * + Named GPIO input "cfg_nonsec": set to 1 if the bus master should be + * treated as nonsecure, or 0 for secure + * + Named GPIO input "cfg_sec_resp": set to 1 if a rejected transaction should + * result in a transaction error, or 0 for the transaction to RAZ/WI + * + Named GPIO input "irq_clear": set to 1 to clear a pending interrupt + * + Named GPIO output "irq": set for a transaction-failed interrupt + * + Property "downstream": MemoryRegion defining where bus master transactions + * are made if they are not blocked + * + Property "idau": an object implementing IDAUInterface, which defines which + * addresses should be treated as secure and which as non-secure. + * This need not be the same IDAU as the one used by the CPU. + * + sysbus MMIO region 0: MemoryRegion defining the upstream end of the MSC; + * this should be passed to the bus master device as the region it should + * make memory transactions to + */ + +#ifndef TZ_MSC_H +#define TZ_MSC_H + +#include "hw/sysbus.h" +#include "target/arm/idau.h" + +#define TYPE_TZ_MSC "tz-msc" +#define TZ_MSC(obj) OBJECT_CHECK(TZMSC, (obj), TYPE_TZ_MSC) + +typedef struct TZMSC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + /* State: these just track the values of our input signals */ + bool cfg_nonsec; + bool cfg_sec_resp; + bool irq_clear; + /* State: are we asserting irq ? */ + bool irq_status; + + qemu_irq irq; + MemoryRegion *downstream; + AddressSpace downstream_as; + MemoryRegion upstream; + IDAUInterface *idau; +} TZMSC; + +#endif diff --git a/include/hw/ssi/pl022.h b/include/hw/ssi/pl022.h new file mode 100644 index 0000000..a080519 --- /dev/null +++ b/include/hw/ssi/pl022.h @@ -0,0 +1,51 @@ +/* + * ARM PrimeCell PL022 Synchronous Serial Port + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm PrimeCell PL022 synchronous serial port. + * The PL022 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0194h/DDI0194H_ssp_pl022_trm.pdf + * + * QEMU interface: + * + sysbus IRQ: SSPINTR combined interrupt line + * + sysbus MMIO region 0: MemoryRegion for the device's registers + */ + +#ifndef HW_SSI_PL022_H +#define HW_SSI_PL022_H + +#include "hw/sysbus.h" + +#define TYPE_PL022 "pl022" +#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) + +typedef struct PL022State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t cr0; + uint32_t cr1; + uint32_t bitmask; + uint32_t sr; + uint32_t cpsr; + uint32_t is; + uint32_t im; + /* The FIFO head points to the next empty entry. */ + int tx_fifo_head; + int rx_fifo_head; + int tx_fifo_len; + int rx_fifo_len; + uint16_t tx_fifo[8]; + uint16_t rx_fifo[8]; + qemu_irq irq; + SSIBus *ssi; +} PL022State; + +#endif diff --git a/include/hw/timer/cmsdk-apb-dualtimer.h b/include/hw/timer/cmsdk-apb-dualtimer.h new file mode 100644 index 0000000..9843a9d --- /dev/null +++ b/include/hw/timer/cmsdk-apb-dualtimer.h @@ -0,0 +1,72 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * 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 + * + sysbus MMIO region 0: the register bank + * + sysbus IRQ 0: combined timer interrupt TIMINTC + * + sysbus IRO 1: timer block 1 interrupt TIMINT1 + * + sysbus IRQ 2: timer block 2 interrupt TIMINT2 + */ + +#ifndef CMSDK_APB_DUALTIMER_H +#define CMSDK_APB_DUALTIMER_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +#define TYPE_CMSDK_APB_DUALTIMER "cmsdk-apb-dualtimer" +#define CMSDK_APB_DUALTIMER(obj) OBJECT_CHECK(CMSDKAPBDualTimer, (obj), \ + TYPE_CMSDK_APB_DUALTIMER) + +typedef struct CMSDKAPBDualTimer CMSDKAPBDualTimer; + +/* One of the two identical timer modules in the dual-timer module */ +typedef struct CMSDKAPBDualTimerModule { + CMSDKAPBDualTimer *parent; + struct ptimer_state *timer; + qemu_irq timerint; + /* + * We must track the guest LOAD and VALUE register state by hand + * rather than leaving this state only in the ptimer limit/count, + * because if CONTROL.SIZE is 0 then only the low 16 bits of the + * counter actually counts, but the high half is still guest + * accessible. + */ + uint32_t load; + uint32_t value; + uint32_t control; + uint32_t intstatus; +} CMSDKAPBDualTimerModule; + +#define CMSDK_APB_DUALTIMER_NUM_MODULES 2 + +struct CMSDKAPBDualTimer { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq timerintc; + uint32_t pclk_frq; + + CMSDKAPBDualTimerModule timermod[CMSDK_APB_DUALTIMER_NUM_MODULES]; + uint32_t timeritcr; + uint32_t timeritop; +}; + +#endif diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c index 7cac873..b2b22d2 100644 --- a/target/arm/arm-semi.c +++ b/target/arm/arm-semi.c @@ -136,7 +136,7 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #ifdef CONFIG_USER_ONLY ts->swi_errno = err; #else - syscall_err = err; + syscall_err = err; #endif reg0 = ret; } else { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 62c36b4..65c0fa0 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1320,14 +1320,14 @@ enum arm_cpu_mode { #define ARM_VFP_FPINST2 10 /* iwMMXt coprocessor control registers. */ -#define ARM_IWMMXT_wCID 0 -#define ARM_IWMMXT_wCon 1 -#define ARM_IWMMXT_wCSSF 2 -#define ARM_IWMMXT_wCASF 3 -#define ARM_IWMMXT_wCGR0 8 -#define ARM_IWMMXT_wCGR1 9 -#define ARM_IWMMXT_wCGR2 10 -#define ARM_IWMMXT_wCGR3 11 +#define ARM_IWMMXT_wCID 0 +#define ARM_IWMMXT_wCon 1 +#define ARM_IWMMXT_wCSSF 2 +#define ARM_IWMMXT_wCASF 3 +#define ARM_IWMMXT_wCGR0 8 +#define ARM_IWMMXT_wCGR1 9 +#define ARM_IWMMXT_wCGR2 10 +#define ARM_IWMMXT_wCGR3 11 /* V7M CCR bits */ FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1b05480..088f452 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -3754,11 +3754,11 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, - { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "HCR_EL2", .state = ARM_CP_STATE_BOTH, .type = ARM_CP_NO_RAW, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, - .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, @@ -3857,6 +3857,15 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { REGINFO_SENTINEL }; +/* Ditto, but for registers which exist in ARMv8 but not v7 */ +static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -3883,10 +3892,26 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) * HCR_PTW forbids certain page-table setups * HCR_DC Disables stage1 and enables stage2 translation */ - if ((raw_read(env, ri) ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { + if ((env->cp15.hcr_el2 ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { tlb_flush(CPU(cpu)); } - raw_write(env, ri, value); + env->cp15.hcr_el2 = value; +} + +static void hcr_writehigh(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR2 write, i.e. write to high half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 32, 32, value); + hcr_write(env, NULL, value); +} + +static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR write, i.e. write to low half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 0, 32, value); + hcr_write(env, NULL, value); } static const ARMCPRegInfo el2_cp_reginfo[] = { @@ -3894,6 +3919,11 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, + { .name = "HCR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, + .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writelow }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, @@ -4128,6 +4158,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writehigh }, + REGINFO_SENTINEL +}; + static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -5179,6 +5219,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el2_v8_cp_reginfo); + } /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { ARMCPRegInfo rvbar = { @@ -5211,6 +5254,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el3_no_el2_v8_cp_reginfo); + } } } if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -5459,6 +5505,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) REGINFO_SENTINEL }; define_arm_cp_regs(cpu, auxcr_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + /* HACTLR2 maps to ACTLR_EL2[63:32] and is not in ARMv7 */ + ARMCPRegInfo hactlr2_reginfo = { + .name = "HACTLR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &hactlr2_reginfo); + } } if (arm_feature(env, ARM_FEATURE_CBAR)) { @@ -7977,6 +8033,125 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[15] = env->pc; } +static void take_aarch32_exception(CPUARMState *env, int new_mode, + uint32_t mask, uint32_t offset, + uint32_t newpc) +{ + /* Change the CPU state so as to actually take the exception. */ + switch_mode(env, new_mode); + /* + * For exceptions taken to AArch32 we must clear the SS bit in both + * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now. + */ + env->uncached_cpsr &= ~PSTATE_SS; + env->spsr = cpsr_read(env); + /* Clear IT bits. */ + env->condexec_bits = 0; + /* Switch to the new mode, and to the correct instruction set. */ + env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; + /* Set new mode endianness */ + env->uncached_cpsr &= ~CPSR_E; + if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { + env->uncached_cpsr |= CPSR_E; + } + /* J and IL must always be cleared for exception entry */ + env->uncached_cpsr &= ~(CPSR_IL | CPSR_J); + env->daif |= mask; + + if (new_mode == ARM_CPU_MODE_HYP) { + env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0; + env->elr_el[2] = env->regs[15]; + } else { + /* + * this is a lie, as there was no c1_sys on V4T/V5, but who cares + * and we should just guard the thumb mode on V4 + */ + if (arm_feature(env, ARM_FEATURE_V4T)) { + env->thumb = + (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; + } + env->regs[14] = env->regs[15] + offset; + } + env->regs[15] = newpc; +} + +static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) +{ + /* + * Handle exception entry to Hyp mode; this is sufficiently + * different to entry to other AArch32 modes that we handle it + * separately here. + * + * The vector table entry used is always the 0x14 Hyp mode entry point, + * unless this is an UNDEF/HVC/abort taken from Hyp to Hyp. + * The offset applied to the preferred return address is always zero + * (see DDI0487C.a section G1.12.3). + * PSTATE A/I/F masks are set based only on the SCR.EA/IRQ/FIQ values. + */ + uint32_t addr, mask; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (cs->exception_index) { + case EXCP_UDEF: + addr = 0x04; + break; + case EXCP_SWI: + addr = 0x14; + break; + case EXCP_BKPT: + /* Fall through to prefetch abort. */ + case EXCP_PREFETCH_ABORT: + env->cp15.ifar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HIFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x0c; + break; + case EXCP_DATA_ABORT: + env->cp15.dfar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HDFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x10; + break; + case EXCP_IRQ: + addr = 0x18; + break; + case EXCP_FIQ: + addr = 0x1c; + break; + case EXCP_HVC: + addr = 0x08; + break; + case EXCP_HYP_TRAP: + addr = 0x14; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + } + + if (cs->exception_index != EXCP_IRQ && cs->exception_index != EXCP_FIQ) { + env->cp15.esr_el[2] = env->exception.syndrome; + } + + if (arm_current_el(env) != 2 && addr < 0x14) { + addr = 0x14; + } + + mask = 0; + if (!(env->cp15.scr_el3 & SCR_EA)) { + mask |= CPSR_A; + } + if (!(env->cp15.scr_el3 & SCR_IRQ)) { + mask |= CPSR_I; + } + if (!(env->cp15.scr_el3 & SCR_FIQ)) { + mask |= CPSR_F; + } + + addr += env->cp15.hvbar; + + take_aarch32_exception(env, ARM_CPU_MODE_HYP, mask, 0, addr); +} + static void arm_cpu_do_interrupt_aarch32(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -8012,6 +8187,11 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.mdscr_el1 = deposit64(env->cp15.mdscr_el1, 2, 4, moe); } + if (env->exception.target_el == 2) { + arm_cpu_do_interrupt_aarch32_hyp(cs); + return; + } + /* TODO: Vectored interrupt controller. */ switch (cs->exception_index) { case EXCP_UDEF: @@ -8119,29 +8299,7 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.scr_el3 &= ~SCR_NS; } - switch_mode (env, new_mode); - /* For exceptions taken to AArch32 we must clear the SS bit in both - * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now. - */ - env->uncached_cpsr &= ~PSTATE_SS; - env->spsr = cpsr_read(env); - /* Clear IT bits. */ - env->condexec_bits = 0; - /* Switch to the new mode, and to the correct instruction set. */ - env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; - /* Set new mode endianness */ - env->uncached_cpsr &= ~CPSR_E; - if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { - env->uncached_cpsr |= CPSR_E; - } - env->daif |= mask; - /* this is a lie, as the was no c1_sys on V4T/V5, but who cares - * and we should just guard the thumb mode on V4 */ - if (arm_feature(env, ARM_FEATURE_V4T)) { - env->thumb = (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; - } - env->regs[14] = env->regs[15] + offset; - env->regs[15] = addr; + take_aarch32_exception(env, new_mode, mask, offset, addr); } /* Handle exception entry to a target EL which is using AArch64 */ @@ -11564,45 +11722,30 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) #define VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ float##fsz HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - float##fsz tmp; \ - tmp = itype##_to_##float##fsz(x, fpst); \ - return float##fsz##_scalbn(tmp, -(int)shift, fpst); \ -} +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); } -/* Notice that we want only input-denormal exception flags from the - * scalbn operation: the other possible flags (overflow+inexact if - * we overflow to infinity, output-denormal) aren't correct for the - * complete scale-and-convert operation. - */ -#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, round) \ -uint##isz##_t HELPER(vfp_to##name##p##round)(float##fsz x, \ - uint32_t shift, \ - void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - int old_exc_flags = get_float_exception_flags(fpst); \ - float##fsz tmp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - tmp = float##fsz##_scalbn(x, shift, fpst); \ - old_exc_flags |= get_float_exception_flags(fpst) \ - & float_flag_input_denormal; \ - set_float_exception_flags(old_exc_flags, fpst); \ - return float##fsz##_to_##itype##round(tmp, fpst); \ +#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ROUND, suff) \ +uint##isz##_t HELPER(vfp_to##name##p##suff)(float##fsz x, uint32_t shift, \ + void *fpst) \ +{ \ + if (unlikely(float##fsz##_is_any_nan(x))) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ } #define VFP_CONV_FIX(name, p, fsz, isz, itype) \ VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, _round_to_zero) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + float_round_to_zero, _round_to_zero) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) #define VFP_CONV_FIX_A64(name, p, fsz, isz, itype) \ VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) VFP_CONV_FIX(sh, d, 64, 64, int16) VFP_CONV_FIX(sl, d, 64, 64, int32) @@ -11622,87 +11765,84 @@ VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) #undef VFP_CONV_FLOAT_FIX_ROUND #undef VFP_CONV_FIX_A64 -/* Conversion to/from f16 can overflow to infinity before/after scaling. - * Therefore we convert to f64, scale, and then convert f64 to f16; or - * vice versa for conversion to integer. - * - * For 16- and 32-bit integers, the conversion to f64 never rounds. - * For 64-bit integers, any integer that would cause rounding will also - * overflow to f16 infinity, so there is no double rounding problem. - */ - -static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) -{ - return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); -} - uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); + return int32_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); + return uint32_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); + return int64_to_float16_scalbn(x, -shift, fpst); } uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) { - return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); + return uint64_to_float16_scalbn(x, -shift, fpst); } -static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) { - if (unlikely(float16_is_any_nan(f))) { + if (unlikely(float16_is_any_nan(x))) { float_raise(float_flag_invalid, fpst); return 0; - } else { - int old_exc_flags = get_float_exception_flags(fpst); - float64 ret; - - ret = float16_to_float64(f, true, fpst); - ret = float64_scalbn(ret, shift, fpst); - old_exc_flags |= get_float_exception_flags(fpst) - & float_flag_input_denormal; - set_float_exception_flags(old_exc_flags, fpst); - - return ret; } -} - -uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); + return float16_to_int16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) { - return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); } /* Set the current fp rounding mode and return the old one. diff --git a/target/arm/iwmmxt_helper.c b/target/arm/iwmmxt_helper.c index f6a4fc5..24244d0 100644 --- a/target/arm/iwmmxt_helper.c +++ b/target/arm/iwmmxt_helper.c @@ -27,30 +27,30 @@ /* iwMMXt macros extracted from GNU gdb. */ /* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ -#define SIMD8_SET( v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) -#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) -#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) -#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) +#define SIMD8_SET(v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) +#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) +#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) +#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) /* Flags to pass as "n" above. */ -#define SIMD_NBIT -1 -#define SIMD_ZBIT -2 -#define SIMD_CBIT -3 -#define SIMD_VBIT -4 +#define SIMD_NBIT -1 +#define SIMD_ZBIT -2 +#define SIMD_CBIT -3 +#define SIMD_VBIT -4 /* Various status bit macros. */ -#define NBIT8(x) ((x) & 0x80) -#define NBIT16(x) ((x) & 0x8000) -#define NBIT32(x) ((x) & 0x80000000) -#define NBIT64(x) ((x) & 0x8000000000000000ULL) -#define ZBIT8(x) (((x) & 0xff) == 0) -#define ZBIT16(x) (((x) & 0xffff) == 0) -#define ZBIT32(x) (((x) & 0xffffffff) == 0) -#define ZBIT64(x) (x == 0) +#define NBIT8(x) ((x) & 0x80) +#define NBIT16(x) ((x) & 0x8000) +#define NBIT32(x) ((x) & 0x80000000) +#define NBIT64(x) ((x) & 0x8000000000000000ULL) +#define ZBIT8(x) (((x) & 0xff) == 0) +#define ZBIT16(x) (((x) & 0xffff) == 0) +#define ZBIT32(x) (((x) & 0xffffffff) == 0) +#define ZBIT64(x) (x == 0) /* Sign extension macros. */ -#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) -#define EXTEND8(a) ((uint32_t) (int8_t) (a)) -#define EXTEND16(a) ((uint32_t) (int16_t) (a)) -#define EXTEND16S(a) ((int32_t) (int16_t) (a)) -#define EXTEND32(a) ((uint64_t) (int32_t) (a)) +#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) +#define EXTEND8(a) ((uint32_t) (int8_t) (a)) +#define EXTEND16(a) ((uint32_t) (int16_t) (a)) +#define EXTEND16S(a) ((int32_t) (int16_t) (a)) +#define EXTEND32(a) ((uint64_t) (int32_t) (a)) uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b) { @@ -159,141 +159,141 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) #define NZBIT64(x) \ SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ SIMD64_SET(ZBIT64(x), SIMD_ZBIT) -#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ +#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ - (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ - (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ - (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ + (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ + (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ + (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffff) << 0) | \ - (((b >> SH0) & 0xffff) << 16) | \ - (((a >> SH2) & 0xffff) << 32) | \ - (((b >> SH2) & 0xffff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ - NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ +{ \ + a = \ + (((a >> SH0) & 0xffff) << 0) | \ + (((b >> SH0) & 0xffff) << 16) | \ + (((a >> SH2) & 0xffff) << 32) | \ + (((b >> SH2) & 0xffff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ + NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffffffff) << 0) | \ - (((b >> SH0) & 0xffffffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = \ + (((a >> SH0) & 0xffffffff) << 0) | \ + (((b >> SH0) & 0xffffffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xff) << 0) | \ - (((x >> SH1) & 0xff) << 16) | \ - (((x >> SH2) & 0xff) << 32) | \ - (((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + (((x >> SH0) & 0xff) << 0) | \ + (((x >> SH1) & 0xff) << 16) | \ + (((x >> SH2) & 0xff) << 32) | \ + (((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xffff) << 0) | \ - (((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + (((x >> SH0) & 0xffff) << 0) | \ + (((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = (((x >> SH0) & 0xffffffff) << 0); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = (((x >> SH0) & 0xffffffff) << 0); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ - ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ - ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ - ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ + ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ + ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ + ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ - ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ + ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = EXTEND32((x >> SH0) & 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = EXTEND32((x >> SH0) & 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ } IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) -#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ +#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ - CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ - CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ - CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ + CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ + CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ + CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ - CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ +{ \ + a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ + CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ + NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tl, O, 0xffffffff) | \ - CMP(32, Tl, O, 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = CMP(0, Tl, O, 0xffffffff) | \ + CMP(32, Tl, O, 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ } #define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ diff --git a/target/arm/translate.c b/target/arm/translate.c index bcfc29c..c6a5d2a 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -1627,7 +1627,7 @@ static inline void gen_mov_vreg_F0(int dp, int reg) tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg)); } -#define ARM_CP_RW_BIT (1 << 20) +#define ARM_CP_RW_BIT (1 << 20) static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) { @@ -1861,12 +1861,12 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) wrd = insn & 0xf; rdlo = (insn >> 12) & 0xf; rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ + if (insn & ARM_CP_RW_BIT) { /* TMRRC */ iwmmxt_load_reg(cpu_V0, wrd); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ + } else { /* TMCRR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, wrd); gen_op_iwmmxt_set_mup(); @@ -1881,25 +1881,25 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } if (insn & ARM_CP_RW_BIT) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ + if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); iwmmxt_store_creg(wrd, tmp); } else { i = 1; if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WLDRD */ + if (insn & (1 << 22)) { /* WLDRD */ gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); i = 0; - } else { /* WLDRW wRd */ + } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); } } else { tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ + if (insn & (1 << 22)) { /* WLDRH */ gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ + } else { /* WLDRB */ gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } } @@ -1910,24 +1910,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); } } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ + if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WSTRD */ + if (insn & (1 << 22)) { /* WSTRD */ gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ + } else { /* WSTRW wRd */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } } else { - if (insn & (1 << 22)) { /* WSTRH */ + if (insn & (1 << 22)) { /* WSTRH */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ + } else { /* WSTRB */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } @@ -1943,7 +1943,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ + case 0x000: /* WOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1954,7 +1954,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x011: /* TMCR */ + case 0x011: /* TMCR */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -1985,7 +1985,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } break; - case 0x100: /* WXOR */ + case 0x100: /* WXOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1996,7 +1996,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x111: /* TMRC */ + case 0x111: /* TMRC */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -2004,7 +2004,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = iwmmxt_load_creg(wrd); store_reg(s, rd, tmp); break; - case 0x300: /* WANDN */ + case 0x300: /* WANDN */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2016,7 +2016,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x200: /* WAND */ + case 0x200: /* WAND */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2027,7 +2027,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x810: case 0xa10: /* WMADD */ + case 0x810: case 0xa10: /* WMADD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2039,7 +2039,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2061,7 +2061,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2083,7 +2083,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ + case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2097,7 +2097,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ + case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2116,7 +2116,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ + case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2132,7 +2132,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ + case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2154,7 +2154,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ + case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2174,7 +2174,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ + case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2187,7 +2187,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ + case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2218,7 +2218,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ + case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ rd = (insn >> 12) & 0xf; wrd = (insn >> 16) & 0xf; if (rd == 15 || ((insn >> 22) & 3) == 3) @@ -2251,7 +2251,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ + case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2270,7 +2270,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_set_nzcv(tmp); tcg_temp_free_i32(tmp); break; - case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ + case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2291,7 +2291,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ + case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2319,7 +2319,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); @@ -2339,7 +2339,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ + case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2367,7 +2367,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ + case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ rd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) @@ -2387,7 +2387,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ + case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ case 0x906: case 0xb06: case 0xd06: case 0xf06: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2419,7 +2419,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ + case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2450,7 +2450,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ + case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2481,7 +2481,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ + case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ case 0x214: case 0x614: case 0xa14: case 0xe14: if (((insn >> 22) & 3) == 0) return 1; @@ -2509,7 +2509,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ + case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ case 0x014: case 0x414: case 0x814: case 0xc14: if (((insn >> 22) & 3) == 0) return 1; @@ -2537,7 +2537,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ + case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ case 0x114: case 0x514: case 0x914: case 0xd14: if (((insn >> 22) & 3) == 0) return 1; @@ -2565,7 +2565,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ + case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ case 0x314: case 0x714: case 0xb14: case 0xf14: if (((insn >> 22) & 3) == 0) return 1; @@ -2601,7 +2601,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ + case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ case 0x916: case 0xb16: case 0xd16: case 0xf16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2632,7 +2632,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ + case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ case 0x816: case 0xa16: case 0xc16: case 0xe16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2663,7 +2663,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ + case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ case 0x402: case 0x502: case 0x602: case 0x702: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2676,7 +2676,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ + case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ case 0x41a: case 0x51a: case 0x61a: case 0x71a: case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: @@ -2719,7 +2719,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ + case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ case 0x41e: case 0x51e: case 0x61e: case 0x71e: case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: @@ -2733,7 +2733,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ + case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ case 0x418: case 0x518: case 0x618: case 0x718: case 0x818: case 0x918: case 0xa18: case 0xb18: case 0xc18: case 0xd18: case 0xe18: case 0xf18: @@ -2776,7 +2776,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ + case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ case 0x408: case 0x508: case 0x608: case 0x708: case 0x808: case 0x908: case 0xa08: case 0xb08: case 0xc08: case 0xd08: case 0xe08: case 0xf08: @@ -2823,13 +2823,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ + case 0x0: /* TMIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* TMIAPH */ + case 0x8: /* TMIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ + case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2872,16 +2872,16 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ + case 0x0: /* MIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* MIAPH */ + case 0x8: /* MIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ + case 0xc: /* MIABB */ + case 0xd: /* MIABT */ + case 0xe: /* MIATB */ + case 0xf: /* MIATT */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2907,13 +2907,13 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) if (acc != 0) return 1; - if (insn & ARM_CP_RW_BIT) { /* MRA */ + if (insn & ARM_CP_RW_BIT) { /* MRA */ iwmmxt_load_reg(cpu_V0, acc); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); - } else { /* MAR */ + } else { /* MAR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, acc); } |