From c5b898743a202742847f9738382c9d928b0d8586 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Thu, 14 Jan 2021 21:28:55 +0100 Subject: [PATCH] aarch64: Fix alignment fault on z_bss_zero() Using newlibc with AArch64 is causing an alignement fault in z_bss_zero() when the code is run on real hardware (on QEMU the problem is not reproducible). The main cause is that the memset() function exported by newlibc is using 'DC ZVA' to zero out memory. While this is often a nice optimization, this is causing the issue on AArch64 because memset() is being used before the MMU is enabled, and when the MMU is disabled all data accesses will be treated as Device_nGnRnE. This is a problem because quoting from the ARM reference manual: "If the memory region being zeroed is any type of Device memory, then DC ZVA generates an Alignment fault which is prioritized in the same way as other alignment faults that are determined by the memory type". newlibc tries to be a bit smart about this reading the DCZID_EL0 register before deciding whether using 'DC ZVA' or not. While this is a good idea for code running in EL0, currently the Zephyr kernel is running in EL1. This means that the value of the DCZID_EL0 register is actually retrieved from the HCR_EL2.TDZ bit, that is always 0 because EL2 is not currently supported / enabled. So the 'DC ZVA' instruction is unconditionally used in the newlibc memset() implementation. The "standard" solution for this case is usually to use a different memset routine to be specifically used for two cases: (1) against IO memory or (2) against normal memory but with MMU disabled (which means all memory is considered device memory for data accesses). To fix this issue in Zephyr we avoid calling memset() when clearing the bss, and instead we use a simple loop to zero the memory region. Signed-off-by: Carlo Caione --- arch/arm/core/aarch64/prep_c.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm/core/aarch64/prep_c.c b/arch/arm/core/aarch64/prep_c.c index e1f149eb3d3..35916e55928 100644 --- a/arch/arm/core/aarch64/prep_c.c +++ b/arch/arm/core/aarch64/prep_c.c @@ -15,8 +15,20 @@ */ #include +#include extern FUNC_NORETURN void z_cstart(void); + +static inline void z_arm64_bss_zero(void) +{ + uint64_t *p = (uint64_t *)__bss_start; + uint64_t *end = (uint64_t *)__bss_end; + + while (p < end) { + *p++ = 0; + } +} + /** * * @brief Prepare to and run C code @@ -27,7 +39,7 @@ extern FUNC_NORETURN void z_cstart(void); */ void z_arm64_prep_c(void) { - z_bss_zero(); + z_arm64_bss_zero(); z_arm64_interrupt_init(); z_cstart();