aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-04-29 18:05:56 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-04-29 18:05:56 +0100
commit82b2865e0d0ea4c1001e9e7ed7920bcc0458f6de (patch)
tree33bea7bef0327838d25f33e638a2528c4a3faf5e
parent413a99a92c13ec408dcf2adaa87918dc81e890c8 (diff)
parent437cc27ddfded3bbab6afd5ac1761e0e195edba7 (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190429' into staging
target-arm queue: * remove "bag of random stuff" hw/devices.h header * implement FPU for Cortex-M and enable it for Cortex-M4 and -M33 * hw/dma: Compile the bcm2835_dma device as common object * configure: Remove --source-path option * hw/ssi/xilinx_spips: Avoid variable length array * hw/arm/smmuv3: Remove SMMUNotifierNode # gpg: Signature made Mon 29 Apr 2019 17:58:57 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190429: (42 commits) hw/devices: Move SMSC 91C111 declaration into a new header hw/net/lan9118: Export TYPE_LAN9118 and use it instead of hardcoded string hw/net/ne2000-isa: Add guards to the header hw/devices: Move LAN9118 declarations into a new header hw/devices: Move TI touchscreen declarations into a new header hw/devices: Move Gamepad declarations into a new header hw/devices: Move CBus declarations into a new header hw/devices: Move Blizzard declarations into a new header hw/devices: Move TC6393XB declarations into a new header hw/display/tc6393xb: Remove unused functions hw/arm/nseries: Use TYPE_TMP105 instead of hardcoded string hw/arm/aspeed: Use TYPE_TMP105/TYPE_PCA9552 instead of hardcoded string hw/dma: Compile the bcm2835_dma device as common object target/arm: Enable FPU for Cortex-M4 and Cortex-M33 target/arm: Implement VLLDM for v7M CPUs with an FPU target/arm: Implement VLSTM for v7M CPUs with an FPU target/arm: Implement M-profile lazy FP state preservation target/arm: Add lazy-FP-stacking support to v7m_stack_write() target/arm: New function armv7m_nvic_set_pending_lazyfp() target/arm: New helper function arm_v7m_mmu_idx_all() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS7
-rwxr-xr-xconfigure10
-rw-r--r--hw/arm/aspeed.c13
-rw-r--r--hw/arm/exynos4_boards.c3
-rw-r--r--hw/arm/gumstix.c2
-rw-r--r--hw/arm/integratorcp.c2
-rw-r--r--hw/arm/kzm.c2
-rw-r--r--hw/arm/mainstone.c2
-rw-r--r--hw/arm/mps2-tz.c3
-rw-r--r--hw/arm/mps2.c2
-rw-r--r--hw/arm/nseries.c7
-rw-r--r--hw/arm/palm.c2
-rw-r--r--hw/arm/realview.c3
-rw-r--r--hw/arm/smmu-common.c6
-rw-r--r--hw/arm/smmuv3.c28
-rw-r--r--hw/arm/stellaris.c2
-rw-r--r--hw/arm/tosa.c2
-rw-r--r--hw/arm/versatilepb.c2
-rw-r--r--hw/arm/vexpress.c2
-rw-r--r--hw/display/blizzard.c2
-rw-r--r--hw/display/tc6393xb.c18
-rw-r--r--hw/dma/Makefile.objs2
-rw-r--r--hw/input/stellaris_input.c2
-rw-r--r--hw/input/tsc2005.c2
-rw-r--r--hw/input/tsc210x.c4
-rw-r--r--hw/intc/armv7m_nvic.c261
-rw-r--r--hw/misc/cbus.c2
-rw-r--r--hw/net/lan9118.c3
-rw-r--r--hw/net/smc91c111.c2
-rw-r--r--hw/ssi/xilinx_spips.c6
-rw-r--r--include/hw/arm/omap.h6
-rw-r--r--include/hw/arm/smmu-common.h8
-rw-r--r--include/hw/devices.h62
-rw-r--r--include/hw/display/blizzard.h22
-rw-r--r--include/hw/display/tc6393xb.h24
-rw-r--r--include/hw/input/gamepad.h19
-rw-r--r--include/hw/input/tsc2xxx.h36
-rw-r--r--include/hw/misc/cbus.h32
-rw-r--r--include/hw/net/lan9118.h21
-rw-r--r--include/hw/net/ne2000-isa.h6
-rw-r--r--include/hw/net/smc91c111.h19
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--target/arm/cpu.c20
-rw-r--r--target/arm/cpu.h95
-rw-r--r--target/arm/helper.c873
-rw-r--r--target/arm/helper.h5
-rw-r--r--target/arm/machine.c16
-rw-r--r--target/arm/translate.c150
-rw-r--r--target/arm/translate.h3
-rw-r--r--target/arm/vfp_helper.c8
50 files changed, 1595 insertions, 235 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index dabbfccf9c..7dd71e0a2d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -663,10 +663,14 @@ M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/arm/nseries.c
+F: hw/display/blizzard.c
F: hw/input/lm832x.c
F: hw/input/tsc2005.c
F: hw/misc/cbus.c
F: hw/timer/twl92230.c
+F: include/hw/display/blizzard.h
+F: include/hw/input/tsc2xxx.h
+F: include/hw/misc/cbus.h
Palm
M: Andrzej Zaborowski <balrogg@gmail.com>
@@ -675,6 +679,7 @@ L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/arm/palm.c
F: hw/input/tsc210x.c
+F: include/hw/input/tsc2xxx.h
Raspberry Pi
M: Peter Maydell <peter.maydell@linaro.org>
@@ -714,6 +719,7 @@ F: hw/misc/mst_fpga.c
F: hw/misc/max111x.c
F: include/hw/arm/pxa.h
F: include/hw/arm/sharpsl.h
+F: include/hw/display/tc6393xb.h
SABRELITE / i.MX6
M: Peter Maydell <peter.maydell@linaro.org>
@@ -740,6 +746,7 @@ M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/stellaris*
+F: include/hw/input/gamepad.h
Versatile Express
M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/configure b/configure
index e1ad87b697..60719ddcc5 100755
--- a/configure
+++ b/configure
@@ -278,6 +278,8 @@ ld_has() {
# default parameters
source_path=$(dirname "$0")
+# make source path absolute
+source_path=$(cd "$source_path"; pwd)
cpu=""
iasl="iasl"
interp_prefix="/usr/gnemul/qemu-%M"
@@ -520,8 +522,6 @@ for opt do
;;
--cxx=*) CXX="$optarg"
;;
- --source-path=*) source_path="$optarg"
- ;;
--cpu=*) cpu="$optarg"
;;
--extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
@@ -600,9 +600,6 @@ if test "$debug_info" = "yes"; then
LDFLAGS="-g $LDFLAGS"
fi
-# make source path absolute
-source_path=$(cd "$source_path"; pwd)
-
# running configure in the source tree?
# we know that's the case if configure is there.
if test -f "./configure"; then
@@ -946,8 +943,6 @@ for opt do
;;
--interp-prefix=*) interp_prefix="$optarg"
;;
- --source-path=*)
- ;;
--cross-prefix=*)
;;
--cc=*)
@@ -1651,7 +1646,6 @@ $(echo Available targets: $default_target_list | \
--target-list-exclude=LIST exclude a set of targets from the default target-list
Advanced options (experts only):
- --source-path=PATH path of source code [$source_path]
--cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]
--cc=CC use C compiler CC [$cc]
--iasl=IASL use ACPI compiler IASL [$iasl]
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 996812498d..1c23ebd992 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -19,6 +19,8 @@
#include "hw/arm/aspeed_soc.h"
#include "hw/boards.h"
#include "hw/i2c/smbus_eeprom.h"
+#include "hw/misc/pca9552.h"
+#include "hw/misc/tmp105.h"
#include "qemu/log.h"
#include "sysemu/block-backend.h"
#include "hw/loader.h"
@@ -267,7 +269,8 @@ static void ast2500_evb_i2c_init(AspeedBoardState *bmc)
eeprom_buf);
/* The AST2500 EVB expects a LM75 but a TMP105 is compatible */
- i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d);
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7),
+ TYPE_TMP105, 0x4d);
/* The AST2500 EVB does not have an RTC. Let's pretend that one is
* plugged on the I2C bus header */
@@ -288,13 +291,15 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc)
AspeedSoCState *soc = &bmc->soc;
uint8_t *eeprom_buf = g_malloc0(8 * 1024);
- i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), "pca9552", 0x60);
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), TYPE_PCA9552,
+ 0x60);
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 4), "tmp423", 0x4c);
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 5), "tmp423", 0x4c);
/* The Witherspoon expects a TMP275 but a TMP105 is compatible */
- i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp105", 0x4a);
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), TYPE_TMP105,
+ 0x4a);
/* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is
* good enough */
@@ -302,7 +307,7 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc)
smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), 0x51,
eeprom_buf);
- i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "pca9552",
+ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), TYPE_PCA9552,
0x60);
}
diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c
index 750162cc95..ea8100f65a 100644
--- a/hw/arm/exynos4_boards.c
+++ b/hw/arm/exynos4_boards.c
@@ -32,6 +32,7 @@
#include "hw/arm/arm.h"
#include "exec/address-spaces.h"
#include "hw/arm/exynos4210.h"
+#include "hw/net/lan9118.h"
#include "hw/boards.h"
#undef DEBUG
@@ -92,7 +93,7 @@ static void lan9215_init(uint32_t base, qemu_irq irq)
/* This should be a 9215 but the 9118 is close enough */
if (nd_table[0].used) {
qemu_check_nic_model(&nd_table[0], "lan9118");
- dev = qdev_create(NULL, "lan9118");
+ dev = qdev_create(NULL, TYPE_LAN9118);
qdev_set_nic_properties(dev, &nd_table[0]);
qdev_prop_set_uint32(dev, "mode_16bit", 1);
qdev_init_nofail(dev);
diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c
index 79886ce378..343cbfd7da 100644
--- a/hw/arm/gumstix.c
+++ b/hw/arm/gumstix.c
@@ -40,7 +40,7 @@
#include "hw/arm/pxa.h"
#include "net/net.h"
#include "hw/block/flash.h"
-#include "hw/devices.h"
+#include "hw/net/smc91c111.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c
index 4eceebb9ea..0b6f24465e 100644
--- a/hw/arm/integratorcp.c
+++ b/hw/arm/integratorcp.c
@@ -12,10 +12,10 @@
#include "qemu-common.h"
#include "cpu.h"
#include "hw/sysbus.h"
-#include "hw/devices.h"
#include "hw/boards.h"
#include "hw/arm/arm.h"
#include "hw/misc/arm_integrator_debug.h"
+#include "hw/net/smc91c111.h"
#include "net/net.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c
index 864c7bd411..139934c4ec 100644
--- a/hw/arm/kzm.c
+++ b/hw/arm/kzm.c
@@ -22,7 +22,7 @@
#include "qemu/error-report.h"
#include "exec/address-spaces.h"
#include "net/net.h"
-#include "hw/devices.h"
+#include "hw/net/lan9118.h"
#include "hw/char/serial.h"
#include "sysemu/qtest.h"
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
index e96738ad26..c1cec59037 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -18,7 +18,7 @@
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
#include "net/net.h"
-#include "hw/devices.h"
+#include "hw/net/smc91c111.h"
#include "hw/boards.h"
#include "hw/block/flash.h"
#include "hw/sysbus.h"
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index f79f090a4a..7832408bb7 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -56,6 +56,7 @@
#include "hw/arm/armsse.h"
#include "hw/dma/pl080.h"
#include "hw/ssi/pl022.h"
+#include "hw/net/lan9118.h"
#include "net/net.h"
#include "hw/core/split-irq.h"
@@ -244,7 +245,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque,
* except that it doesn't support the checksum-offload feature.
*/
qemu_check_nic_model(nd, "lan9118");
- mms->lan9118 = qdev_create(NULL, "lan9118");
+ mms->lan9118 = qdev_create(NULL, TYPE_LAN9118);
qdev_set_nic_properties(mms->lan9118, nd);
qdev_init_nofail(mms->lan9118);
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
index e3d698ba6c..54b7395849 100644
--- a/hw/arm/mps2.c
+++ b/hw/arm/mps2.c
@@ -36,7 +36,7 @@
#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 "hw/net/lan9118.h"
#include "net/net.h"
typedef enum MPS2FPGAType {
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index 906b7ca22d..303f7a31e1 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -30,7 +30,10 @@
#include "ui/console.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
-#include "hw/devices.h"
+#include "hw/display/blizzard.h"
+#include "hw/input/tsc2xxx.h"
+#include "hw/misc/cbus.h"
+#include "hw/misc/tmp105.h"
#include "hw/block/flash.h"
#include "hw/hw.h"
#include "hw/bt.h"
@@ -218,7 +221,7 @@ static void n8x0_i2c_setup(struct n800_s *s)
qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier);
/* Attach a TMP105 PM chip (A0 wired to ground) */
- dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR);
+ dev = i2c_create_slave(i2c, TYPE_TMP105, N8X0_TMP105_ADDR);
qdev_connect_gpio_out(dev, 0, tmp_irq);
}
diff --git a/hw/arm/palm.c b/hw/arm/palm.c
index 285f43709d..139d27d1cc 100644
--- a/hw/arm/palm.c
+++ b/hw/arm/palm.c
@@ -26,7 +26,7 @@
#include "hw/arm/omap.h"
#include "hw/boards.h"
#include "hw/arm/arm.h"
-#include "hw/devices.h"
+#include "hw/input/tsc2xxx.h"
#include "hw/loader.h"
#include "exec/address-spaces.h"
#include "cpu.h"
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 242f5a87b6..05a244df25 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -14,7 +14,8 @@
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
-#include "hw/devices.h"
+#include "hw/net/lan9118.h"
+#include "hw/net/smc91c111.h"
#include "hw/pci/pci.h"
#include "net/net.h"
#include "sysemu/sysemu.h"
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index bbf4b8721a..e94be6db6c 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -412,10 +412,10 @@ inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr)
/* Unmap all notifiers of all mr's */
void smmu_inv_notifiers_all(SMMUState *s)
{
- SMMUNotifierNode *node;
+ SMMUDevice *sdev;
- QLIST_FOREACH(node, &s->notifiers_list, next) {
- smmu_inv_notifiers_mr(&node->sdev->iommu);
+ QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) {
+ smmu_inv_notifiers_mr(&sdev->iommu);
}
}
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 8c4e99fecc..fd8ec7860e 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -828,10 +828,10 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
/* invalidate an asid/iova tuple in all mr's */
static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
{
- SMMUNotifierNode *node;
+ SMMUDevice *sdev;
- QLIST_FOREACH(node, &s->notifiers_list, next) {
- IOMMUMemoryRegion *mr = &node->sdev->iommu;
+ QLIST_FOREACH(sdev, &s->devices_with_notifiers, next) {
+ IOMMUMemoryRegion *mr = &sdev->iommu;
IOMMUNotifier *n;
trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova);
@@ -1472,8 +1472,6 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu);
SMMUv3State *s3 = sdev->smmu;
SMMUState *s = &(s3->smmu_state);
- SMMUNotifierNode *node = NULL;
- SMMUNotifierNode *next_node = NULL;
if (new & IOMMU_NOTIFIER_MAP) {
int bus_num = pci_bus_num(sdev->bus);
@@ -1485,22 +1483,10 @@ static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
if (old == IOMMU_NOTIFIER_NONE) {
trace_smmuv3_notify_flag_add(iommu->parent_obj.name);
- node = g_malloc0(sizeof(*node));
- node->sdev = sdev;
- QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
- return;
- }
-
- /* update notifier node with new flags */
- QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
- if (node->sdev == sdev) {
- if (new == IOMMU_NOTIFIER_NONE) {
- trace_smmuv3_notify_flag_del(iommu->parent_obj.name);
- QLIST_REMOVE(node, next);
- g_free(node);
- }
- return;
- }
+ QLIST_INSERT_HEAD(&s->devices_with_notifiers, sdev, next);
+ } else if (new == IOMMU_NOTIFIER_NONE) {
+ trace_smmuv3_notify_flag_del(iommu->parent_obj.name);
+ QLIST_REMOVE(sdev, next);
}
}
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 05f86749f4..5059aedbaa 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -12,7 +12,6 @@
#include "hw/sysbus.h"
#include "hw/ssi/ssi.h"
#include "hw/arm/arm.h"
-#include "hw/devices.h"
#include "qemu/timer.h"
#include "hw/i2c/i2c.h"
#include "net/net.h"
@@ -22,6 +21,7 @@
#include "sysemu/sysemu.h"
#include "hw/arm/armv7m.h"
#include "hw/char/pl011.h"
+#include "hw/input/gamepad.h"
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "hw/misc/unimp.h"
#include "cpu.h"
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index eef9d427e7..9a1247797f 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -16,10 +16,10 @@
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/arm/arm.h"
-#include "hw/devices.h"
#include "hw/arm/sharpsl.h"
#include "hw/pcmcia.h"
#include "hw/boards.h"
+#include "hw/display/tc6393xb.h"
#include "hw/i2c/i2c.h"
#include "hw/ssi/ssi.h"
#include "hw/sysbus.h"
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index d67181810a..25166e1517 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -13,7 +13,7 @@
#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
-#include "hw/devices.h"
+#include "hw/net/smc91c111.h"
#include "net/net.h"
#include "sysemu/sysemu.h"
#include "hw/pci/pci.h"
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index f07134c424..d8634f3dd2 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -28,7 +28,7 @@
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
-#include "hw/devices.h"
+#include "hw/net/lan9118.h"
#include "hw/i2c/i2c.h"
#include "net/net.h"
#include "sysemu/sysemu.h"
diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c
index 291abe6fca..471bd0ed99 100644
--- a/hw/display/blizzard.c
+++ b/hw/display/blizzard.c
@@ -21,7 +21,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
-#include "hw/devices.h"
+#include "hw/display/blizzard.h"
#include "ui/pixel_ops.h"
typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index e1b1e302f2..0b7c59cde7 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -14,7 +14,7 @@
#include "qapi/error.h"
#include "qemu/host-utils.h"
#include "hw/hw.h"
-#include "hw/devices.h"
+#include "hw/display/tc6393xb.h"
#include "hw/block/flash.h"
#include "ui/console.h"
#include "ui/pixel_ops.h"
@@ -137,11 +137,6 @@ struct TC6393xbState {
blanked : 1;
};
-qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s)
-{
- return s->gpio_in;
-}
-
static void tc6393xb_gpio_set(void *opaque, int line, int level)
{
// TC6393xbState *s = opaque;
@@ -154,17 +149,6 @@ static void tc6393xb_gpio_set(void *opaque, int line, int level)
// FIXME: how does the chip reflect the GPIO input level change?
}
-void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
- qemu_irq handler)
-{
- if (line >= TC6393XB_GPIOS) {
- fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line);
- return;
- }
-
- s->handler[line] = handler;
-}
-
static void tc6393xb_gpio_handler_update(TC6393xbState *s)
{
uint32_t level, diff;
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index 79affecc39..8b39f9c600 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -14,4 +14,4 @@ common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
-obj-$(CONFIG_RASPI) += bcm2835_dma.o
+common-obj-$(CONFIG_RASPI) += bcm2835_dma.o
diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c
index 99168bfeef..20c87d86f4 100644
--- a/hw/input/stellaris_input.c
+++ b/hw/input/stellaris_input.c
@@ -8,7 +8,7 @@
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
-#include "hw/devices.h"
+#include "hw/input/gamepad.h"
#include "ui/console.h"
typedef struct {
diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c
index 2b9108a193..f82771e7a7 100644
--- a/hw/input/tsc2005.c
+++ b/hw/input/tsc2005.c
@@ -23,7 +23,7 @@
#include "hw/hw.h"
#include "qemu/timer.h"
#include "ui/console.h"
-#include "hw/devices.h"
+#include "hw/input/tsc2xxx.h"
#include "trace.h"
#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10)))
diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c
index 41731619bb..f94cb4683b 100644
--- a/hw/input/tsc210x.c
+++ b/hw/input/tsc210x.c
@@ -24,8 +24,8 @@
#include "audio/audio.h"
#include "qemu/timer.h"
#include "ui/console.h"
-#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */
-#include "hw/devices.h"
+#include "hw/arm/omap.h" /* For I2SCodec */
+#include "hw/input/tsc2xxx.h"
#define TSC_DATA_REGISTERS_PAGE 0x0
#define TSC_CONTROL_REGISTERS_PAGE 0x1
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index ab822f4251..fff6e694e6 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -655,6 +655,102 @@ void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure)
do_armv7m_nvic_set_pending(opaque, irq, secure, true);
}
+void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure)
+{
+ /*
+ * Pend an exception during lazy FP stacking. This differs
+ * from the usual exception pending because the logic for
+ * whether we should escalate depends on the saved context
+ * in the FPCCR register, not on the current state of the CPU/NVIC.
+ */
+ NVICState *s = (NVICState *)opaque;
+ bool banked = exc_is_banked(irq);
+ VecInfo *vec;
+ bool targets_secure;
+ bool escalate = false;
+ /*
+ * We will only look at bits in fpccr if this is a banked exception
+ * (in which case 'secure' tells us whether it is the S or NS version).
+ * All the bits for the non-banked exceptions are in fpccr_s.
+ */
+ uint32_t fpccr_s = s->cpu->env.v7m.fpccr[M_REG_S];
+ uint32_t fpccr = s->cpu->env.v7m.fpccr[secure];
+
+ assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
+ assert(!secure || banked);
+
+ vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq];
+
+ targets_secure = banked ? secure : exc_targets_secure(s, irq);
+
+ switch (irq) {
+ case ARMV7M_EXCP_DEBUG:
+ if (!(fpccr_s & R_V7M_FPCCR_MONRDY_MASK)) {
+ /* Ignore DebugMonitor exception */
+ return;
+ }
+ break;
+ case ARMV7M_EXCP_MEM:
+ escalate = !(fpccr & R_V7M_FPCCR_MMRDY_MASK);
+ break;
+ case ARMV7M_EXCP_USAGE:
+ escalate = !(fpccr & R_V7M_FPCCR_UFRDY_MASK);
+ break;
+ case ARMV7M_EXCP_BUS:
+ escalate = !(fpccr_s & R_V7M_FPCCR_BFRDY_MASK);
+ break;
+ case ARMV7M_EXCP_SECURE:
+ escalate = !(fpccr_s & R_V7M_FPCCR_SFRDY_MASK);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (escalate) {
+ /*
+ * Escalate to HardFault: faults that initially targeted Secure
+ * continue to do so, even if HF normally targets NonSecure.
+ */
+ irq = ARMV7M_EXCP_HARD;
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY) &&
+ (targets_secure ||
+ !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK))) {
+ vec = &s->sec_vectors[irq];
+ } else {
+ vec = &s->vectors[irq];
+ }
+ }
+
+ if (!vec->enabled ||
+ nvic_exec_prio(s) <= exc_group_prio(s, vec->prio, secure)) {
+ if (!(fpccr_s & R_V7M_FPCCR_HFRDY_MASK)) {
+ /*
+ * We want to escalate to HardFault but the context the
+ * FP state belongs to prevents the exception pre-empting.
+ */
+ cpu_abort(&s->cpu->parent_obj,
+ "Lockup: can't escalate to HardFault during "
+ "lazy FP register stacking\n");
+ }
+ }
+
+ if (escalate) {
+ s->cpu->env.v7m.hfsr |= R_V7M_HFSR_FORCED_MASK;
+ }
+ if (!vec->pending) {
+ vec->pending = 1;
+ /*
+ * We do not call nvic_irq_update(), because we know our caller
+ * is going to handle causing us to take the exception by
+ * raising EXCP_LAZYFP, so raising the IRQ line would be
+ * pointless extra work. We just need to recompute the
+ * priorities so that armv7m_nvic_can_take_pending_exception()
+ * returns the right answer.
+ */
+ nvic_recompute_state(s);
+ }
+}
+
/* Make pending IRQ active. */
void armv7m_nvic_acknowledge_irq(void *opaque)
{
@@ -746,6 +842,40 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure)
return ret;
}
+bool armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure)
+{
+ /*
+ * Return whether an exception is "ready", i.e. it is enabled and is
+ * configured at a priority which would allow it to interrupt the
+ * current execution priority.
+ *
+ * irq and secure have the same semantics as for armv7m_nvic_set_pending():
+ * for non-banked exceptions secure is always false; for banked exceptions
+ * it indicates which of the exceptions is required.
+ */
+ NVICState *s = (NVICState *)opaque;
+ bool banked = exc_is_banked(irq);
+ VecInfo *vec;
+ int running = nvic_exec_prio(s);
+
+ assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
+ assert(!secure || banked);
+
+ /*
+ * HardFault is an odd special case: we always check against -1,
+ * even if we're secure and HardFault has priority -3; we never
+ * need to check for enabled state.
+ */
+ if (irq == ARMV7M_EXCP_HARD) {
+ return running > -1;
+ }
+
+ vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq];
+
+ return vec->enabled &&
+ exc_group_prio(s, vec->prio, secure) < running;
+}
+
/* callback when external interrupt line is changed */
static void set_irq_level(void *opaque, int n, int level)
{
@@ -1077,6 +1207,16 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
}
case 0xd84: /* CSSELR */
return cpu->env.v7m.csselr[attrs.secure];
+ case 0xd88: /* CPACR */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ return 0;
+ }
+ return cpu->env.v7m.cpacr[attrs.secure];
+ case 0xd8c: /* NSACR */
+ if (!attrs.secure || !arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ return 0;
+ }
+ return cpu->env.v7m.nsacr;
/* TODO: Implement debug registers. */
case 0xd90: /* MPU_TYPE */
/* Unified MPU; if the MPU is not present this value is zero */
@@ -1222,6 +1362,49 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
return 0;
}
return cpu->env.v7m.sfar;
+ case 0xf34: /* FPCCR */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ return 0;
+ }
+ if (attrs.secure) {
+ return cpu->env.v7m.fpccr[M_REG_S];
+ } else {
+ /*
+ * NS can read LSPEN, CLRONRET and MONRDY. It can read
+ * BFRDY and HFRDY if AIRCR.BFHFNMINS != 0;
+ * other non-banked bits RAZ.
+ * TODO: MONRDY should RAZ/WI if DEMCR.SDME is set.
+ */
+ uint32_t value = cpu->env.v7m.fpccr[M_REG_S];
+ uint32_t mask = R_V7M_FPCCR_LSPEN_MASK |
+ R_V7M_FPCCR_CLRONRET_MASK |
+ R_V7M_FPCCR_MONRDY_MASK;
+
+ if (s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) {
+ mask |= R_V7M_FPCCR_BFRDY_MASK | R_V7M_FPCCR_HFRDY_MASK;
+ }
+
+ value &= mask;
+
+ value |= cpu->env.v7m.fpccr[M_REG_NS];
+ return value;
+ }
+ case 0xf38: /* FPCAR */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ return 0;
+ }
+ return cpu->env.v7m.fpcar[attrs.secure];
+ case 0xf3c: /* FPDSCR */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ return 0;
+ }
+ return cpu->env.v7m.fpdscr[attrs.secure];
+ case 0xf40: /* MVFR0 */
+ return cpu->isar.mvfr0;
+ case 0xf44: /* MVFR1 */
+ return cpu->isar.mvfr1;
+ case 0xf48: /* MVFR2 */
+ return cpu->isar.mvfr2;
default:
bad_offset:
qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
@@ -1469,6 +1652,18 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
cpu->env.v7m.csselr[attrs.secure] = value & R_V7M_CSSELR_INDEX_MASK;
}
break;
+ case 0xd88: /* CPACR */
+ if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ /* We implement only the Floating Point extension's CP10/CP11 */
+ cpu->env.v7m.cpacr[attrs.secure] = value & (0xf << 20);
+ }
+ break;
+ case 0xd8c: /* NSACR */
+ if (attrs.secure && arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ /* We implement only the Floating Point extension's CP10/CP11 */
+ cpu->env.v7m.nsacr = value & (3 << 10);
+ }
+ break;
case 0xd90: /* MPU_TYPE */
return; /* RO */
case 0xd94: /* MPU_CTRL */
@@ -1697,6 +1892,72 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
}
break;
}
+ case 0xf34: /* FPCCR */
+ if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ /* Not all bits here are banked. */
+ uint32_t fpccr_s;
+
+ if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ /* Don't allow setting of bits not present in v7M */
+ value &= (R_V7M_FPCCR_LSPACT_MASK |
+ R_V7M_FPCCR_USER_MASK |
+ R_V7M_FPCCR_THREAD_MASK |
+ R_V7M_FPCCR_HFRDY_MASK |
+ R_V7M_FPCCR_MMRDY_MASK |
+ R_V7M_FPCCR_BFRDY_MASK |
+ R_V7M_FPCCR_MONRDY_MASK |
+ R_V7M_FPCCR_LSPEN_MASK |
+ R_V7M_FPCCR_ASPEN_MASK);
+ }
+ value &= ~R_V7M_FPCCR_RES0_MASK;
+
+ if (!attrs.secure) {
+ /* Some non-banked bits are configurably writable by NS */
+ fpccr_s = cpu->env.v7m.fpccr[M_REG_S];
+ if (!(fpccr_s & R_V7M_FPCCR_LSPENS_MASK)) {
+ uint32_t lspen = FIELD_EX32(value, V7M_FPCCR, LSPEN);
+ fpccr_s = FIELD_DP32(fpccr_s, V7M_FPCCR, LSPEN, lspen);
+ }
+ if (!(fpccr_s & R_V7M_FPCCR_CLRONRETS_MASK)) {
+ uint32_t cor = FIELD_EX32(value, V7M_FPCCR, CLRONRET);
+ fpccr_s = FIELD_DP32(fpccr_s, V7M_FPCCR, CLRONRET, cor);
+ }
+ if ((s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
+ uint32_t hfrdy = FIELD_EX32(value, V7M_FPCCR, HFRDY);
+ uint32_t bfrdy = FIELD_EX32(value, V7M_FPCCR, BFRDY);
+ fpccr_s = FIELD_DP32(fpccr_s, V7M_FPCCR, HFRDY, hfrdy);
+ fpccr_s = FIELD_DP32(fpccr_s, V7M_FPCCR, BFRDY, bfrdy);
+ }
+ /* TODO MONRDY should RAZ/WI if DEMCR.SDME is set */
+ {
+ uint32_t monrdy = FIELD_EX32(value, V7M_FPCCR, MONRDY);
+ fpccr_s = FIELD_DP32(fpccr_s, V7M_FPCCR, MONRDY, monrdy);
+ }
+
+ /*
+ * All other non-banked bits are RAZ/WI from NS; write
+ * just the banked bits to fpccr[M_REG_NS].
+ */
+ value &= R_V7M_FPCCR_BANKED_MASK;
+ cpu->env.v7m.fpccr[M_REG_NS] = value;
+ } else {
+ fpccr_s = value;
+ }
+ cpu->env.v7m.fpccr[M_REG_S] = fpccr_s;
+ }
+ break;
+ case 0xf38: /* FPCAR */
+ if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ value &= ~7;
+ cpu->env.v7m.fpcar[attrs.secure] = value;
+ }
+ break;
+ case 0xf3c: /* FPDSCR */
+ if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) {
+ value &= 0x07c00000;
+ cpu->env.v7m.fpdscr[attrs.secure] = value;
+ }
+ break;
case 0xf50: /* ICIALLU */
case 0xf58: /* ICIMVAU */
case 0xf5c: /* DCIMVAC */
diff --git a/hw/misc/cbus.c b/hw/misc/cbus.c
index 25e337ea77..16ee704bca 100644
--- a/hw/misc/cbus.c
+++ b/hw/misc/cbus.c
@@ -23,7 +23,7 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/irq.h"
-#include "hw/devices.h"
+#include "hw/misc/cbus.h"
#include "sysemu/sysemu.h"
//#define DEBUG
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index a6269d9463..b29e3fee49 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -14,7 +14,7 @@
#include "hw/sysbus.h"
#include "net/net.h"
#include "net/eth.h"
-#include "hw/devices.h"
+#include "hw/net/lan9118.h"
#include "sysemu/sysemu.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
@@ -175,7 +175,6 @@ static const VMStateDescription vmstate_lan9118_packet = {
}
};
-#define TYPE_LAN9118 "lan9118"
#define LAN9118(obj) OBJECT_CHECK(lan9118_state, (obj), TYPE_LAN9118)
typedef struct {
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 99da2d9297..d19ea0750d 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -10,7 +10,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "net/net.h"
-#include "hw/devices.h"
+#include "hw/net/smc91c111.h"
#include "qemu/log.h"
/* For crc32 */
#include <zlib.h>
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 16f88f7402..c615058cc1 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -429,12 +429,14 @@ static void xlnx_zynqmp_qspips_reset(DeviceState *d)
static inline void stripe8(uint8_t *x, int num, bool dir)
{
- uint8_t r[num];
- memset(r, 0, sizeof(uint8_t) * num);
+ uint8_t r[MAX_NUM_BUSSES];
int idx[2] = {0, 0};
int bit[2] = {0, 7};
int d = dir;
+ assert(num <= MAX_NUM_BUSSES);
+ memset(r, 0, sizeof(uint8_t) * num);
+
for (idx[0] = 0; idx[0] < num; ++idx[0]) {
for (bit[0] = 7; bit[0] >= 0; bit[0]--) {
r[idx[!d]] |= x[idx[d]] & 1 << bit[d] ? 1 << bit[!d] : 0;
diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h
index e7fbd340f3..9de867daa4 100644
--- a/include/hw/arm/omap.h
+++ b/include/hw/arm/omap.h
@@ -20,6 +20,7 @@
#include "exec/memory.h"
# define hw_omap_h "omap.h"
#include "hw/irq.h"
+#include "hw/input/tsc2xxx.h"
#include "target/arm/cpu-qom.h"
#include "qemu/log.h"
@@ -679,11 +680,6 @@ qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler);
void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down);
-struct uWireSlave {
- uint16_t (*receive)(void *opaque);
- void (*send)(void *opaque, uint16_t data);
- void *opaque;
-};
struct omap_uwire_s;
void omap_uwire_attach(struct omap_uwire_s *s,
uWireSlave *slave, int chipselect);
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index b07cadd0ef..2c7fbf4202 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -80,13 +80,9 @@ typedef struct SMMUDevice {
AddressSpace as;
uint32_t cfg_cache_hits;
uint32_t cfg_cache_misses;
+ QLIST_ENTRY(SMMUDevice) next;
} SMMUDevice;
-typedef struct SMMUNotifierNode {
- SMMUDevice *sdev;
- QLIST_ENTRY(SMMUNotifierNode) next;
-} SMMUNotifierNode;
-
typedef struct SMMUPciBus {
PCIBus *bus;
SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */
@@ -108,7 +104,7 @@ typedef struct SMMUState {
GHashTable *iotlb;
SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX];
PCIBus *pci_bus;
- QLIST_HEAD(, SMMUNotifierNode) notifiers_list;
+ QLIST_HEAD(, SMMUDevice) devices_with_notifiers;
uint8_t bus_num;
PCIBus *primary_bus;
} SMMUState;
diff --git a/include/hw/devices.h b/include/hw/devices.h
deleted file mode 100644
index 1ed5be3296..0000000000
--- a/include/hw/devices.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef QEMU_DEVICES_H
-#define QEMU_DEVICES_H
-
-/* Devices that have nowhere better to go. */
-
-#include "hw/hw.h"
-#include "ui/console.h"
-
-/* smc91c111.c */
-void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
-
-/* lan9118.c */
-void lan9118_init(NICInfo *, uint32_t, qemu_irq);
-
-/* tsc210x.c */
-uWireSlave *tsc2102_init(qemu_irq pint);
-uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav);
-I2SCodec *tsc210x_codec(uWireSlave *chip);
-uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
-void tsc210x_set_transform(uWireSlave *chip,
- MouseTransformInfo *info);
-void tsc210x_key_event(uWireSlave *chip, int key, int down);
-
-/* tsc2005.c */
-void *tsc2005_init(qemu_irq pintdav);
-uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
-void tsc2005_set_transform(void *opaque, MouseTransformInfo *info);
-
-/* stellaris_input.c */
-void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
-
-/* blizzard.c */
-void *s1d13745_init(qemu_irq gpio_int);
-void s1d13745_write(void *opaque, int dc, uint16_t value);
-void s1d13745_write_block(void *opaque, int dc,
- void *buf, size_t len, int pitch);
-uint16_t s1d13745_read(void *opaque, int dc);
-
-/* cbus.c */
-typedef struct {
- qemu_irq clk;
- qemu_irq dat;
- qemu_irq sel;
-} CBus;
-CBus *cbus_init(qemu_irq dat_out);
-void cbus_attach(CBus *bus, void *slave_opaque);
-
-void *retu_init(qemu_irq irq, int vilma);
-void *tahvo_init(qemu_irq irq, int betty);
-
-void retu_key_event(void *retu, int state);
-
-/* tc6393xb.c */
-typedef struct TC6393xbState TC6393xbState;
-TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem,
- uint32_t base, qemu_irq irq);
-void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
- qemu_irq handler);
-qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s);
-qemu_irq tc6393xb_l3v_get(TC6393xbState *s);
-
-#endif
diff --git a/include/hw/display/blizzard.h b/include/hw/display/blizzard.h
new file mode 100644
index 0000000000..ef72bbc186
--- /dev/null
+++ b/include/hw/display/blizzard.h
@@ -0,0 +1,22 @@
+/*
+ * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_DISPLAY_BLIZZARD_H
+#define HW_DISPLAY_BLIZZARD_H
+
+#include "hw/irq.h"
+
+void *s1d13745_init(qemu_irq gpio_int);
+void s1d13745_write(void *opaque, int dc, uint16_t value);
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch);
+uint16_t s1d13745_read(void *opaque, int dc);
+
+#endif
diff --git a/include/hw/display/tc6393xb.h b/include/hw/display/tc6393xb.h
new file mode 100644
index 0000000000..5c4da91f80
--- /dev/null
+++ b/include/hw/display/tc6393xb.h
@@ -0,0 +1,24 @@
+/*
+ * Toshiba TC6393XB I/O Controller.
+ * Found in Sharp Zaurus SL-6000 (tosa) or some
+ * Toshiba e-Series PDAs.
+ *
+ * Copyright (c) 2007 Hervé Poussineau
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_DISPLAY_TC6393XB_H
+#define HW_DISPLAY_TC6393XB_H
+
+#include "exec/memory.h"
+#include "hw/irq.h"
+
+typedef struct TC6393xbState TC6393xbState;
+
+TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem,
+ uint32_t base, qemu_irq irq);
+qemu_irq tc6393xb_l3v_get(TC6393xbState *s);
+
+#endif
diff --git a/include/hw/input/gamepad.h b/include/hw/input/gamepad.h
new file mode 100644
index 0000000000..e20211baef
--- /dev/null
+++ b/include/hw/input/gamepad.h
@@ -0,0 +1,19 @@
+/*
+ * Gamepad style buttons connected to IRQ/GPIO lines
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_INPUT_GAMEPAD_H
+#define HW_INPUT_GAMEPAD_H
+
+#include "hw/irq.h"
+
+/* stellaris_input.c */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
+
+#endif
diff --git a/include/hw/input/tsc2xxx.h b/include/hw/input/tsc2xxx.h
new file mode 100644
index 0000000000..dbfe5c55c1
--- /dev/null
+++ b/include/hw/input/tsc2xxx.h
@@ -0,0 +1,36 @@
+/*
+ * TI touchscreen controller
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_INPUT_TSC2XXX_H
+#define HW_INPUT_TSC2XXX_H
+
+#include "hw/irq.h"
+#include "ui/console.h"
+
+typedef struct uWireSlave {
+ uint16_t (*receive)(void *opaque);
+ void (*send)(void *opaque, uint16_t data);
+ void *opaque;
+} uWireSlave;
+
+/* tsc210x.c */
+uWireSlave *tsc2102_init(qemu_irq pint);
+uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav);
+I2SCodec *tsc210x_codec(uWireSlave *chip);
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
+void tsc210x_set_transform(uWireSlave *chip, MouseTransformInfo *info);
+void tsc210x_key_event(uWireSlave *chip, int key, int down);
+
+/* tsc2005.c */
+void *tsc2005_init(qemu_irq pintdav);
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
+void tsc2005_set_transform(void *opaque, MouseTransformInfo *info);
+
+#endif
diff --git a/include/hw/misc/cbus.h b/include/hw/misc/cbus.h
new file mode 100644
index 0000000000..c899943e03
--- /dev/null
+++ b/include/hw/misc/cbus.h
@@ -0,0 +1,32 @@
+/*
+ * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma /
+ * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms.
+ * Based on reverse-engineering of a linux driver.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_MISC_CBUS_H
+#define HW_MISC_CBUS_H
+
+#include "hw/irq.h"
+
+typedef struct {
+ qemu_irq clk;
+ qemu_irq dat;
+ qemu_irq sel;
+} CBus;
+
+CBus *cbus_init(qemu_irq dat_out);
+void cbus_attach(CBus *bus, void *slave_opaque);
+
+void *retu_init(qemu_irq irq, int vilma);
+void *tahvo_init(qemu_irq irq, int betty);
+
+void retu_key_event(void *retu, int state);
+
+#endif
diff --git a/include/hw/net/lan9118.h b/include/hw/net/lan9118.h
new file mode 100644
index 0000000000..500acb4c14
--- /dev/null
+++ b/include/hw/net/lan9118.h
@@ -0,0 +1,21 @@
+/*
+ * SMSC LAN9118 Ethernet interface emulation
+ *
+ * Copyright (c) 2009 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_NET_LAN9118_H
+#define HW_NET_LAN9118_H
+
+#include "hw/irq.h"
+#include "net/net.h"
+
+#define TYPE_LAN9118 "lan9118"
+
+void lan9118_init(NICInfo *, uint32_t, qemu_irq);
+
+#endif
diff --git a/include/hw/net/ne2000-isa.h b/include/hw/net/ne2000-isa.h
index ff2bed9c95..527337c454 100644
--- a/include/hw/net/ne2000-isa.h
+++ b/include/hw/net/ne2000-isa.h
@@ -6,6 +6,10 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
+
+#ifndef HW_NET_NE2K_ISA_H
+#define HW_NET_NE2K_ISA_H
+
#include "hw/hw.h"
#include "hw/qdev.h"
#include "hw/isa/isa.h"
@@ -31,3 +35,5 @@ static inline ISADevice *isa_ne2000_init(ISABus *bus, int base, int irq,
}
return d;
}
+
+#endif
diff --git a/include/hw/net/smc91c111.h b/include/hw/net/smc91c111.h
new file mode 100644
index 0000000000..a66ba4112f
--- /dev/null
+++ b/include/hw/net/smc91c111.h
@@ -0,0 +1,19 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_NET_SMC91C111_H
+#define HW_NET_SMC91C111_H
+
+#include "hw/irq.h"
+#include "net/net.h"
+
+void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
+
+#endif
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index e4a0a656d1..fcdaae58c4 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -99,7 +99,6 @@ typedef struct RAMBlock RAMBlock;
typedef struct Range Range;
typedef struct SHPCDevice SHPCDevice;
typedef struct SSIBus SSIBus;
-typedef struct uWireSlave uWireSlave;
typedef struct VirtIODevice VirtIODevice;
typedef struct Visitor Visitor;
typedef void SaveStateHandler(QEMUFile *f, void *opaque);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index bb9fdc6304..a181fa8dc1 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -281,6 +281,11 @@ static void arm_cpu_reset(CPUState *s)
env->v7m.ccr[M_REG_S] |= R_V7M_CCR_UNALIGN_TRP_MASK;
}
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ env->v7m.fpccr[M_REG_NS] = R_V7M_FPCCR_ASPEN_MASK;
+ env->v7m.fpccr[M_REG_S] = R_V7M_FPCCR_ASPEN_MASK |
+ R_V7M_FPCCR_LSPEN_MASK | R_V7M_FPCCR_S_MASK;
+ }
/* Unlike A/R profile, M profile defines the reset LR value */
env->regs[14] = 0xffffffff;
@@ -1029,6 +1034,13 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_THUMB_DSP);
}
+ /*
+ * We rely on no XScale CPU having VFP so we can use the same bits in the
+ * TB flags field for VECSTRIDE and XSCALE_CPAR.
+ */
+ assert(!(arm_feature(env, ARM_FEATURE_VFP) &&
+ arm_feature(env, ARM_FEATURE_XSCALE)));
+
if (arm_feature(env, ARM_FEATURE_V7) &&
!arm_feature(env, ARM_FEATURE_M) &&
!arm_feature(env, ARM_FEATURE_PMSA)) {
@@ -1481,8 +1493,12 @@ static void cortex_m4_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_M);
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
+ set_feature(&cpu->env, ARM_FEATURE_VFP4);
cpu->midr = 0x410fc240; /* r0p0 */
cpu->pmsav7_dregion = 8;
+ cpu->isar.mvfr0 = 0x10110021;
+ cpu->isar.mvfr1 = 0x11000011;
+ cpu->isar.mvfr2 = 0x00000000;
cpu->id_pfr0 = 0x00000030;
cpu->id_pfr1 = 0x00000200;
cpu->id_dfr0 = 0x00100000;
@@ -1509,9 +1525,13 @@ static void cortex_m33_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_M_MAIN);
set_feature(&cpu->env, ARM_FEATURE_M_SECURITY);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
+ set_feature(&cpu->env, ARM_FEATURE_VFP4);
cpu->midr = 0x410fd213; /* r0p3 */
cpu->pmsav7_dregion = 16;
cpu->sau_sregion = 8;
+ cpu->isar.mvfr0 = 0x10110021;
+ cpu->isar.mvfr1 = 0x11000011;
+ cpu->isar.mvfr2 = 0x00000040;
cpu->id_pfr0 = 0x00000030;
cpu->id_pfr1 = 0x00000210;
cpu->id_dfr0 = 0x00200000;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index f7f2f5a99c..22bc6e00ab 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -57,6 +57,9 @@
#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
#define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */
#define EXCP_STKOF 19 /* v8M STKOF UsageFault */
+#define EXCP_LAZYFP 20 /* v7M fault during lazy FP stacking */
+#define EXCP_LSERR 21 /* v8M LSERR SecureFault */
+#define EXCP_UNALIGNED 22 /* v7M UNALIGNED UsageFault */
/* NB: add new EXCP_ defines to the array in arm_log_exception() too */
#define ARMV7M_EXCP_RESET 1
@@ -533,6 +536,11 @@ typedef struct CPUARMState {
uint32_t scr[M_REG_NUM_BANKS];
uint32_t msplim[M_REG_NUM_BANKS];
uint32_t psplim[M_REG_NUM_BANKS];
+ uint32_t fpcar[M_REG_NUM_BANKS];
+ uint32_t fpccr[M_REG_NUM_BANKS];
+ uint32_t fpdscr[M_REG_NUM_BANKS];
+ uint32_t cpacr[M_REG_NUM_BANKS];
+ uint32_t nsacr;
} v7m;
/* Information associated with an exception about to be taken:
@@ -1576,6 +1584,35 @@ FIELD(V7M_CSSELR, LEVEL, 1, 3)
*/
FIELD(V7M_CSSELR, INDEX, 0, 4)
+/* v7M FPCCR bits */
+FIELD(V7M_FPCCR, LSPACT, 0, 1)
+FIELD(V7M_FPCCR, USER, 1, 1)
+FIELD(V7M_FPCCR, S, 2, 1)
+FIELD(V7M_FPCCR, THREAD, 3, 1)
+FIELD(V7M_FPCCR, HFRDY, 4, 1)
+FIELD(V7M_FPCCR, MMRDY, 5, 1)
+FIELD(V7M_FPCCR, BFRDY, 6, 1)
+FIELD(V7M_FPCCR, SFRDY, 7, 1)
+FIELD(V7M_FPCCR, MONRDY, 8, 1)
+FIELD(V7M_FPCCR, SPLIMVIOL, 9, 1)
+FIELD(V7M_FPCCR, UFRDY, 10, 1)
+FIELD(V7M_FPCCR, RES0, 11, 15)
+FIELD(V7M_FPCCR, TS, 26, 1)
+FIELD(V7M_FPCCR, CLRONRETS, 27, 1)
+FIELD(V7M_FPCCR, CLRONRET, 28, 1)
+FIELD(V7M_FPCCR, LSPENS, 29, 1)
+FIELD(V7M_FPCCR, LSPEN, 30, 1)
+FIELD(V7M_FPCCR, ASPEN, 31, 1)
+/* These bits are banked. Others are non-banked and live in the M_REG_S bank */
+#define R_V7M_FPCCR_BANKED_MASK \
+ (R_V7M_FPCCR_LSPACT_MASK | \
+ R_V7M_FPCCR_USER_MASK | \
+ R_V7M_FPCCR_THREAD_MASK | \
+ R_V7M_FPCCR_MMRDY_MASK | \
+ R_V7M_FPCCR_SPLIMVIOL_MASK | \
+ R_V7M_FPCCR_UFRDY_MASK | \
+ R_V7M_FPCCR_ASPEN_MASK)
+
/*
* System register ID fields.
*/
@@ -1975,6 +2012,18 @@ void armv7m_nvic_set_pending(void *opaque, int irq, bool secure);
*/
void armv7m_nvic_set_pending_derived(void *opaque, int irq, bool secure);
/**
+ * armv7m_nvic_set_pending_lazyfp: mark this lazy FP exception as pending
+ * @opaque: the NVIC
+ * @irq: the exception number to mark pending
+ * @secure: false for non-banked exceptions or for the nonsecure
+ * version of a banked exception, true for the secure version of a banked
+ * exception.
+ *
+ * Similar to armv7m_nvic_set_pending(), but specifically for exceptions
+ * generated in the course of lazy stacking of FP registers.
+ */
+void armv7m_nvic_set_pending_lazyfp(void *opaque, int irq, bool secure);
+/**
* armv7m_nvic_get_pending_irq_info: return highest priority pending
* exception, and whether it targets Secure state
* @opaque: the NVIC
@@ -2011,6 +2060,20 @@ void armv7m_nvic_acknowledge_irq(void *opaque);
*/
int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure);
/**
+ * armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure)
+ * @opaque: the NVIC
+ * @irq: the exception number to mark pending
+ * @secure: false for non-banked exceptions or for the nonsecure
+ * version of a banked exception, true for the secure version of a banked
+ * exception.
+ *
+ * Return whether an exception is "ready", i.e. whether the exception is
+ * enabled and is configured at a priority which would allow it to
+ * interrupt the current execution priority. This controls whether the
+ * RDY bit for it in the FPCCR is set.
+ */
+bool armv7m_nvic_get_ready_status(void *opaque, int irq, bool secure);
+/**
* armv7m_nvic_raw_execution_priority: return the raw execution priority
* @opaque: the NVIC
*
@@ -2863,6 +2926,13 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx)
}
}
+/*
+ * Return the MMU index for a v7M CPU with all relevant information
+ * manually specified.
+ */
+ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env,
+ bool secstate, bool priv, bool negpri);
+
/* Return the MMU index for a v7M CPU in the specified security and
* privilege state.
*/
@@ -3090,18 +3160,27 @@ FIELD(TBFLAG_ANY, BE_DATA, 23, 1)
FIELD(TBFLAG_A32, THUMB, 0, 1)
FIELD(TBFLAG_A32, VECLEN, 1, 3)
FIELD(TBFLAG_A32, VECSTRIDE, 4, 2)
-FIELD(TBFLAG_A32, VFPEN, 7, 1)
-FIELD(TBFLAG_A32, CONDEXEC, 8, 8)
-FIELD(TBFLAG_A32, SCTLR_B, 16, 1)
-/* We store the bottom two bits of the CPAR as TB flags and handle
- * checks on the other bits at runtime
+/*
+ * We store the bottom two bits of the CPAR as TB flags and handle
+ * checks on the other bits at runtime. This shares the same bits as
+ * VECSTRIDE, which is OK as no XScale CPU has VFP.
*/
-FIELD(TBFLAG_A32, XSCALE_CPAR, 17, 2)
-/* Indicates whether cp register reads and writes by guest code should access
+FIELD(TBFLAG_A32, XSCALE_CPAR, 4, 2)
+/*
+ * Indicates whether cp register reads and writes by guest code should access
* the secure or nonsecure bank of banked registers; note that this is not
* the same thing as the current security state of the processor!
*/
-FIELD(TBFLAG_A32, NS, 19, 1)
+FIELD(TBFLAG_A32, NS, 6, 1)
+FIELD(TBFLAG_A32, VFPEN, 7, 1)
+FIELD(TBFLAG_A32, CONDEXEC, 8, 8)
+FIELD(TBFLAG_A32, SCTLR_B, 16, 1)
+/* For M profile only, set if FPCCR.LSPACT is set */
+FIELD(TBFLAG_A32, LSPACT, 18, 1)
+/* For M profile only, set if we must create a new FP context */
+FIELD(TBFLAG_A32, NEW_FP_CTXT_NEEDED, 19, 1)
+/* For M profile only, set if FPCCR.S does not match current security state */
+FIELD(TBFLAG_A32, FPCCR_S_WRONG, 20, 1)
/* For M profile only, Handler (ie not Thread) mode */
FIELD(TBFLAG_A32, HANDLER, 21, 1)
/* For M profile only, whether we should generate stack-limit checks */
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 57ef75b3fc..81a92ab491 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -7378,6 +7378,24 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
g_assert_not_reached();
}
+void HELPER(v7m_preserve_fp_state)(CPUARMState *env)
+{
+ /* translate.c should never generate calls here in user-only mode */
+ g_assert_not_reached();
+}
+
+void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr)
+{
+ /* translate.c should never generate calls here in user-only mode */
+ g_assert_not_reached();
+}
+
+void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr)
+{
+ /* translate.c should never generate calls here in user-only mode */
+ g_assert_not_reached();
+}
+
uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op)
{
/* The TT instructions can be used by unprivileged code, but in
@@ -7556,8 +7574,37 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
return target_el;
}
+/*
+ * Return true if the v7M CPACR permits access to the FPU for the specified
+ * security state and privilege level.
+ */
+static bool v7m_cpacr_pass(CPUARMState *env, bool is_secure, bool is_priv)
+{
+ switch (extract32(env->v7m.cpacr[is_secure], 20, 2)) {
+ case 0:
+ case 2: /* UNPREDICTABLE: we treat like 0 */
+ return false;
+ case 1:
+ return is_priv;
+ case 3:
+ return true;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/*
+ * What kind of stack write are we doing? This affects how exceptions
+ * generated during the stacking are treated.
+ */
+typedef enum StackingMode {
+ STACK_NORMAL,
+ STACK_IGNFAULTS,
+ STACK_LAZYFP,
+} StackingMode;
+
static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value,
- ARMMMUIdx mmu_idx, bool ignfault)
+ ARMMMUIdx mmu_idx, StackingMode mode)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
@@ -7575,15 +7622,31 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value,
&attrs, &prot, &page_size, &fi, NULL)) {
/* MPU/SAU lookup failed */
if (fi.type == ARMFault_QEMU_SFault) {
- qemu_log_mask(CPU_LOG_INT,
- "...SecureFault with SFSR.AUVIOL during stacking\n");
- env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
+ if (mode == STACK_LAZYFP) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault with SFSR.LSPERR "
+ "during lazy stacking\n");
+ env->v7m.sfsr |= R_V7M_SFSR_LSPERR_MASK;
+ } else {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault with SFSR.AUVIOL "
+ "during stacking\n");
+ env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK;
+ }
+ env->v7m.sfsr |= R_V7M_SFSR_SFARVALID_MASK;
env->v7m.sfar = addr;
exc = ARMV7M_EXCP_SECURE;
exc_secure = false;
} else {
- qemu_log_mask(CPU_LOG_INT, "...MemManageFault with CFSR.MSTKERR\n");
- env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK;
+ if (mode == STACK_LAZYFP) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...MemManageFault with CFSR.MLSPERR\n");
+ env->v7m.cfsr[secure] |= R_V7M_CFSR_MLSPERR_MASK;
+ } else {
+ qemu_log_mask(CPU_LOG_INT,
+ "...MemManageFault with CFSR.MSTKERR\n");
+ env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK;
+ }
exc = ARMV7M_EXCP_MEM;
exc_secure = secure;
}
@@ -7593,8 +7656,13 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value,
attrs, &txres);
if (txres != MEMTX_OK) {
/* BusFault trying to write the data */
- qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n");
- env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK;
+ if (mode == STACK_LAZYFP) {
+ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.LSPERR\n");
+ env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_LSPERR_MASK;
+ } else {
+ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n");
+ env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK;
+ }
exc = ARMV7M_EXCP_BUS;
exc_secure = false;
goto pend_fault;
@@ -7609,11 +7677,19 @@ pend_fault:
* later if we have two derived exceptions.
* The only case when we must not pend the exception but instead
* throw it away is if we are doing the push of the callee registers
- * and we've already generated a derived exception. Even in this
- * case we will still update the fault status registers.
+ * and we've already generated a derived exception (this is indicated
+ * by the caller passing STACK_IGNFAULTS). Even in this case we will
+ * still update the fault status registers.
*/
- if (!ignfault) {
+ switch (mode) {
+ case STACK_NORMAL:
armv7m_nvic_set_pending_derived(env->nvic, exc, exc_secure);
+ break;
+ case STACK_LAZYFP:
+ armv7m_nvic_set_pending_lazyfp(env->nvic, exc, exc_secure);
+ break;
+ case STACK_IGNFAULTS:
+ break;
}
return false;
}
@@ -7679,6 +7755,97 @@ pend_fault:
return false;
}
+void HELPER(v7m_preserve_fp_state)(CPUARMState *env)
+{
+ /*
+ * Preserve FP state (because LSPACT was set and we are about
+ * to execute an FP instruction). This corresponds to the
+ * PreserveFPState() pseudocode.
+ * We may throw an exception if the stacking fails.
+ */
+ ARMCPU *cpu = arm_env_get_cpu(env);
+ bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
+ bool negpri = !(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_HFRDY_MASK);
+ bool is_priv = !(env->v7m.fpccr[is_secure] & R_V7M_FPCCR_USER_MASK);
+ bool splimviol = env->v7m.fpccr[is_secure] & R_V7M_FPCCR_SPLIMVIOL_MASK;
+ uint32_t fpcar = env->v7m.fpcar[is_secure];
+ bool stacked_ok = true;
+ bool ts = is_secure && (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK);
+ bool take_exception;
+
+ /* Take the iothread lock as we are going to touch the NVIC */
+ qemu_mutex_lock_iothread();
+
+ /* Check the background context had access to the FPU */
+ if (!v7m_cpacr_pass(env, is_secure, is_priv)) {
+ armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, is_secure);
+ env->v7m.cfsr[is_secure] |= R_V7M_CFSR_NOCP_MASK;
+ stacked_ok = false;
+ } else if (!is_secure && !extract32(env->v7m.nsacr, 10, 1)) {
+ armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S);
+ env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK;
+ stacked_ok = false;
+ }
+
+ if (!splimviol && stacked_ok) {
+ /* We only stack if the stack limit wasn't violated */
+ int i;
+ ARMMMUIdx mmu_idx;
+
+ mmu_idx = arm_v7m_mmu_idx_all(env, is_secure, is_priv, negpri);
+ for (i = 0; i < (ts ? 32 : 16); i += 2) {
+ uint64_t dn = *aa32_vfp_dreg(env, i / 2);
+ uint32_t faddr = fpcar + 4 * i;
+ uint32_t slo = extract64(dn, 0, 32);
+ uint32_t shi = extract64(dn, 32, 32);
+
+ if (i >= 16) {
+ faddr += 8; /* skip the slot for the FPSCR */
+ }
+ stacked_ok = stacked_ok &&
+ v7m_stack_write(cpu, faddr, slo, mmu_idx, STACK_LAZYFP) &&
+ v7m_stack_write(cpu, faddr + 4, shi, mmu_idx, STACK_LAZYFP);
+ }
+
+ stacked_ok = stacked_ok &&
+ v7m_stack_write(cpu, fpcar + 0x40,
+ vfp_get_fpscr(env), mmu_idx, STACK_LAZYFP);
+ }
+
+ /*
+ * We definitely pended an exception, but it's possible that it
+ * might not be able to be taken now. If its priority permits us
+ * to take it now, then we must not update the LSPACT or FP regs,
+ * but instead jump out to take the exception immediately.
+ * If it's just pending and won't be taken until the current
+ * handler exits, then we do update LSPACT and the FP regs.
+ */
+ take_exception = !stacked_ok &&
+ armv7m_nvic_can_take_pending_exception(env->nvic);
+
+ qemu_mutex_unlock_iothread();
+
+ if (take_exception) {
+ raise_exception_ra(env, EXCP_LAZYFP, 0, 1, GETPC());
+ }
+
+ env->v7m.fpccr[is_secure] &= ~R_V7M_FPCCR_LSPACT_MASK;
+
+ if (ts) {
+ /* Clear s0 to s31 and the FPSCR */
+ int i;
+
+ for (i = 0; i < 32; i += 2) {
+ *aa32_vfp_dreg(env, i / 2) = 0;
+ }
+ vfp_set_fpscr(env, 0);
+ }
+ /*
+ * Otherwise s0 to s15 and FPSCR are UNKNOWN; we choose to leave them
+ * unchanged.
+ */
+}
+
/* Write to v7M CONTROL.SPSEL bit for the specified security bank.
* This may change the current stack pointer between Main and Process
* stack pointers if it is done for the CONTROL register for the current
@@ -7800,6 +7967,9 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
/* translate.c should have made BXNS UNDEF unless we're secure */
assert(env->v7m.secure);
+ if (!(dest & 1)) {
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
+ }
switch_v7m_security_state(env, dest & 1);
env->thumb = 1;
env->regs[15] = dest & ~1;
@@ -7857,6 +8027,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
*/
write_v7m_exception(env, 1);
}
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
switch_v7m_security_state(env, 0);
env->thumb = 1;
env->regs[15] = dest;
@@ -7956,6 +8127,21 @@ load_fail:
return false;
}
+static uint32_t v7m_integrity_sig(CPUARMState *env, uint32_t lr)
+{
+ /*
+ * Return the integrity signature value for the callee-saves
+ * stack frame section. @lr is the exception return payload/LR value
+ * whose FType bit forms bit 0 of the signature if FP is present.
+ */
+ uint32_t sig = 0xfefa125a;
+
+ if (!arm_feature(env, ARM_FEATURE_VFP) || (lr & R_V7M_EXCRET_FTYPE_MASK)) {
+ sig |= 1;
+ }
+ return sig;
+}
+
static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
bool ignore_faults)
{
@@ -7970,6 +8156,8 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
bool stacked_ok;
uint32_t limit;
bool want_psp;
+ uint32_t sig;
+ StackingMode smode = ignore_faults ? STACK_IGNFAULTS : STACK_NORMAL;
if (dotailchain) {
bool mode = lr & R_V7M_EXCRET_MODE_MASK;
@@ -8011,24 +8199,17 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
/* Write as much of the stack frame as we can. A write failure may
* cause us to pend a derived exception.
*/
+ sig = v7m_integrity_sig(env, lr);
stacked_ok =
- v7m_stack_write(cpu, frameptr, 0xfefa125b, mmu_idx, ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx,
- ignore_faults) &&
- v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx,
- ignore_faults);
+ v7m_stack_write(cpu, frameptr, sig, mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx, smode) &&
+ v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx, smode);
/* Update SP regardless of whether any of the stack accesses failed. */
*frame_sp_p = frameptr;
@@ -8053,6 +8234,14 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
qemu_log_mask(CPU_LOG_INT, "...taking pending %s exception %d\n",
targets_secure ? "secure" : "nonsecure", exc);
+ if (dotailchain) {
+ /* Sanitize LR FType and PREFIX bits */
+ if (!arm_feature(env, ARM_FEATURE_VFP)) {
+ lr |= R_V7M_EXCRET_FTYPE_MASK;
+ }
+ lr = deposit32(lr, 24, 8, 0xff);
+ }
+
if (arm_feature(env, ARM_FEATURE_V8)) {
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
(lr & R_V7M_EXCRET_S_MASK)) {
@@ -8148,6 +8337,9 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
switch_v7m_security_state(env, targets_secure);
write_v7m_control_spsel(env, 0);
arm_clear_exclusive(env);
+ /* Clear SFPA and FPCA (has no effect if no FPU) */
+ env->v7m.control[M_REG_S] &=
+ ~(R_V7M_CONTROL_FPCA_MASK | R_V7M_CONTROL_SFPA_MASK);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
@@ -8155,6 +8347,187 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
env->thumb = addr & 1;
}
+static void v7m_update_fpccr(CPUARMState *env, uint32_t frameptr,
+ bool apply_splim)
+{
+ /*
+ * Like the pseudocode UpdateFPCCR: save state in FPCAR and FPCCR
+ * that we will need later in order to do lazy FP reg stacking.
+ */
+ bool is_secure = env->v7m.secure;
+ void *nvic = env->nvic;
+ /*
+ * Some bits are unbanked and live always in fpccr[M_REG_S]; some bits
+ * are banked and we want to update the bit in the bank for the
+ * current security state; and in one case we want to specifically
+ * update the NS banked version of a bit even if we are secure.
+ */
+ uint32_t *fpccr_s = &env->v7m.fpccr[M_REG_S];
+ uint32_t *fpccr_ns = &env->v7m.fpccr[M_REG_NS];
+ uint32_t *fpccr = &env->v7m.fpccr[is_secure];
+ bool hfrdy, bfrdy, mmrdy, ns_ufrdy, s_ufrdy, sfrdy, monrdy;
+
+ env->v7m.fpcar[is_secure] = frameptr & ~0x7;
+
+ if (apply_splim && arm_feature(env, ARM_FEATURE_V8)) {
+ bool splimviol;
+ uint32_t splim = v7m_sp_limit(env);
+ bool ign = armv7m_nvic_neg_prio_requested(nvic, is_secure) &&
+ (env->v7m.ccr[is_secure] & R_V7M_CCR_STKOFHFNMIGN_MASK);
+
+ splimviol = !ign && frameptr < splim;
+ *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, SPLIMVIOL, splimviol);
+ }
+
+ *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, LSPACT, 1);
+
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, S, is_secure);
+
+ *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, USER, arm_current_el(env) == 0);
+
+ *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, THREAD,
+ !arm_v7m_is_handler_mode(env));
+
+ hfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_HARD, false);
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, HFRDY, hfrdy);
+
+ bfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_BUS, false);
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, BFRDY, bfrdy);
+
+ mmrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_MEM, is_secure);
+ *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, MMRDY, mmrdy);
+
+ ns_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, false);
+ *fpccr_ns = FIELD_DP32(*fpccr_ns, V7M_FPCCR, UFRDY, ns_ufrdy);
+
+ monrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_DEBUG, false);
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, MONRDY, monrdy);
+
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ s_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, true);
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, UFRDY, s_ufrdy);
+
+ sfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_SECURE, false);
+ *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, SFRDY, sfrdy);
+ }
+}
+
+void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr)
+{
+ /* fptr is the value of Rn, the frame pointer we store the FP regs to */
+ bool s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
+ bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK;
+
+ assert(env->v7m.secure);
+
+ if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) {
+ return;
+ }
+
+ /* Check access to the coprocessor is permitted */
+ if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) {
+ raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC());
+ }
+
+ if (lspact) {
+ /* LSPACT should not be active when there is active FP state */
+ raise_exception_ra(env, EXCP_LSERR, 0, 1, GETPC());
+ }
+
+ if (fptr & 7) {
+ raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC());
+ }
+
+ /*
+ * Note that we do not use v7m_stack_write() here, because the
+ * accesses should not set the FSR bits for stacking errors if they
+ * fail. (In pseudocode terms, they are AccType_NORMAL, not AccType_STACK
+ * or AccType_LAZYFP). Faults in cpu_stl_data() will throw exceptions
+ * and longjmp out.
+ */
+ if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) {
+ bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK;
+ int i;
+
+ for (i = 0; i < (ts ? 32 : 16); i += 2) {
+ uint64_t dn = *aa32_vfp_dreg(env, i / 2);
+ uint32_t faddr = fptr + 4 * i;
+ uint32_t slo = extract64(dn, 0, 32);
+ uint32_t shi = extract64(dn, 32, 32);
+
+ if (i >= 16) {
+ faddr += 8; /* skip the slot for the FPSCR */
+ }
+ cpu_stl_data(env, faddr, slo);
+ cpu_stl_data(env, faddr + 4, shi);
+ }
+ cpu_stl_data(env, fptr + 0x40, vfp_get_fpscr(env));
+
+ /*
+ * If TS is 0 then s0 to s15 and FPSCR are UNKNOWN; we choose to
+ * leave them unchanged, matching our choice in v7m_preserve_fp_state.
+ */
+ if (ts) {
+ for (i = 0; i < 32; i += 2) {
+ *aa32_vfp_dreg(env, i / 2) = 0;
+ }
+ vfp_set_fpscr(env, 0);
+ }
+ } else {
+ v7m_update_fpccr(env, fptr, false);
+ }
+
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK;
+}
+
+void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr)
+{
+ /* fptr is the value of Rn, the frame pointer we load the FP regs from */
+ assert(env->v7m.secure);
+
+ if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) {
+ return;
+ }
+
+ /* Check access to the coprocessor is permitted */
+ if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) {
+ raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC());
+ }
+
+ if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) {
+ /* State in FP is still valid */
+ env->v7m.fpccr[M_REG_S] &= ~R_V7M_FPCCR_LSPACT_MASK;
+ } else {
+ bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK;
+ int i;
+ uint32_t fpscr;
+
+ if (fptr & 7) {
+ raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC());
+ }
+
+ for (i = 0; i < (ts ? 32 : 16); i += 2) {
+ uint32_t slo, shi;
+ uint64_t dn;
+ uint32_t faddr = fptr + 4 * i;
+
+ if (i >= 16) {
+ faddr += 8; /* skip the slot for the FPSCR */
+ }
+
+ slo = cpu_ldl_data(env, faddr);
+ shi = cpu_ldl_data(env, faddr + 4);
+
+ dn = (uint64_t) shi << 32 | slo;
+ *aa32_vfp_dreg(env, i / 2) = dn;
+ }
+ fpscr = cpu_ldl_data(env, fptr + 0x40);
+ vfp_set_fpscr(env, fpscr);
+ }
+
+ env->v7m.control[M_REG_S] |= R_V7M_CONTROL_FPCA_MASK;
+}
+
static bool v7m_push_stack(ARMCPU *cpu)
{
/* Do the "set up stack frame" part of exception entry,
@@ -8163,11 +8536,25 @@ static bool v7m_push_stack(ARMCPU *cpu)
* should ignore further stack faults trying to process
* that derived exception.)
*/
- bool stacked_ok;
+ bool stacked_ok = true, limitviol = false;
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
uint32_t frameptr = env->regs[13];
ARMMMUIdx mmu_idx = arm_mmu_idx(env);
+ uint32_t framesize;
+ bool nsacr_cp10 = extract32(env->v7m.nsacr, 10, 1);
+
+ if ((env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) &&
+ (env->v7m.secure || nsacr_cp10)) {
+ if (env->v7m.secure &&
+ env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK) {
+ framesize = 0xa8;
+ } else {
+ framesize = 0x68;
+ }
+ } else {
+ framesize = 0x20;
+ }
/* Align stack pointer if the guest wants that */
if ((frameptr & 4) &&
@@ -8176,7 +8563,13 @@ static bool v7m_push_stack(ARMCPU *cpu)
xpsr |= XPSR_SPREALIGN;
}
- frameptr -= 0x20;
+ xpsr &= ~XPSR_SFPA;
+ if (env->v7m.secure &&
+ (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) {
+ xpsr |= XPSR_SFPA;
+ }
+
+ frameptr -= framesize;
if (arm_feature(env, ARM_FEATURE_V8)) {
uint32_t limit = v7m_sp_limit(env);
@@ -8194,7 +8587,14 @@ static bool v7m_push_stack(ARMCPU *cpu)
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
env->regs[13] = limit;
- return true;
+ /*
+ * We won't try to perform any further memory accesses but
+ * we must continue through the following code to check for
+ * permission faults during FPU state preservation, and we
+ * must update FPCCR if lazy stacking is enabled.
+ */
+ limitviol = true;
+ stacked_ok = false;
}
}
@@ -8203,18 +8603,99 @@ static bool v7m_push_stack(ARMCPU *cpu)
* (which may be taken in preference to the one we started with
* if it has higher priority).
*/
- stacked_ok =
- v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 4, env->regs[1], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 8, env->regs[2], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 12, env->regs[3], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 16, env->regs[12], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 20, env->regs[14], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) &&
- v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false);
+ stacked_ok = stacked_ok &&
+ v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 4, env->regs[1],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 8, env->regs[2],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 12, env->regs[3],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 16, env->regs[12],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 20, env->regs[14],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 24, env->regs[15],
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, STACK_NORMAL);
+
+ if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) {
+ /* FPU is active, try to save its registers */
+ bool fpccr_s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
+ bool lspact = env->v7m.fpccr[fpccr_s] & R_V7M_FPCCR_LSPACT_MASK;
+
+ if (lspact && arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault because LSPACT and FPCA both set\n");
+ env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
+ } else if (!env->v7m.secure && !nsacr_cp10) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...Secure UsageFault with CFSR.NOCP because "
+ "NSACR.CP10 prevents stacking FP regs\n");
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S);
+ env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK;
+ } else {
+ if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) {
+ /* Lazy stacking disabled, save registers now */
+ int i;
+ bool cpacr_pass = v7m_cpacr_pass(env, env->v7m.secure,
+ arm_current_el(env) != 0);
- /* Update SP regardless of whether any of the stack accesses failed. */
- env->regs[13] = frameptr;
+ if (stacked_ok && !cpacr_pass) {
+ /*
+ * Take UsageFault if CPACR forbids access. The pseudocode
+ * here does a full CheckCPEnabled() but we know the NSACR
+ * check can never fail as we have already handled that.
+ */
+ qemu_log_mask(CPU_LOG_INT,
+ "...UsageFault with CFSR.NOCP because "
+ "CPACR.CP10 prevents stacking FP regs\n");
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
+ env->v7m.secure);
+ env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK;
+ stacked_ok = false;
+ }
+
+ for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) {
+ uint64_t dn = *aa32_vfp_dreg(env, i / 2);
+ uint32_t faddr = frameptr + 0x20 + 4 * i;
+ uint32_t slo = extract64(dn, 0, 32);
+ uint32_t shi = extract64(dn, 32, 32);
+
+ if (i >= 16) {
+ faddr += 8; /* skip the slot for the FPSCR */
+ }
+ stacked_ok = stacked_ok &&
+ v7m_stack_write(cpu, faddr, slo,
+ mmu_idx, STACK_NORMAL) &&
+ v7m_stack_write(cpu, faddr + 4, shi,
+ mmu_idx, STACK_NORMAL);
+ }
+ stacked_ok = stacked_ok &&
+ v7m_stack_write(cpu, frameptr + 0x60,
+ vfp_get_fpscr(env), mmu_idx, STACK_NORMAL);
+ if (cpacr_pass) {
+ for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) {
+ *aa32_vfp_dreg(env, i / 2) = 0;
+ }
+ vfp_set_fpscr(env, 0);
+ }
+ } else {
+ /* Lazy stacking enabled, save necessary info to stack later */
+ v7m_update_fpccr(env, frameptr + 0x20, true);
+ }
+ }
+ }
+
+ /*
+ * If we broke a stack limit then SP was already updated earlier;
+ * otherwise we update SP regardless of whether any of the stack
+ * accesses failed or we took some other kind of fault.
+ */
+ if (!limitviol) {
+ env->regs[13] = frameptr;
+ }
return !stacked_ok;
}
@@ -8231,6 +8712,8 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
bool rettobase = false;
bool exc_secure = false;
bool return_to_secure;
+ bool ftype;
+ bool restore_s16_s31;
/* If we're not in Handler mode then jumps to magic exception-exit
* addresses don't have magic behaviour. However for the v8M
@@ -8268,6 +8751,16 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
excret);
}
+ ftype = excret & R_V7M_EXCRET_FTYPE_MASK;
+
+ if (!arm_feature(env, ARM_FEATURE_VFP) && !ftype) {
+ qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero FTYPE in exception "
+ "exit PC value 0x%" PRIx32 " is UNPREDICTABLE "
+ "if FPU not present\n",
+ excret);
+ ftype = true;
+ }
+
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
/* EXC_RETURN.ES validation check (R_SMFL). We must do this before
* we pick which FAULTMASK to clear.
@@ -8368,6 +8861,30 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*/
write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure);
+ /*
+ * Clear scratch FP values left in caller saved registers; this
+ * must happen before any kind of tail chaining.
+ */
+ if ((env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_CLRONRET_MASK) &&
+ (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) {
+ if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) {
+ env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
+ qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing "
+ "stackframe: error during lazy state deactivation\n");
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ } else {
+ /* Clear s0..s15 and FPSCR */
+ int i;
+
+ for (i = 0; i < 16; i += 2) {
+ *aa32_vfp_dreg(env, i / 2) = 0;
+ }
+ vfp_set_fpscr(env, 0);
+ }
+ }
+
if (sfault) {
env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
@@ -8441,12 +8958,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
if (return_to_secure &&
((excret & R_V7M_EXCRET_ES_MASK) == 0 ||
(excret & R_V7M_EXCRET_DCRS_MASK) == 0)) {
- uint32_t expected_sig = 0xfefa125b;
uint32_t actual_sig;
pop_ok = v7m_stack_read(cpu, &actual_sig, frameptr, mmu_idx);
- if (pop_ok && expected_sig != actual_sig) {
+ if (pop_ok && v7m_integrity_sig(env, excret) != actual_sig) {
/* Take a SecureFault on the current stack */
env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
@@ -8530,8 +9046,105 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
}
}
+ if (!ftype) {
+ /* FP present and we need to handle it */
+ if (!return_to_secure &&
+ (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK)) {
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
+ env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
+ qemu_log_mask(CPU_LOG_INT,
+ "...taking SecureFault on existing stackframe: "
+ "Secure LSPACT set but exception return is "
+ "not to secure state\n");
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ }
+
+ restore_s16_s31 = return_to_secure &&
+ (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK);
+
+ if (env->v7m.fpccr[return_to_secure] & R_V7M_FPCCR_LSPACT_MASK) {
+ /* State in FPU is still valid, just clear LSPACT */
+ env->v7m.fpccr[return_to_secure] &= ~R_V7M_FPCCR_LSPACT_MASK;
+ } else {
+ int i;
+ uint32_t fpscr;
+ bool cpacr_pass, nsacr_pass;
+
+ cpacr_pass = v7m_cpacr_pass(env, return_to_secure,
+ return_to_priv);
+ nsacr_pass = return_to_secure ||
+ extract32(env->v7m.nsacr, 10, 1);
+
+ if (!cpacr_pass) {
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
+ return_to_secure);
+ env->v7m.cfsr[return_to_secure] |= R_V7M_CFSR_NOCP_MASK;
+ qemu_log_mask(CPU_LOG_INT,
+ "...taking UsageFault on existing "
+ "stackframe: CPACR.CP10 prevents unstacking "
+ "FP regs\n");
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ } else if (!nsacr_pass) {
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, true);
+ env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_INVPC_MASK;
+ qemu_log_mask(CPU_LOG_INT,
+ "...taking Secure UsageFault on existing "
+ "stackframe: NSACR.CP10 prevents unstacking "
+ "FP regs\n");
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ }
+
+ for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) {
+ uint32_t slo, shi;
+ uint64_t dn;
+ uint32_t faddr = frameptr + 0x20 + 4 * i;
+
+ if (i >= 16) {
+ faddr += 8; /* Skip the slot for the FPSCR */
+ }
+
+ pop_ok = pop_ok &&
+ v7m_stack_read(cpu, &slo, faddr, mmu_idx) &&
+ v7m_stack_read(cpu, &shi, faddr + 4, mmu_idx);
+
+ if (!pop_ok) {
+ break;
+ }
+
+ dn = (uint64_t)shi << 32 | slo;
+ *aa32_vfp_dreg(env, i / 2) = dn;
+ }
+ pop_ok = pop_ok &&
+ v7m_stack_read(cpu, &fpscr, frameptr + 0x60, mmu_idx);
+ if (pop_ok) {
+ vfp_set_fpscr(env, fpscr);
+ }
+ if (!pop_ok) {
+ /*
+ * These regs are 0 if security extension present;
+ * otherwise merely UNKNOWN. We zero always.
+ */
+ for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) {
+ *aa32_vfp_dreg(env, i / 2) = 0;
+ }
+ vfp_set_fpscr(env, 0);
+ }
+ }
+ }
+ env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S],
+ V7M_CONTROL, FPCA, !ftype);
+
/* Commit to consuming the stack frame */
frameptr += 0x20;
+ if (!ftype) {
+ frameptr += 0x48;
+ if (restore_s16_s31) {
+ frameptr += 0x40;
+ }
+ }
/* Undo stack alignment (the SPREALIGN bit indicates that the original
* pre-exception SP was not 8-aligned and we added a padding word to
* align it, so we undo this by ORing in the bit that increases it
@@ -8544,7 +9157,14 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
*frame_sp_p = frameptr;
}
/* This xpsr_write() will invalidate frame_sp_p as it may switch stack */
- xpsr_write(env, xpsr, ~XPSR_SPREALIGN);
+ xpsr_write(env, xpsr, ~(XPSR_SPREALIGN | XPSR_SFPA));
+
+ if (env->v7m.secure) {
+ bool sfpa = xpsr & XPSR_SFPA;
+
+ env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S],
+ V7M_CONTROL, SFPA, sfpa);
+ }
/* The restored xPSR exception field will be zero if we're
* resuming in Thread mode. If that doesn't match what the
@@ -8667,6 +9287,9 @@ static void arm_log_exception(int idx)
[EXCP_NOCP] = "v7M NOCP UsageFault",
[EXCP_INVSTATE] = "v7M INVSTATE UsageFault",
[EXCP_STKOF] = "v8M STKOF UsageFault",
+ [EXCP_LAZYFP] = "v7M exception during lazy FP stacking",
+ [EXCP_LSERR] = "v8M LSERR UsageFault",
+ [EXCP_UNALIGNED] = "v7M UNALIGNED UsageFault",
};
if (idx >= 0 && idx < ARRAY_SIZE(excnames)) {
@@ -8785,6 +9408,7 @@ static bool v7m_handle_execute_nsc(ARMCPU *cpu)
qemu_log_mask(CPU_LOG_INT, "...really an SG instruction at 0x%08" PRIx32
", executing it\n", env->regs[15]);
env->regs[14] &= ~1;
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
switch_v7m_security_state(env, true);
xpsr_write(env, 0, XPSR_IT);
env->regs[15] += 4;
@@ -8815,9 +9439,23 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK;
break;
case EXCP_NOCP:
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
- env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK;
+ {
+ /*
+ * NOCP might be directed to something other than the current
+ * security state if this fault is because of NSACR; we indicate
+ * the target security state using exception.target_el.
+ */
+ int target_secstate;
+
+ if (env->exception.target_el == 3) {
+ target_secstate = M_REG_S;
+ } else {
+ target_secstate = env->v7m.secure;
+ }
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, target_secstate);
+ env->v7m.cfsr[target_secstate] |= R_V7M_CFSR_NOCP_MASK;
break;
+ }
case EXCP_INVSTATE:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK;
@@ -8826,6 +9464,14 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK;
break;
+ case EXCP_LSERR:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false);
+ env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK;
+ break;
+ case EXCP_UNALIGNED:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
+ env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNALIGNED_MASK;
+ break;
case EXCP_SWI:
/* The PC already points to the next instruction. */
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure);
@@ -8945,6 +9591,12 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
return;
}
break;
+ case EXCP_LAZYFP:
+ /*
+ * We already pended the specific exception in the NVIC in the
+ * v7m_preserve_fp_state() helper function.
+ */
+ break;
default:
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
return; /* Never happens. Keep compiler happy. */
@@ -8952,8 +9604,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
if (arm_feature(env, ARM_FEATURE_V8)) {
lr = R_V7M_EXCRET_RES1_MASK |
- R_V7M_EXCRET_DCRS_MASK |
- R_V7M_EXCRET_FTYPE_MASK;
+ R_V7M_EXCRET_DCRS_MASK;
/* The S bit indicates whether we should return to Secure
* or NonSecure (ie our current state).
* The ES bit indicates whether we're taking this exception
@@ -8968,6 +9619,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
if (env->v7m.secure) {
lr |= R_V7M_EXCRET_S_MASK;
}
+ if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) {
+ lr |= R_V7M_EXCRET_FTYPE_MASK;
+ }
} else {
lr = R_V7M_EXCRET_RES1_MASK |
R_V7M_EXCRET_S_MASK |
@@ -11993,7 +12647,14 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
return xpsr_read(env) & mask;
break;
case 20: /* CONTROL */
- return env->v7m.control[env->v7m.secure];
+ {
+ uint32_t value = env->v7m.control[env->v7m.secure];
+ if (!env->v7m.secure) {
+ /* SFPA is RAZ/WI from NS; FPCA is stored in the M_REG_S bank */
+ value |= env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK;
+ }
+ return value;
+ }
case 0x94: /* CONTROL_NS */
/* We have to handle this here because unprivileged Secure code
* can read the NS CONTROL register.
@@ -12001,7 +12662,8 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
if (!env->v7m.secure) {
return 0;
}
- return env->v7m.control[M_REG_NS];
+ return env->v7m.control[M_REG_NS] |
+ (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK);
}
if (el == 0) {
@@ -12107,9 +12769,13 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
*/
uint32_t mask = extract32(maskreg, 8, 4);
uint32_t reg = extract32(maskreg, 0, 8);
+ int cur_el = arm_current_el(env);
- if (arm_current_el(env) == 0 && reg > 7) {
- /* only xPSR sub-fields may be written by unprivileged */
+ if (cur_el == 0 && reg > 7 && reg != 20) {
+ /*
+ * only xPSR sub-fields and CONTROL.SFPA may be written by
+ * unprivileged code
+ */
return;
}
@@ -12168,6 +12834,15 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK;
env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK;
}
+ /*
+ * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0,
+ * RES0 if the FPU is not present, and is stored in the S bank
+ */
+ if (arm_feature(env, ARM_FEATURE_VFP) &&
+ extract32(env->v7m.nsacr, 10, 1)) {
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK;
+ env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK;
+ }
return;
case 0x98: /* SP_NS */
{
@@ -12270,21 +12945,41 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
env->v7m.faultmask[env->v7m.secure] = val & 1;
break;
case 20: /* CONTROL */
- /* Writing to the SPSEL bit only has an effect if we are in
+ /*
+ * Writing to the SPSEL bit only has an effect if we are in
* thread mode; other bits can be updated by any privileged code.
* write_v7m_control_spsel() deals with updating the SPSEL bit in
* env->v7m.control, so we only need update the others.
* For v7M, we must just ignore explicit writes to SPSEL in handler
* mode; for v8M the write is permitted but will have no effect.
+ * All these bits are writes-ignored from non-privileged code,
+ * except for SFPA.
*/
- if (arm_feature(env, ARM_FEATURE_V8) ||
- !arm_v7m_is_handler_mode(env)) {
+ if (cur_el > 0 && (arm_feature(env, ARM_FEATURE_V8) ||
+ !arm_v7m_is_handler_mode(env))) {
write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
}
- if (arm_feature(env, ARM_FEATURE_M_MAIN)) {
+ if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) {
env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK;
env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK;
}
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ /*
+ * SFPA is RAZ/WI from NS or if no FPU.
+ * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present.
+ * Both are stored in the S bank.
+ */
+ if (env->v7m.secure) {
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
+ env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_SFPA_MASK;
+ }
+ if (cur_el > 0 &&
+ (env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_SECURITY) ||
+ extract32(env->v7m.nsacr, 10, 1))) {
+ env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK;
+ env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK;
+ }
+ }
break;
default:
bad_reg:
@@ -12751,6 +13446,22 @@ int fp_exception_el(CPUARMState *env, int cur_el)
return 0;
}
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ /* CPACR can cause a NOCP UsageFault taken to current security state */
+ if (!v7m_cpacr_pass(env, env->v7m.secure, cur_el != 0)) {
+ return 1;
+ }
+
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY) && !env->v7m.secure) {
+ if (!extract32(env->v7m.nsacr, 10, 1)) {
+ /* FP insns cause a NOCP UsageFault taken to Secure */
+ return 3;
+ }
+ }
+
+ return 0;
+ }
+
/* The CPACR controls traps to EL1, or PL1 if we're 32 bit:
* 0, 2 : trap EL0 and EL1/PL1 accesses
* 1 : trap only EL0 accesses
@@ -12801,8 +13512,8 @@ int fp_exception_el(CPUARMState *env, int cur_el)
return 0;
}
-ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
- bool secstate, bool priv)
+ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env,
+ bool secstate, bool priv, bool negpri)
{
ARMMMUIdx mmu_idx = ARM_MMU_IDX_M;
@@ -12810,7 +13521,7 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
mmu_idx |= ARM_MMU_IDX_M_PRIV;
}
- if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) {
+ if (negpri) {
mmu_idx |= ARM_MMU_IDX_M_NEGPRI;
}
@@ -12821,6 +13532,14 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
return mmu_idx;
}
+ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env,
+ bool secstate, bool priv)
+{
+ bool negpri = armv7m_nvic_neg_prio_requested(env->nvic, secstate);
+
+ return arm_v7m_mmu_idx_all(env, secstate, priv, negpri);
+}
+
/* Return the MMU index for a v7M CPU in the specified security state */
ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate)
{
@@ -12938,10 +13657,14 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, arm_sctlr_b(env));
flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env));
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)
- || arm_el_is_aa64(env, 1)) {
+ || arm_el_is_aa64(env, 1) || arm_feature(env, ARM_FEATURE_M)) {
flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
}
- flags = FIELD_DP32(flags, TBFLAG_A32, XSCALE_CPAR, env->cp15.c15_cpar);
+ /* Note that XSCALE_CPAR shares bits with VECSTRIDE */
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ flags = FIELD_DP32(flags, TBFLAG_A32,
+ XSCALE_CPAR, env->cp15.c15_cpar);
+ }
}
flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX, arm_to_core_mmu_idx(mmu_idx));
@@ -12984,6 +13707,32 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
flags = FIELD_DP32(flags, TBFLAG_A32, STACKCHECK, 1);
}
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
+ FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) != env->v7m.secure) {
+ flags = FIELD_DP32(flags, TBFLAG_A32, FPCCR_S_WRONG, 1);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_M) &&
+ (env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) &&
+ (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) ||
+ (env->v7m.secure &&
+ !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) {
+ /*
+ * ASPEN is set, but FPCA/SFPA indicate that there is no active
+ * FP context; we must create a new FP context before executing
+ * any FP insn.
+ */
+ flags = FIELD_DP32(flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED, 1);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
+
+ if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) {
+ flags = FIELD_DP32(flags, TBFLAG_A32, LSPACT, 1);
+ }
+ }
+
*pflags = flags;
*cs_base = 0;
}
diff --git a/target/arm/helper.h b/target/arm/helper.h
index a09566f795..50cb036378 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -69,6 +69,11 @@ DEF_HELPER_2(v7m_blxns, void, env, i32)
DEF_HELPER_3(v7m_tt, i32, env, i32, i32)
+DEF_HELPER_1(v7m_preserve_fp_state, void, env)
+
+DEF_HELPER_2(v7m_vlstm, void, env, i32)
+DEF_HELPER_2(v7m_vlldm, void, env, i32)
+
DEF_HELPER_2(v8m_stackcheck, void, env, i32)
DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32)
diff --git a/target/arm/machine.c b/target/arm/machine.c
index b292549614..09567d4fc6 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -305,6 +305,21 @@ static const VMStateDescription vmstate_m_v8m = {
}
};
+static const VMStateDescription vmstate_m_fp = {
+ .name = "cpu/m/fp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vfp_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(env.v7m.fpcar, ARMCPU, M_REG_NUM_BANKS),
+ VMSTATE_UINT32_ARRAY(env.v7m.fpccr, ARMCPU, M_REG_NUM_BANKS),
+ VMSTATE_UINT32_ARRAY(env.v7m.fpdscr, ARMCPU, M_REG_NUM_BANKS),
+ VMSTATE_UINT32_ARRAY(env.v7m.cpacr, ARMCPU, M_REG_NUM_BANKS),
+ VMSTATE_UINT32(env.v7m.nsacr, ARMCPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_m = {
.name = "cpu/m",
.version_id = 4,
@@ -330,6 +345,7 @@ static const VMStateDescription vmstate_m = {
&vmstate_m_scr,
&vmstate_m_other_sp,
&vmstate_m_v8m,
+ &vmstate_m_fp,
NULL
}
};
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 4ea4018e2b..10bc53f91c 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -3399,8 +3399,14 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
* for attempts to execute invalid vfp/neon encodings with FP disabled.
*/
if (s->fp_excp_el) {
- gen_exception_insn(s, 4, EXCP_UDEF,
- syn_fp_access_trap(1, 0xe, false), s->fp_excp_el);
+ if (arm_dc_feature(s, ARM_FEATURE_M)) {
+ gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(),
+ s->fp_excp_el);
+ } else {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, false),
+ s->fp_excp_el);
+ }
return 0;
}
@@ -3415,6 +3421,73 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
}
}
+ if (arm_dc_feature(s, ARM_FEATURE_M)) {
+ /* Handle M-profile lazy FP state mechanics */
+
+ /* Trigger lazy-state preservation if necessary */
+ if (s->v7m_lspact) {
+ /*
+ * Lazy state saving affects external memory and also the NVIC,
+ * so we must mark it as an IO operation for icount.
+ */
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
+ gen_helper_v7m_preserve_fp_state(cpu_env);
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_end();
+ }
+ /*
+ * If the preserve_fp_state helper doesn't throw an exception
+ * then it will clear LSPACT; we don't need to repeat this for
+ * any further FP insns in this TB.
+ */
+ s->v7m_lspact = false;
+ }
+
+ /* Update ownership of FP context: set FPCCR.S to match current state */
+ if (s->v8m_fpccr_s_wrong) {
+ TCGv_i32 tmp;
+
+ tmp = load_cpu_field(v7m.fpccr[M_REG_S]);
+ if (s->v8m_secure) {
+ tcg_gen_ori_i32(tmp, tmp, R_V7M_FPCCR_S_MASK);
+ } else {
+ tcg_gen_andi_i32(tmp, tmp, ~R_V7M_FPCCR_S_MASK);
+ }
+ store_cpu_field(tmp, v7m.fpccr[M_REG_S]);
+ /* Don't need to do this for any further FP insns in this TB */
+ s->v8m_fpccr_s_wrong = false;
+ }
+
+ if (s->v7m_new_fp_ctxt_needed) {
+ /*
+ * Create new FP context by updating CONTROL.FPCA, CONTROL.SFPA
+ * and the FPSCR.
+ */
+ TCGv_i32 control, fpscr;
+ uint32_t bits = R_V7M_CONTROL_FPCA_MASK;
+
+ fpscr = load_cpu_field(v7m.fpdscr[s->v8m_secure]);
+ gen_helper_vfp_set_fpscr(cpu_env, fpscr);
+ tcg_temp_free_i32(fpscr);
+ /*
+ * We don't need to arrange to end the TB, because the only
+ * parts of FPSCR which we cache in the TB flags are the VECLEN
+ * and VECSTRIDE, and those don't exist for M-profile.
+ */
+
+ if (s->v8m_secure) {
+ bits |= R_V7M_CONTROL_SFPA_MASK;
+ }
+ control = load_cpu_field(v7m.control[M_REG_S]);
+ tcg_gen_ori_i32(control, control, bits);
+ store_cpu_field(control, v7m.control[M_REG_S]);
+ /* Don't need to do this for any further FP insns in this TB */
+ s->v7m_new_fp_ctxt_needed = false;
+ }
+ }
+
if (extract32(insn, 28, 4) == 0xf) {
/*
* Encodings with T=1 (Thumb) or unconditional (ARM):
@@ -3513,12 +3586,27 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
}
}
} else { /* !dp */
+ bool is_sysreg;
+
if ((insn & 0x6f) != 0x00)
return 1;
rn = VFP_SREG_N(insn);
+
+ is_sysreg = extract32(insn, 21, 1);
+
+ if (arm_dc_feature(s, ARM_FEATURE_M)) {
+ /*
+ * The only M-profile VFP vmrs/vmsr sysreg is FPSCR.
+ * Writes to R15 are UNPREDICTABLE; we choose to undef.
+ */
+ if (is_sysreg && (rd == 15 || (rn >> 1) != ARM_VFP_FPSCR)) {
+ return 1;
+ }
+ }
+
if (insn & ARM_CP_RW_BIT) {
/* vfp->arm */
- if (insn & (1 << 21)) {
+ if (is_sysreg) {
/* system register */
rn >>= 1;
@@ -3585,7 +3673,7 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
}
} else {
/* arm->vfp */
- if (insn & (1 << 21)) {
+ if (is_sysreg) {
rn >>= 1;
/* system register */
switch (rn) {
@@ -11707,10 +11795,19 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
case 6: case 7: case 14: case 15:
/* Coprocessor. */
if (arm_dc_feature(s, ARM_FEATURE_M)) {
- /* We don't currently implement M profile FP support,
- * so this entire space should give a NOCP fault, with
- * the exception of the v8M VLLDM and VLSTM insns, which
- * must be NOPs in Secure state and UNDEF in Nonsecure state.
+ /* 0b111x_11xx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxx */
+ if (extract32(insn, 24, 2) == 3) {
+ goto illegal_op; /* op0 = 0b11 : unallocated */
+ }
+
+ /*
+ * Decode VLLDM and VLSTM first: these are nonstandard because:
+ * * if there is no FPU then these insns must NOP in
+ * Secure state and UNDEF in Nonsecure state
+ * * if there is an FPU then these insns do not have
+ * the usual behaviour that disas_vfp_insn() provides of
+ * being controlled by CPACR/NSACR enable bits or the
+ * lazy-stacking logic.
*/
if (arm_dc_feature(s, ARM_FEATURE_V8) &&
(insn & 0xffa00f00) == 0xec200a00) {
@@ -11721,9 +11818,31 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
if (!s->v8m_secure || (insn & 0x0040f0ff)) {
goto illegal_op;
}
- /* Just NOP since FP support is not implemented */
+
+ if (arm_dc_feature(s, ARM_FEATURE_VFP)) {
+ TCGv_i32 fptr = load_reg(s, rn);
+
+ if (extract32(insn, 20, 1)) {
+ gen_helper_v7m_vlldm(cpu_env, fptr);
+ } else {
+ gen_helper_v7m_vlstm(cpu_env, fptr);
+ }
+ tcg_temp_free_i32(fptr);
+
+ /* End the TB, because we have updated FP control bits */
+ s->base.is_jmp = DISAS_UPDATE;
+ }
break;
}
+ if (arm_dc_feature(s, ARM_FEATURE_VFP) &&
+ ((insn >> 8) & 0xe) == 10) {
+ /* FP, and the CPU supports it */
+ if (disas_vfp_insn(s, insn)) {
+ goto illegal_op;
+ }
+ break;
+ }
+
/* All other insns: NOCP */
gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(),
default_exception_el(s));
@@ -13291,12 +13410,21 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL);
dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN);
dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN);
- dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE);
- dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR);
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR);
+ dc->vec_stride = 0;
+ } else {
+ dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE);
+ dc->c15_cpar = 0;
+ }
dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_A32, HANDLER);
dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) &&
regime_is_secure(env, dc->mmu_idx);
dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_A32, STACKCHECK);
+ dc->v8m_fpccr_s_wrong = FIELD_EX32(tb_flags, TBFLAG_A32, FPCCR_S_WRONG);
+ dc->v7m_new_fp_ctxt_needed =
+ FIELD_EX32(tb_flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED);
+ dc->v7m_lspact = FIELD_EX32(tb_flags, TBFLAG_A32, LSPACT);
dc->cp_regs = cpu->cp_regs;
dc->features = env->features;
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 984617786d..c2348def0d 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -40,6 +40,9 @@ typedef struct DisasContext {
bool v7m_handler_mode;
bool v8m_secure; /* true if v8M and we're in Secure mode */
bool v8m_stackcheck; /* true if we need to perform v8M stack limit checks */
+ bool v8m_fpccr_s_wrong; /* true if v8M FPCCR.S != v8m_secure */
+ bool v7m_new_fp_ctxt_needed; /* ASPEN set but no active FP context */
+ bool v7m_lspact; /* FPCCR.LSPACT set */
/* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI
* so that top level loop can generate correct syndrome information.
*/
diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c
index 2468fc1629..7a46d99148 100644
--- a/target/arm/vfp_helper.c
+++ b/target/arm/vfp_helper.c
@@ -105,6 +105,14 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val)
val &= ~FPCR_FZ16;
}
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ /*
+ * M profile FPSCR is RES0 for the QC, STRIDE, FZ16, LEN bits
+ * and also for the trapped-exception-handling bits IxE.
+ */
+ val &= 0xf7c0009f;
+ }
+
/*
* We don't implement trapped exception handling, so the
* trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!)