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:
Peter Maydell 2018-08-24 13:29:07 +01:00
commit f4e8428b9a
41 changed files with 3405 additions and 718 deletions

View File

@ -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>

View File

@ -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

View File

@ -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;
}
}
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;
uint64_t f = a;
int shift;
r.cls = float_class_normal;
if (a < 0) {
f = -a;
f = -f;
r.sign = true;
} else {
f = a;
r.sign = false;
}
int shift = clz64(f) - 1;
r.cls = float_class_normal;
r.exp = (DECOMPOSED_BINARY_POINT - shift);
r.frac = f << shift;
shift = clz64(f) - 1;
scale = MIN(MAX(scale, -0x10000), 0x10000);
r.exp = DECOMPOSED_BINARY_POINT - shift + scale;
r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift);
}
return r;
}
float16 int64_to_float16_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 };
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 */

View File

@ -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);

View File

@ -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));
}
/*

View File

@ -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);
}
/*

View File

@ -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++) {

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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)

View File

@ -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[] = {

View File

@ -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;
}
void bcm2835_fb_validate_config(BCM2835FBConfig *config)
{
/*
* 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->config = *newconfig;
s->invalidate = true;
qemu_console_resize(s->con, s->config.xres, s->config.yres);
s->lock = false;
}
static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
{
uint32_t pitch;
uint32_t size;
BCM2835FBConfig newconf;
value &= ~0xf;
s->lock = true;
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->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);
newconf.base = s->vcram_base | (value & 0xc0000000);
newconf.base += BCM2835_FB_OFFSET;
s->base = s->vcram_base | (value & 0xc0000000);
s->base += BCM2835_FB_OFFSET;
bcm2835_fb_validate_config(&newconf);
/* TODO - Manage properly virtual resolution */
pitch = bcm2835_fb_get_pitch(&newconf);
size = bcm2835_fb_get_size(&newconf);
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
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);
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->invalidate = true;
qemu_console_resize(s->con, s->xres, s->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)
{
s->lock = true;
/* 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;
}
/* TODO - Manage properly virtual resolution */
s->pitch = s->xres * (s->bpp >> 3);
s->size = s->yres * s->pitch;
s->invalidate = true;
qemu_console_resize(s->con, s->xres, s->yres);
s->lock = false;
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()
};

View File

@ -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]);
}
}

View File

@ -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

View File

@ -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 = &alpha;
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 */

View File

@ -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
View 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
View 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);

View File

@ -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
}
};

View File

@ -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
View 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);

View File

@ -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 = {

View File

@ -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

View 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);

View File

@ -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"

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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];

View 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

View 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

View File

@ -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
View 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
View 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

View 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

View File

@ -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) \
#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) \
{ \
float_status *fpst = fpstp; \
int old_exc_flags = get_float_exception_flags(fpst); \
float##fsz tmp; \
if (float##fsz##_is_any_nan(x)) { \
if (unlikely(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); \
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.