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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJbf/e4AAoJEDwlJe0UNgze+PwP/jOwmu1UXHiFlegv3DgTCd3O ZHXxVZH1yqXY1DDXU4LwAv6dtnrxmd8w2Q352FlETdXeXYhG9MOlLIAq7VKseLMe oeTxDB4fGyd1sLjovxYVjpcELW2P7Oz0oXuCX/IgxycAu7nDZqUVdy5jyqdZdeEI YoIcpEjG8wigO2GN5ccbuwy2bo9OAvKGyD3qGvcENxc9JgmkE5rhfTWsuZhxlhKe ONU9kOPK0AnRGOfbr6S0fcXZYZcZ4vzc0h/seAkkrjwkkdfUAQ7lhZEBRfX+ENYL KgShmPHfu+IxTeAfdIlWz70FEM6gxxUxut/Bta0tl/OGqUAcHaKvGxUEoTrIqcLe XLTd5ae3imFerVlLrwIfJj2Lk3nVWDdArG2isDcIVAQ9uWhMY37x5IrRK55USgcw GSFuNUU3dQmKVqD3e0i6fCE/+ZAVaZU/2RNWiH8s5Y1e38DajxMgw4ed4KZ5lG/b lyR2q5xcCMpryaNqqMP3eIZhS00KP9n7CZblIj01//txcpMijXMWLXatjt71RUlk AnSv8yjcBh2s91OCt4sqD1zjYEECDw1xLEi2SGyVo5WOod4vGAoFqHbD4JF9anq0 TTsS5TBXsG2tFztKpyftqzffLCpHepN8VcptIRWcS22PYaMjT2ibvbV9ojEgQH8Y Wj9rqLgU0aqgIwVObrzu =4kLg -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
f4e8428b9a
10
MAINTAINERS
10
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>
|
||||
|
@ -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
|
||||
|
579
fpu/softfloat.c
579
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)) {
|
||||
return return_nan(a, s);
|
||||
}
|
||||
|
||||
switch (a.cls) {
|
||||
case float_class_qnan:
|
||||
case float_class_snan:
|
||||
return return_nan(a, s);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_INT(16, 16)
|
||||
FLOAT_TO_INT(16, 32)
|
||||
FLOAT_TO_INT(16, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_INT(32, 16)
|
||||
FLOAT_TO_INT(32, 32)
|
||||
FLOAT_TO_INT(32, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_INT(64, 16)
|
||||
FLOAT_TO_INT(64, 32)
|
||||
FLOAT_TO_INT(64, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
#undef FLOAT_TO_INT
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_UINT(16, 16)
|
||||
FLOAT_TO_UINT(16, 32)
|
||||
FLOAT_TO_UINT(16, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_UINT(32, 16)
|
||||
FLOAT_TO_UINT(32, 32)
|
||||
FLOAT_TO_UINT(32, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
FLOAT_TO_UINT(64, 16)
|
||||
FLOAT_TO_UINT(64, 32)
|
||||
FLOAT_TO_UINT(64, 64)
|
||||
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);
|
||||
}
|
||||
|
||||
#undef FLOAT_TO_UINT
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
uint64_t float64_to_uint64(float64 a, float_status *s)
|
||||
{
|
||||
return float64_to_uint64_scalbn(a, s->float_rounding_mode, 0, s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
if (a < 0) {
|
||||
f = -a;
|
||||
r.sign = true;
|
||||
} else {
|
||||
f = a;
|
||||
r.sign = false;
|
||||
}
|
||||
int shift = clz64(f) - 1;
|
||||
uint64_t f = a;
|
||||
int shift;
|
||||
|
||||
r.cls = float_class_normal;
|
||||
r.exp = (DECOMPOSED_BINARY_POINT - shift);
|
||||
r.frac = f << shift;
|
||||
if (a < 0) {
|
||||
f = -f;
|
||||
r.sign = true;
|
||||
}
|
||||
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_scalbn(int64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = int_to_float(a, status);
|
||||
return float16_round_pack_canonical(pa, 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_scalbn(int64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = int_to_float(a, status);
|
||||
return float32_round_pack_canonical(pa, 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_scalbn(int64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = int_to_float(a, status);
|
||||
return float64_round_pack_canonical(pa, 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_scalbn(uint64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = uint_to_float(a, status);
|
||||
return float16_round_pack_canonical(pa, 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_scalbn(uint64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = uint_to_float(a, status);
|
||||
return float32_round_pack_canonical(pa, 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_scalbn(uint64_t a, int scale, float_status *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)
|
||||
{
|
||||
FloatParts pa = uint_to_float(a, status);
|
||||
return float64_round_pack_canonical(pa, 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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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++) {
|
||||
|
114
hw/arm/iotkit.c
114
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;
|
||||
}
|
||||
|
||||
|
142
hw/arm/mps2-tz.c
142
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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig)
|
||||
{
|
||||
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;
|
||||
|
||||
/* TODO - Manage properly virtual resolution */
|
||||
|
||||
s->pitch = s->xres * (s->bpp >> 3);
|
||||
s->size = s->yres * s->pitch;
|
||||
|
||||
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()
|
||||
};
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
},
|
||||
};
|
||||
|
261
hw/misc/iotkit-sysctl.c
Normal file
261
hw/misc/iotkit-sysctl.c
Normal file
@ -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);
|
128
hw/misc/iotkit-sysinfo.c
Normal file
128
hw/misc/iotkit-sysinfo.c
Normal file
@ -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);
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
|
308
hw/misc/tz-msc.c
Normal file
308
hw/misc/tz-msc.c
Normal file
@ -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);
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
515
hw/timer/cmsdk-apb-dualtimer.c
Normal file
515
hw/timer/cmsdk-apb-dualtimer.c
Normal file
@ -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);
|
@ -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"
|
||||
|
@ -190,43 +190,14 @@ enum {
|
||||
/*----------------------------------------------------------------------------
|
||||
| Software IEC/IEEE integer-to-floating-point conversion routines.
|
||||
*----------------------------------------------------------------------------*/
|
||||
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 uint16_to_float32(uint16_t, float_status *status);
|
||||
float32 uint32_to_float32(uint32_t, float_status *status);
|
||||
float64 uint16_to_float64(uint16_t, float_status *status);
|
||||
float64 uint32_to_float64(uint32_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 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(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);
|
||||
int64_t float16_to_int64_round_to_zero(float16, float_status *status);
|
||||
uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *status);
|
||||
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);
|
||||
@ -234,6 +205,74 @@ 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
int32_t float16_to_int32(float16, float_status *status);
|
||||
int64_t float16_to_int64(float16, 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);
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| 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);
|
||||
int32_t float32_to_int32_round_to_zero(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);
|
||||
uint64_t float32_to_uint64_round_to_zero(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);
|
||||
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);
|
||||
|
||||
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);
|
||||
int32_t float64_to_int32_round_to_zero(float64, float_status *status);
|
||||
uint32_t float64_to_uint32(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);
|
||||
|
||||
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);
|
||||
uint64_t float64_to_uint64(float64 a, float_status *status);
|
||||
uint64_t float64_to_uint64_round_to_zero(float64 a, 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);
|
||||
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);
|
||||
|
@ -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;
|
||||
|
||||
UnimplementedDeviceState dualtimer;
|
||||
UnimplementedDeviceState s32ktimer;
|
||||
CMSDKAPBDualTimer dualtimer;
|
||||
|
||||
CMSDKAPBWatchdog s32kwatchdog;
|
||||
CMSDKAPBWatchdog nswatchdog;
|
||||
CMSDKAPBWatchdog swatchdog;
|
||||
|
||||
IoTKitSysCtl sysctl;
|
||||
IoTKitSysCtl sysinfo;
|
||||
|
||||
MemoryRegion container;
|
||||
MemoryRegion alias1;
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
49
include/hw/misc/iotkit-sysctl.h
Normal file
49
include/hw/misc/iotkit-sysctl.h
Normal file
@ -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
|
37
include/hw/misc/iotkit-sysinfo.h
Normal file
37
include/hw/misc/iotkit-sysinfo.h
Normal file
@ -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
|
@ -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
|
||||
|
79
include/hw/misc/tz-msc.h
Normal file
79
include/hw/misc/tz-msc.h
Normal file
@ -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
|
51
include/hw/ssi/pl022.h
Normal file
51
include/hw/ssi/pl022.h
Normal file
@ -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
|
72
include/hw/timer/cmsdk-apb-dualtimer.h
Normal file
72
include/hw/timer/cmsdk-apb-dualtimer.h
Normal file
@ -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
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst)
|
||||
{
|
||||
if (unlikely(float16_is_any_nan(f))) {
|
||||
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;
|
||||
}
|
||||
return uint64_to_float16_scalbn(x, -shift, fpst);
|
||||
}
|
||||
|
||||
uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst)
|
||||
{
|
||||
return float64_to_int16(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_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.
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user