diff options
Diffstat (limited to 'arch/arm/cpu')
55 files changed, 1744 insertions, 1060 deletions
diff --git a/arch/arm/cpu/Kconfig b/arch/arm/cpu/Kconfig index 6b4fed5269..e69acaacdf 100644 --- a/arch/arm/cpu/Kconfig +++ b/arch/arm/cpu/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + comment "Processor Type" config PHYS_ADDR_T_64BIT @@ -6,14 +8,20 @@ config PHYS_ADDR_T_64BIT config CPU_32 bool select HAS_MODULES + select HAVE_MOD_ARCH_SPECIFIC select HAS_DMA select HAVE_PBL_IMAGE + select ARCH_HAS_ZERO_PAGE config CPU_64 bool select PHYS_ADDR_T_64BIT select HAVE_PBL_IMAGE + select HAVE_PBL_MULTI_IMAGES select HAS_DMA + select ARCH_WANT_FRAME_POINTERS + select ARCH_HAS_ZERO_PAGE + select HAVE_EFI_PAYLOAD # Select CPU types depending on the architecture selected. This selects # which CPUs we support in the kernel image, and the compiler instruction @@ -84,7 +92,6 @@ config CPU_V7 config CPU_V8 bool select CPU_64v8 - select CPU_SUPPORTS_64BIT_KERNEL select ARM_EXCEPTIONS select GENERIC_FIND_NEXT_BIT select ARCH_HAS_STACK_DUMP @@ -152,14 +159,3 @@ config CACHE_L2X0 bool "Enable L2x0 PrimeCell" depends on MMU && ARCH_HAS_L2X0 -config SYS_SUPPORTS_32BIT_KERNEL - bool - -config SYS_SUPPORTS_64BIT_KERNEL - bool - -config CPU_SUPPORTS_32BIT_KERNEL - bool - -config CPU_SUPPORTS_64BIT_KERNEL - bool diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile index 63cf35c299..28161cd7d7 100644 --- a/arch/arm/cpu/Makefile +++ b/arch/arm/cpu/Makefile @@ -1,19 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-y += cpu.o -obj-$(CONFIG_ARM_EXCEPTIONS) += exceptions$(S64).o interrupts$(S64).o -obj-$(CONFIG_MMU) += mmu$(S64).o mmu-common.o -obj-pbl-y += lowlevel$(S64).o -obj-pbl-$(CONFIG_MMU) += mmu-early$(S64).o +obj-$(CONFIG_ARM_EXCEPTIONS) += exceptions_$(S64_32).o interrupts_$(S64_32).o +obj-$(CONFIG_MMU) += mmu-common.o +obj-pbl-$(CONFIG_MMU) += mmu_$(S64_32).o +obj-$(CONFIG_MMU) += dma_$(S64_32).o +obj-pbl-y += lowlevel_$(S64_32).o obj-pbl-$(CONFIG_CPU_32v7) += hyp.o AFLAGS_hyp.o :=-Wa,-march=armv7-a -Wa,-mcpu=all -AFLAGS_pbl-hyp.o :=-Wa,-march=armv7-a -Wa,-mcpu=all +AFLAGS_hyp.pbl.o :=-Wa,-march=armv7-a -Wa,-mcpu=all + +obj-y += start.o entry.o entry_ll_$(S64_32).o +KASAN_SANITIZE_start.o := n -obj-y += start.o entry.o entry_ll$(S64).o +pbl-$(CONFIG_CPU_64) += head_64.o pbl-$(CONFIG_BOARD_ARM_GENERIC_DT) += board-dt-2nd.o pbl-$(CONFIG_BOARD_ARM_GENERIC_DT_AARCH64) += board-dt-2nd-aarch64.o -obj-pbl-y += setupc$(S64).o cache$(S64).o +obj-pbl-y += setupc_$(S64_32).o cache_$(S64_32).o obj-$(CONFIG_ARM_PSCI_CLIENT) += psci-client.o @@ -21,8 +27,9 @@ obj-$(CONFIG_ARM_PSCI_CLIENT) += psci-client.o # Any variants can be called as start-armxyz.S # obj-$(CONFIG_CMD_ARM_CPUINFO) += cpuinfo.o -obj-$(CONFIG_CMD_ARM_MMUINFO) += mmuinfo.o +obj-$(CONFIG_MMUINFO) += mmuinfo.o mmuinfo_$(S64_32).o obj-$(CONFIG_OFDEVICE) += dtb.o +obj-$(CONFIG_BOOTM_ELF) += bootm-elf.o ifeq ($(CONFIG_MMU),) obj-$(CONFIG_CPU_32v7) += no-mmu.o @@ -30,24 +37,26 @@ endif obj-$(CONFIG_ARM_PSCI) += psci.o obj-$(CONFIG_ARM_PSCI_OF) += psci-of.o -obj-pbl-$(CONFIG_ARM_SMCCC) += smccc-call$(S64).o -AFLAGS_smccc-call$(S64).o :=-Wa,-march=armv$(if $(S64),8,7)-a -AFLAGS_pbl-smccc-call$(S64).o :=-Wa,-march=armv$(if $(S64),8,7)-a +obj-pbl-$(CONFIG_ARM_SMCCC) += smccc-call_$(S64_32).o +AFLAGS_smccc-call_$(S64_32).o :=-Wa,-march=armv$(if $(S64),8,7)-a +AFLAGS_smccc-call_$(S64_32).pbl.o :=-Wa,-march=armv$(if $(S64),8,7)-a obj-$(CONFIG_ARM_SECURE_MONITOR) += sm.o sm_as.o AFLAGS_sm_as.o :=-Wa,-march=armv7-a obj-pbl-$(CONFIG_CPU_32v4T) += cache-armv4.o obj-pbl-$(CONFIG_CPU_32v5) += cache-armv5.o obj-pbl-$(CONFIG_CPU_32v6) += cache-armv6.o -AFLAGS_cache-armv7.o :=-Wa,-march=armv7-a obj-pbl-$(CONFIG_CPU_32v7) += cache-armv7.o -AFLAGS_pbl-cache-armv7.o :=-Wa,-march=armv7-a +AFLAGS_cache-armv7.o :=-Wa,-march=armv7-a +AFLAGS_cache-armv7.pbl.o :=-Wa,-march=armv7-a obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o -AFLAGS_cache-armv8.o :=-Wa,-march=armv8-a obj-pbl-$(CONFIG_CPU_64v8) += cache-armv8.o -AFLAGS_pbl-cache-armv8.o :=-Wa,-march=armv8-a +AFLAGS_cache-armv8.o :=-Wa,-march=armv8-a +AFLAGS-cache-armv8.pbl.o :=-Wa,-march=armv8-a -pbl-y += entry.o entry_ll$(S64).o +pbl-y += entry.o entry_ll_$(S64_32).o pbl-y += uncompress.o +pbl-$(CONFIG_ARM_ATF) += atf.o obj-pbl-y += common.o sections.o +KASAN_SANITIZE_common.o := n diff --git a/arch/arm/cpu/atf.c b/arch/arm/cpu/atf.c new file mode 100644 index 0000000000..d01e20508c --- /dev/null +++ b/arch/arm/cpu/atf.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <asm/atf_common.h> +#include <asm/system.h> + +static inline void raw_write_daif(unsigned int daif) +{ + __asm__ __volatile__("msr DAIF, %0\n\t" : : "r" (daif) : "memory"); +} + +void bl31_entry(uintptr_t bl31_entry, uintptr_t bl32_entry, + uintptr_t bl33_entry, uintptr_t fdt_addr) +{ + struct atf_image_info bl31_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_1, + .size = sizeof(bl31_image_info), + }, + }; + struct atf_image_info bl32_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_1, + .size = sizeof(bl32_image_info), + }, + }; + struct entry_point_info bl32_ep_info = { + .h = { + .type = ATF_PARAM_EP, + .version = ATF_VERSION_1, + .attr = ATF_EP_SECURE, + .size = sizeof(bl32_ep_info), + }, + .pc = bl32_entry, + .spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXECPTIONS), + .args = { + .arg3 = fdt_addr, + }, + }; + struct atf_image_info bl33_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_1, + .size = sizeof(bl33_image_info), + }, + }; + struct entry_point_info bl33_ep_info = { + .h = { + .type = ATF_PARAM_EP, + .version = ATF_VERSION_1, + .attr = ATF_EP_NON_SECURE, + .size = sizeof(bl33_ep_info), + }, + .pc = bl33_entry, + .spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, DISABLE_ALL_EXECPTIONS), + .args = { + /* BL33 expects to receive the primary CPU MPID (through x0) */ + .arg0 = 0xffff & read_mpidr(), + }, + }; + struct bl31_params bl31_params = { + .h = { + .type = ATF_PARAM_BL31, + .version = ATF_VERSION_1, + .size = sizeof(bl31_params), + }, + .bl31_image_info = &bl31_image_info, + .bl32_ep_info = &bl32_ep_info, + .bl32_image_info = &bl32_image_info, + .bl33_ep_info = &bl33_ep_info, + .bl33_image_info = &bl33_image_info, + }; + void (*atf_entry)(struct bl31_params *params, uintptr_t plat_params); + + raw_write_daif(SPSR_EXCEPTION_MASK); + + atf_entry = (void *)bl31_entry; + + atf_entry(&bl31_params, fdt_addr); +} + +struct bl2_to_bl31_params_mem_v2 *bl2_plat_get_bl31_params_v2(uintptr_t bl32_entry, + uintptr_t bl33_entry, uintptr_t fdt_addr) +{ + static struct bl2_to_bl31_params_mem_v2 p = { + .bl_params = { + .h = { + .type = ATF_PARAM_BL_PARAMS, + .version = ATF_VERSION_2, + .size = sizeof(struct bl_params), + .attr = 0, + }, + .head = &p.bl31_params_node, + }, + .bl31_params_node = { + .image_id = ATF_BL31_IMAGE_ID, + .image_info = &p.bl31_image_info, + .ep_info = &p.bl31_ep_info, + .next_params_info = &p.bl32_params_node, + }, + .bl32_params_node = { + .image_id = ATF_BL32_IMAGE_ID, + .image_info = &p.bl32_image_info, + .ep_info = &p.bl32_ep_info, + .next_params_info = &p.bl33_params_node, + }, + .bl33_params_node = { + .image_id = ATF_BL33_IMAGE_ID, + .image_info = &p.bl33_image_info, + .ep_info = &p.bl33_ep_info, + .next_params_info = NULL, + }, + .bl32_ep_info = { + .h = { + .type = ATF_PARAM_EP, + .version = ATF_VERSION_2, + .size = sizeof(struct entry_point_info), + .attr = ATF_EP_SECURE, + }, + .spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXECPTIONS), + }, + .bl33_ep_info = { + .h = { + .type = ATF_PARAM_EP, + .version = ATF_VERSION_2, + .size = sizeof(struct entry_point_info), + .attr = ATF_EP_NON_SECURE, + }, + .spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, DISABLE_ALL_EXECPTIONS), + }, + .bl33_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_2, + .size = sizeof(struct atf_image_info), + .attr = 0, + }, + }, + .bl32_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_2, + .size = sizeof(struct atf_image_info), + .attr = ATF_EP_SECURE, + }, + }, + .bl31_image_info = { + .h = { + .type = ATF_PARAM_IMAGE_BINARY, + .version = ATF_VERSION_2, + .size = sizeof(struct atf_image_info), + .attr = 0, + }, + }, + }; + + p.bl33_ep_info.args.arg0 = 0xffff & read_mpidr(); + p.bl33_ep_info.pc = bl33_entry; + p.bl32_ep_info.args.arg3 = fdt_addr; + p.bl32_ep_info.pc = bl32_entry; + + return &p; +} + +void bl31_entry_v2(uintptr_t bl31_entry, struct bl_params *params, void *fdt_addr) +{ + void (*atf_entry)(struct bl_params *params, uintptr_t plat_params); + + raw_write_daif(SPSR_EXCEPTION_MASK); + + atf_entry = (void *)bl31_entry; + + atf_entry(params, (uintptr_t)fdt_addr); +} diff --git a/arch/arm/cpu/board-dt-2nd-aarch64.S b/arch/arm/cpu/board-dt-2nd-aarch64.S index 0540a1690d..030366c1cb 100644 --- a/arch/arm/cpu/board-dt-2nd-aarch64.S +++ b/arch/arm/cpu/board-dt-2nd-aarch64.S @@ -1,11 +1,33 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include <linux/linkage.h> #include <asm/barebox-arm64.h> +#include <asm/image.h> +#include "efi-header-aarch64.S" -ENTRY_PROC(start_dt_2nd) - adr x1, stack +#define IMAGE_FLAGS \ + (ARM64_IMAGE_FLAG_PAGE_SIZE_4K << ARM64_IMAGE_FLAG_PAGE_SIZE_SHIFT) | \ + (ARM64_IMAGE_FLAG_PHYS_BASE << ARM64_IMAGE_FLAG_PHYS_BASE_SHIFT) + +.section .text_head_entry_start_dt_2nd +ENTRY("start_dt_2nd") + efi_signature_nop /* code0 */ + b 2f /* code1 */ + .xword 0x80000 /* Image load offset */ + .xword _barebox_image_size /* Effective Image size */ + .xword IMAGE_FLAGS /* Kernel flags */ + .xword 0 /* reserved */ + .xword 0 /* reserved */ + .xword 0 /* reserved */ + .ascii ARM64_IMAGE_MAGIC /* magic number */ + .int .Lpe_header_offset /* reserved (PE-COFF offset) */ + .asciz "barebox" /* unused for now */ +2: + adr x1, 0 mov sp, x1 + /* Stack now grows into the 0x80000 image load offset specified + * above. This is more than enough until FDT /memory is decoded. + */ b dt_2nd_aarch64 -.word 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 -stack: + + __EFI_PE_HEADER ENTRY_PROC_END(start_dt_2nd) diff --git a/arch/arm/cpu/board-dt-2nd.c b/arch/arm/cpu/board-dt-2nd.c index 4e7d575e8a..6f69a6dd27 100644 --- a/arch/arm/cpu/board-dt-2nd.c +++ b/arch/arm/cpu/board-dt-2nd.c @@ -8,101 +8,32 @@ #include <debug_ll.h> #include <asm/cache.h> #include <asm/sections.h> -#include <linux/libfdt.h> - -static void of_find_mem(void *fdt, unsigned long *membase, unsigned long *memsize) -{ - const __be32 *nap, *nsp, *reg; - uint32_t na, ns; - uint64_t memsize64, membase64; - int node, size, i; - - /* Make sure FDT blob is sane */ - if (fdt_check_header(fdt) != 0) { - pr_err("Invalid device tree blob\n"); - goto err; - } - - /* Find the #address-cells and #size-cells properties */ - node = fdt_path_offset(fdt, "/"); - if (node < 0) { - pr_err("Cannot find root node\n"); - goto err; - } - - nap = fdt_getprop(fdt, node, "#address-cells", &size); - if (!nap || (size != 4)) { - pr_err("Cannot find #address-cells property"); - goto err; - } - na = fdt32_to_cpu(*nap); - - nsp = fdt_getprop(fdt, node, "#size-cells", &size); - if (!nsp || (size != 4)) { - pr_err("Cannot find #size-cells property"); - goto err; - } - ns = fdt32_to_cpu(*nap); - - /* Find the memory range */ - node = fdt_node_offset_by_prop_value(fdt, -1, "device_type", - "memory", sizeof("memory")); - if (node < 0) { - pr_err("Cannot find memory node\n"); - goto err; - } - - reg = fdt_getprop(fdt, node, "reg", &size); - if (size < (na + ns) * sizeof(u32)) { - pr_err("cannot get memory range\n"); - goto err; - } - - membase64 = 0; - for (i = 0; i < na; i++) - membase64 = (membase64 << 32) | fdt32_to_cpu(*reg++); - - /* get the memsize and truncate it to under 4G on 32 bit machines */ - memsize64 = 0; - for (i = 0; i < ns; i++) - memsize64 = (memsize64 << 32) | fdt32_to_cpu(*reg++); - - *membase = membase64; - *memsize = memsize64; - - return; -err: - pr_err("No memory, cannot continue\n"); - while (1); -} +#include <pbl.h> #ifdef CONFIG_CPU_V8 -static noinline void dt_2nd_continue_aarch64(void *fdt) -{ - unsigned long membase, memsize; - - if (!fdt) - hang(); - - of_find_mem(fdt, &membase, &memsize); - - barebox_arm_entry(membase, memsize, fdt); -} - /* called from assembly */ void dt_2nd_aarch64(void *fdt); void dt_2nd_aarch64(void *fdt) { - unsigned long image_start = (unsigned long)_text + global_variable_offset(); + unsigned long membase, memsize; - arm_setup_stack(image_start); + putc_ll('>'); + + /* entry point already set up stack */ + + arm_cpu_lowlevel_init(); relocate_to_current_adr(); setup_c(); - dt_2nd_continue_aarch64(fdt); + if (!fdt) + hang(); + + fdt_find_mem(fdt, &membase, &memsize); + + barebox_arm_entry(membase, memsize, fdt); } #else @@ -114,7 +45,7 @@ static noinline void dt_2nd_continue(void *fdt) if (!fdt) hang(); - of_find_mem(fdt, &membase, &memsize); + fdt_find_mem(fdt, &membase, &memsize); barebox_arm_entry(membase, memsize, fdt); } @@ -123,6 +54,8 @@ ENTRY_FUNCTION(start_dt_2nd, r0, r1, r2) { unsigned long image_start = (unsigned long)_text + global_variable_offset(); + arm_cpu_lowlevel_init(); + arm_setup_stack(image_start); relocate_to_current_adr(); diff --git a/arch/arm/cpu/bootm-elf.c b/arch/arm/cpu/bootm-elf.c new file mode 100644 index 0000000000..bcca3931f2 --- /dev/null +++ b/arch/arm/cpu/bootm-elf.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "ELF: " fmt + +#include <bootm.h> +#include <elf.h> +#include <common.h> +#include <init.h> +#include <errno.h> + +static int do_bootm_elf(struct image_data *data) +{ + void (*fn)(unsigned long x0, unsigned long x1, unsigned long x2, + unsigned long x3); + struct elf_image *elf = data->elf; + int ret; + + if (elf_hdr_e_machine(elf, elf->hdr_buf) != ELF_ARCH) { + pr_err("Unsupported machine: 0x%02x, but 0x%02x expected\n", + elf_hdr_e_machine(elf, elf->hdr_buf), ELF_ARCH); + + return -EINVAL; + } + + ret = bootm_load_os(data, data->os_address); + if (ret) + return ret; + + if (data->dryrun) + return ret; + + ret = of_overlay_load_firmware(); + if (ret) + return ret; + + shutdown_barebox(); + + fn = (void *) (unsigned long) data->os_address; + + fn(0, 0, 0, 0); + + pr_err("ELF application terminated\n"); + return -EINVAL; +} + +static struct image_handler elf_handler = { + .name = "ELF", + .bootm = do_bootm_elf, + .filetype = filetype_elf, +}; + +static int arm_register_elf_image_handler(void) +{ + return register_image_handler(&elf_handler); +} +late_initcall(arm_register_elf_image_handler); diff --git a/arch/arm/cpu/cache-armv4.S b/arch/arm/cpu/cache-armv4.S index db87de17e9..78a098b2fe 100644 --- a/arch/arm/cpu/cache-armv4.S +++ b/arch/arm/cpu/cache-armv4.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> diff --git a/arch/arm/cpu/cache-armv5.S b/arch/arm/cpu/cache-armv5.S index 4267f3e37f..bcb7ebf466 100644 --- a/arch/arm/cpu/cache-armv5.S +++ b/arch/arm/cpu/cache-armv5.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> diff --git a/arch/arm/cpu/cache-armv6.S b/arch/arm/cpu/cache-armv6.S index 7a06751997..cc720314c0 100644 --- a/arch/arm/cpu/cache-armv6.S +++ b/arch/arm/cpu/cache-armv6.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> diff --git a/arch/arm/cpu/cache-armv7.S b/arch/arm/cpu/cache-armv7.S index 0f6108426c..efd9fe412f 100644 --- a/arch/arm/cpu/cache-armv7.S +++ b/arch/arm/cpu/cache-armv7.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> diff --git a/arch/arm/cpu/cache-l2x0.c b/arch/arm/cpu/cache-l2x0.c index e975ecffc7..82ae16ba4d 100644 --- a/arch/arm/cpu/cache-l2x0.c +++ b/arch/arm/cpu/cache-l2x0.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #define pr_fmt(fmt) "l2x0: " fmt #include <common.h> diff --git a/arch/arm/cpu/cache.c b/arch/arm/cpu/cache_32.c index 2b6e958a4e..0ac50c4d9a 100644 --- a/arch/arm/cpu/cache.c +++ b/arch/arm/cpu/cache_32.c @@ -1,10 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <init.h> #include <asm/mmu.h> #include <asm/cache.h> #include <asm/system_info.h> -#include "mmu.h" +#include "mmu_32.h" struct cache_fns { void (*dma_clean_range)(unsigned long start, unsigned long end); @@ -15,8 +17,6 @@ struct cache_fns { void (*mmu_cache_flush)(void); }; -struct cache_fns *cache_fns; - #define DEFINE_CPU_FNS(arch) \ void arch##_dma_clean_range(unsigned long start, unsigned long end); \ void arch##_dma_flush_range(unsigned long start, unsigned long end); \ @@ -39,50 +39,13 @@ DEFINE_CPU_FNS(v5) DEFINE_CPU_FNS(v6) DEFINE_CPU_FNS(v7) -void __dma_clean_range(unsigned long start, unsigned long end) +static struct cache_fns *cache_functions(void) { - if (cache_fns) - cache_fns->dma_clean_range(start, end); -} + static struct cache_fns *cache_fns; -void __dma_flush_range(unsigned long start, unsigned long end) -{ if (cache_fns) - cache_fns->dma_flush_range(start, end); -} + return cache_fns; -void __dma_inv_range(unsigned long start, unsigned long end) -{ - if (cache_fns) - cache_fns->dma_inv_range(start, end); -} - -#ifdef CONFIG_MMU - -void __mmu_cache_on(void) -{ - if (cache_fns) - cache_fns->mmu_cache_on(); -} - -void __mmu_cache_off(void) -{ - if (cache_fns) - cache_fns->mmu_cache_off(); -} - -void __mmu_cache_flush(void) -{ - if (cache_fns) - cache_fns->mmu_cache_flush(); - if (outer_cache.flush_all) - outer_cache.flush_all(); -} - -#endif - -int arm_set_cache_functions(void) -{ switch (cpu_architecture()) { #ifdef CONFIG_CPU_32v4T case CPU_ARCH_ARMv4T: @@ -111,9 +74,45 @@ int arm_set_cache_functions(void) while(1); } - return 0; + return cache_fns; +} + +void __dma_clean_range(unsigned long start, unsigned long end) +{ + cache_functions()->dma_clean_range(start, end); +} + +void __dma_flush_range(unsigned long start, unsigned long end) +{ + cache_functions()->dma_flush_range(start, end); +} + +void __dma_inv_range(unsigned long start, unsigned long end) +{ + cache_functions()->dma_inv_range(start, end); +} + +#ifdef CONFIG_MMU + +void __mmu_cache_on(void) +{ + cache_functions()->mmu_cache_on(); +} + +void __mmu_cache_off(void) +{ + cache_functions()->mmu_cache_off(); } +void __mmu_cache_flush(void) +{ + cache_functions()->mmu_cache_flush(); + if (outer_cache.flush_all) + outer_cache.flush_all(); +} + +#endif + /* * Early function to flush the caches. This is for use when the * C environment is not yet fully initialized. diff --git a/arch/arm/cpu/cache_64.c b/arch/arm/cpu/cache_64.c index 6e18d981a4..3a30296128 100644 --- a/arch/arm/cpu/cache_64.c +++ b/arch/arm/cpu/cache_64.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> @@ -15,11 +6,6 @@ #include <asm/cache.h> #include <asm/system_info.h> -int arm_set_cache_functions(void) -{ - return 0; -} - /* * Early function to flush the caches. This is for use when the * C environment is not yet fully initialized. diff --git a/arch/arm/cpu/common.c b/arch/arm/cpu/common.c index c7d1709b8b..e9118b450d 100644 --- a/arch/arm/cpu/common.c +++ b/arch/arm/cpu/common.c @@ -1,19 +1,5 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix #include <common.h> #include <init.h> @@ -37,6 +23,12 @@ */ void sync_caches_for_execution(void) { + /* if caches are disabled, don't do data cache maintenance */ + if (!(get_cr() & CR_C)) { + icache_invalidate(); + return; + } + /* * Despite the name arm_early_mmu_cache_flush not only flushes the * data cache, but also invalidates the instruction cache. @@ -67,18 +59,21 @@ void pbl_barebox_break(void) /* * relocate binary to the currently running address */ -void relocate_to_current_adr(void) +void __prereloc relocate_to_current_adr(void) { - unsigned long offset, offset_var; + unsigned long offset; unsigned long __maybe_unused *dynsym, *dynend; void *dstart, *dend; /* Get offset between linked address and runtime address */ offset = get_runtime_offset(); - offset_var = global_variable_offset(); - dstart = (void *)__rel_dyn_start + offset_var; - dend = (void *)__rel_dyn_end + offset_var; + /* + * We have yet to relocate, so using runtime_address + * to compute the relocated address + */ + dstart = runtime_address(__rel_dyn_start); + dend = runtime_address(__rel_dyn_end); #if defined(CONFIG_CPU_64) while (dstart < dend) { @@ -98,14 +93,14 @@ void relocate_to_current_adr(void) putc_ll(' '); puthex_ll(rel->r_addend); putc_ll('\n'); - panic(""); + __hang(); } dstart += sizeof(*rel); } #elif defined(CONFIG_CPU_32) - dynsym = (void *)__dynsym_start + offset_var; - dynend = (void *)__dynsym_end + offset_var; + dynsym = runtime_address(__dynsym_start); + dynend = runtime_address(__dynsym_end); while (dstart < dend) { struct elf32_rel *rel = dstart; @@ -128,13 +123,13 @@ void relocate_to_current_adr(void) putc_ll(' '); puthex_ll(rel->r_offset); putc_ll('\n'); - panic(""); + __hang(); } dstart += sizeof(*rel); } - memset(dynsym, 0, (unsigned long)dynend - (unsigned long)dynsym); + __memset(dynsym, 0, (unsigned long)dynend - (unsigned long)dynsym); #else #error "Architecture not specified" #endif diff --git a/arch/arm/cpu/cpu.c b/arch/arm/cpu/cpu.c index c5daf6c60e..5f1ffe9a3c 100644 --- a/arch/arm/cpu/cpu.c +++ b/arch/arm/cpu/cpu.c @@ -1,20 +1,5 @@ -/* - * cpu.c - A few helper functions for ARM - * - * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix /** * @file @@ -32,8 +17,7 @@ #include <asm/cputype.h> #include <asm/cache.h> #include <asm/ptrace.h> - -#include "mmu.h" +#include <efi/efi-mode.h> /** * Enable processor's instruction cache @@ -99,6 +83,8 @@ static void disable_interrupts(void) */ static void arch_shutdown(void) { + if (efi_is_payload()) + return; #ifdef CONFIG_MMU mmu_disable(); @@ -113,6 +99,9 @@ extern unsigned long arm_stack_top; static int arm_request_stack(void) { + if (efi_is_payload()) + return 0; + if (!request_sdram_region("stack", arm_stack_top - STACK_SIZE, STACK_SIZE)) pr_err("Error: Cannot request SDRAM region for stack\n"); diff --git a/arch/arm/cpu/cpuinfo.c b/arch/arm/cpu/cpuinfo.c index ff6e1eb87b..2d3fe2ac8d 100644 --- a/arch/arm/cpu/cpuinfo.c +++ b/arch/arm/cpu/cpuinfo.c @@ -1,22 +1,10 @@ -/* - * cpuinfo.c - Show information about cp15 registers - * - * Copyright (c) 2009 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2009 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + +/* cpuinfo.c - Show information about cp15 registers */ #include <common.h> +#include <getopt.h> #include <command.h> #include <complete.h> #include <asm/system.h> @@ -40,6 +28,7 @@ #define ARM_CPU_PART_CORTEX_A15 0xC0F0 #define ARM_CPU_PART_CORTEX_A53 0xD030 #define ARM_CPU_PART_CORTEX_A57 0xD070 +#define ARM_CPU_PART_CORTEX_A72 0xD080 static void decode_cache(unsigned long size) { @@ -61,9 +50,23 @@ static int do_cpuinfo(int argc, char *argv[]) { unsigned long mainid, cache, cr; char *architecture, *implementer; - int i; + int opt, i; int cpu_arch; + while ((opt = getopt(argc, argv, "s")) > 0) { + switch (opt) { + case 's': + if (!IS_ENABLED(CONFIG_ARCH_HAS_STACK_DUMP)) + return -ENOSYS; + + printf("SP: 0x%08lx\n", get_sp()); + dump_stack(); + return 0; + default: + return COMMAND_ERROR_USAGE; + } + } + #ifdef CONFIG_CPU_64v8 __asm__ __volatile__( "mrs %0, midr_el1\n" @@ -204,7 +207,7 @@ static int do_cpuinfo(int argc, char *argv[]) if (cpu_arch >= CPU_ARCH_ARMv7) { unsigned int major, minor; - char *part; + const char *part = NULL; major = (mainid >> 20) & 0xf; minor = mainid & 0xf; switch (mainid & 0xfff0) { @@ -229,12 +232,23 @@ static int do_cpuinfo(int argc, char *argv[]) case ARM_CPU_PART_CORTEX_A57: part = "Cortex-A57"; break; + case ARM_CPU_PART_CORTEX_A72: + part = "Cortex-A72"; + break; default: - part = "unknown"; + printf("core: unknown (0x%08lx) r%up%u\n", + mainid, major, minor); + break; } - printf("core: %s r%up%u\n", part, major, minor); + + if (part) + printf("core: %s r%up%u\n", part, major, minor); } +#ifdef CONFIG_CPU_64v8 + printf("exception level: %u\n", current_el()); +#endif + if (cache & (1 << 24)) { /* separate I/D cache */ printf("I-cache: "); @@ -256,10 +270,16 @@ static int do_cpuinfo(int argc, char *argv[]) return 0; } +BAREBOX_CMD_HELP_START(cpuinfo) +BAREBOX_CMD_HELP_TEXT("Shows misc info about CPU") +BAREBOX_CMD_HELP_OPT ("-s", "print call stack info (if supported)") +BAREBOX_CMD_HELP_END + BAREBOX_CMD_START(cpuinfo) .cmd = do_cpuinfo, BAREBOX_CMD_DESC("show info about CPU") + BAREBOX_CMD_OPTS("[-s]") BAREBOX_CMD_GROUP(CMD_GRP_INFO) BAREBOX_CMD_COMPLETE(empty_complete) + BAREBOX_CMD_HELP(cmd_cpuinfo_help) BAREBOX_CMD_END - diff --git a/arch/arm/cpu/dma_32.c b/arch/arm/cpu/dma_32.c new file mode 100644 index 0000000000..842ea7033a --- /dev/null +++ b/arch/arm/cpu/dma_32.c @@ -0,0 +1,19 @@ +#include <dma.h> +#include <asm/mmu.h> + +void arch_sync_dma_for_device(void *vaddr, size_t size, + enum dma_data_direction dir) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size; + + if (dir == DMA_FROM_DEVICE) { + __dma_inv_range(start, end); + if (outer_cache.inv_range) + outer_cache.inv_range(start, end); + } else { + __dma_clean_range(start, end); + if (outer_cache.clean_range) + outer_cache.clean_range(start, end); + } +} diff --git a/arch/arm/cpu/dma_64.c b/arch/arm/cpu/dma_64.c new file mode 100644 index 0000000000..74d7167860 --- /dev/null +++ b/arch/arm/cpu/dma_64.c @@ -0,0 +1,15 @@ +#include <dma.h> +#include <asm/mmu.h> +#include <asm/cache.h> + +void arch_sync_dma_for_device(void *vaddr, size_t size, + enum dma_data_direction dir) +{ + unsigned long start = (unsigned long)vaddr; + unsigned long end = start + size - 1; + + if (dir == DMA_FROM_DEVICE) + v8_inv_dcache_range(start, end); + else + v8_flush_dcache_range(start, end); +} diff --git a/arch/arm/cpu/dtb.c b/arch/arm/cpu/dtb.c index c43474e63b..9aa979ca08 100644 --- a/arch/arm/cpu/dtb.c +++ b/arch/arm/cpu/dtb.c @@ -1,19 +1,6 @@ -/* - * Copyright (c) 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + #include <common.h> #include <init.h> #include <of.h> @@ -39,14 +26,6 @@ static int of_arm_init(void) return 0; } - root = of_unflatten_dtb(fdt); - if (!IS_ERR(root)) { - of_set_root_node(root); - of_fix_tree(root); - if (IS_ENABLED(CONFIG_OFDEVICE)) - of_probe(); - } - - return 0; + return barebox_register_fdt(fdt); } core_initcall(of_arm_init); diff --git a/arch/arm/cpu/efi-header-aarch64.S b/arch/arm/cpu/efi-header-aarch64.S new file mode 100644 index 0000000000..941d0d8fdc --- /dev/null +++ b/arch/arm/cpu/efi-header-aarch64.S @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 - 2017 Linaro, Ltd. + * Copyright (C) 2013, 2014 Red Hat, Inc. + */ + +#include <linux/pe.h> +#include <linux/sizes.h> +#include <asm/memory.h> + + .macro efi_signature_nop +#ifdef CONFIG_EFI_STUB +.L_head: + /* + * This ccmp instruction has no meaningful effect except that + * its opcode forms the magic "MZ" signature required by UEFI. + */ + ccmp x18, #0, #0xd, pl +#else + /* + * Bootloaders may inspect the opcode at the start of the kernel + * image to decide if the kernel is capable of booting via UEFI. + * So put an ordinary NOP here, not the "MZ.." pseudo-nop above. + */ + nop +#endif + .endm + + .macro __EFI_PE_HEADER +#ifdef CONFIG_EFI_STUB + .set .Lpe_header_offset, . - .L_head + .long PE_MAGIC + .short IMAGE_FILE_MACHINE_ARM64 // Machine + .short .Lsection_count // NumberOfSections + .long 0 // TimeDateStamp + .long 0 // PointerToSymbolTable + .long 0 // NumberOfSymbols + .short .Lsection_table - .Loptional_header // SizeOfOptionalHeader + .short IMAGE_FILE_DEBUG_STRIPPED | \ + IMAGE_FILE_EXECUTABLE_IMAGE | \ + IMAGE_FILE_LINE_NUMS_STRIPPED // Characteristics + +.Loptional_header: + .short PE_OPT_MAGIC_PE32PLUS // PE32+ format + .byte 0x02 // MajorLinkerVersion + .byte 0x14 // MinorLinkerVersion + .long _sdata - .Lefi_header_end // SizeOfCode + .long __pecoff_data_size // SizeOfInitializedData + .long 0 // SizeOfUninitializedData + .long __efistub_efi_pe_entry - .L_head // AddressOfEntryPoint + .long .Lefi_header_end - .L_head // BaseOfCode + + .quad 0 // ImageBase + .long PBL_SEGMENT_ALIGN // SectionAlignment + .long PECOFF_FILE_ALIGNMENT // FileAlignment + .short 0 // MajorOperatingSystemVersion + .short 0 // MinorOperatingSystemVersion + .short LINUX_EFISTUB_MAJOR_VERSION // MajorImageVersion + .short LINUX_EFISTUB_MINOR_VERSION // MinorImageVersion + .short 0 // MajorSubsystemVersion + .short 0 // MinorSubsystemVersion + .long 0 // Win32VersionValue + + .long __image_end - .L_head // SizeOfImage + + // Everything before the kernel image is considered part of the header + .long .Lefi_header_end - .L_head // SizeOfHeaders + .long 0 // CheckSum + .short IMAGE_SUBSYSTEM_EFI_APPLICATION // Subsystem + .short 0 // DllCharacteristics + .quad 0 // SizeOfStackReserve + .quad 0 // SizeOfStackCommit + .quad 0 // SizeOfHeapReserve + .quad 0 // SizeOfHeapCommit + .long 0 // LoaderFlags + .long (.Lsection_table - .) / 8 // NumberOfRvaAndSizes + + .quad 0 // ExportTable + .quad 0 // ImportTable + .quad 0 // ResourceTable + .quad 0 // ExceptionTable + .quad 0 // CertificationTable + .quad 0 // BaseRelocationTable + + // Section table +.Lsection_table: + .ascii ".text\0\0\0" + .long _sdata - .Lefi_header_end // VirtualSize + .long .Lefi_header_end - .L_head // VirtualAddress + .long _sdata - .Lefi_header_end // SizeOfRawData + .long .Lefi_header_end - .L_head // PointerToRawData + + .long 0 // PointerToRelocations + .long 0 // PointerToLineNumbers + .short 0 // NumberOfRelocations + .short 0 // NumberOfLineNumbers + .long IMAGE_SCN_CNT_CODE | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_EXECUTE // Characteristics + + .ascii ".data\0\0\0" + .long __pecoff_data_size // VirtualSize + .long _sdata - .L_head // VirtualAddress + .long __pecoff_data_rawsize // SizeOfRawData + .long _sdata - .L_head // PointerToRawData + + .long 0 // PointerToRelocations + .long 0 // PointerToLineNumbers + .short 0 // NumberOfRelocations + .short 0 // NumberOfLineNumbers + .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_WRITE // Characteristics + + .set .Lsection_count, (. - .Lsection_table) / 40 + + .balign PBL_SEGMENT_ALIGN +.Lefi_header_end: +#else + .set .Lpe_header_offset, 0x0 +#endif + .endm diff --git a/arch/arm/cpu/entry.c b/arch/arm/cpu/entry.c index 0b447de801..cc08d0ff7e 100644 --- a/arch/arm/cpu/entry.c +++ b/arch/arm/cpu/entry.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <types.h> #include <asm/cache.h> @@ -38,5 +40,8 @@ void NAKED __noreturn barebox_arm_entry(unsigned long membase, unsigned long memsize, void *boarddata) { __barebox_arm_entry(membase, memsize, boarddata, - arm_mem_stack_top(membase, membase + memsize)); + arm_mem_stack_top(membase + memsize)); } + +void __noreturn barebox_pbl_entry(ulong, ulong, void *) + __alias(barebox_arm_entry); diff --git a/arch/arm/cpu/entry.h b/arch/arm/cpu/entry.h index 18110eadf3..ba0d3a25fe 100644 --- a/arch/arm/cpu/entry.h +++ b/arch/arm/cpu/entry.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #ifndef __ENTRY_H__ #define __ENTRY_H__ diff --git a/arch/arm/cpu/entry_ll.S b/arch/arm/cpu/entry_ll_32.S index 8cc7a84f10..2800174c45 100644 --- a/arch/arm/cpu/entry_ll.S +++ b/arch/arm/cpu/entry_ll_32.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <asm/sections.h> diff --git a/arch/arm/cpu/entry_ll_64.S b/arch/arm/cpu/entry_ll_64.S index fb8645e0a0..6530bec5eb 100644 --- a/arch/arm/cpu/entry_ll_64.S +++ b/arch/arm/cpu/entry_ll_64.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <asm/sections.h> diff --git a/arch/arm/cpu/exceptions.S b/arch/arm/cpu/exceptions_32.S index eda0d6ab8d..749c713aab 100644 --- a/arch/arm/cpu/exceptions.S +++ b/arch/arm/cpu/exceptions_32.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <config.h> #include <linux/linkage.h> #include <asm-generic/memory_layout.h> @@ -55,26 +57,6 @@ mov r0, sp .endm - .macro irq_save_user_regs - sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - r12} @ Calling r0-r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ @ Calling SP, LR - str lr, [r8, #0] @ Save calling PC - mrs r6, spsr - str r6, [r8, #4] @ Save CPSR - str r0, [r8, #8] @ Save OLD_R0 - mov r0, sp - .endm - - .macro irq_restore_user_regs - ldmia sp, {r0 - lr}^ @ Calling r0 - lr - mov r0, r0 - ldr lr, [sp, #S_PC] @ Get PC - add sp, sp, #S_FRAME_SIZE - subs pc, lr, #4 @ return & move spsr_svc into cpsr - .endm - .macro get_bad_stack ldr r13, =abort_stack str lr, [r13] @ save caller lr / spsr @@ -103,14 +85,6 @@ do_abort_\@: .endm - .macro get_irq_stack @ setup IRQ stack - ldr sp, IRQ_STACK_START - .endm - - .macro get_fiq_stack @ setup FIQ stack - ldr sp, FIQ_STACK_START - .endm - /* * exception handlers */ diff --git a/arch/arm/cpu/head_64.S b/arch/arm/cpu/head_64.S new file mode 100644 index 0000000000..546efc263a --- /dev/null +++ b/arch/arm/cpu/head_64.S @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <linux/linkage.h> +#include <asm/barebox-arm64.h> +#include <asm/image.h> + +/* Linker will point these at board-specific symbols */ +.globl __pbl_board_stack_top +.globl __pbl_board_entry + +.section .text_head_prologue_common, "x" +ENTRY(__barebox_arm64_head) + nop + adr x9, __pbl_board_stack_top + ldr x9, [x9] + cbz x9, 1f + mov sp, x9 +1: +#ifdef CONFIG_PBL_BREAK + brk #17 + nop +#else + nop + nop +#endif + b __pbl_board_entry + .org 0x20 + .asciz "barebox" + .word 0xffffffff + .word _barebox_image_size /* image size to copy */ + .rept 8 + .word 0x55555555 + .endr +ENDPROC(__barebox_arm64_head) diff --git a/arch/arm/cpu/hyp.S b/arch/arm/cpu/hyp.S index 1314b56eab..b5e4807877 100644 --- a/arch/arm/cpu/hyp.S +++ b/arch/arm/cpu/hyp.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <asm/system.h> #include <asm/opcodes-virt.h> diff --git a/arch/arm/cpu/interrupts.c b/arch/arm/cpu/interrupts_32.c index b9b91f3153..468dcdd30e 100644 --- a/arch/arm/cpu/interrupts.c +++ b/arch/arm/cpu/interrupts_32.c @@ -1,21 +1,5 @@ -/* - * interrupts.c - Interrupt Support Routines - * - * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix /** * @file @@ -24,7 +8,9 @@ #include <common.h> #include <abort.h> +#include <linux/sizes.h> #include <asm/ptrace.h> +#include <asm/barebox-arm.h> #include <asm/unwind.h> #include <init.h> @@ -84,7 +70,7 @@ static void __noreturn do_exception(struct pt_regs *pt_regs) { show_regs(pt_regs); - panic(""); + panic_no_stacktrace(""); } /** @@ -122,6 +108,22 @@ void do_prefetch_abort (struct pt_regs *pt_regs) do_exception(pt_regs); } +static const char *data_abort_reason(ulong far) +{ + ulong guard_page; + + if (far < PAGE_SIZE) + return "NULL pointer dereference"; + + if (IS_ENABLED(CONFIG_STACK_GUARD_PAGE)) { + guard_page = arm_mem_guard_page_get(); + if (guard_page <= far && far < guard_page + PAGE_SIZE) + return "stack overflow"; + } + + return "paging request"; +} + /** * The CPU catches a data abort. That really should not happen! * @param[in] pt_regs Register set content when the accident happens @@ -135,8 +137,7 @@ void do_data_abort (struct pt_regs *pt_regs) asm volatile ("mrc p15, 0, %0, c6, c0, 0" : "=r" (far) : : "cc"); printf("unable to handle %s at address 0x%08x\n", - far < PAGE_SIZE ? "NULL pointer dereference" : - "paging request", far); + data_abort_reason(far), far); do_exception(pt_regs); } diff --git a/arch/arm/cpu/interrupts_64.c b/arch/arm/cpu/interrupts_64.c index e8475d2e47..6262ba8872 100644 --- a/arch/arm/cpu/interrupts_64.c +++ b/arch/arm/cpu/interrupts_64.c @@ -1,29 +1,17 @@ -/* - * interrupts_64.c - Interrupt Support Routines - * - * Copyright (c) 2018 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2018 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + +/* interrupts_64.c - Interrupt Support Routines */ #include <common.h> #include <abort.h> #include <asm/ptrace.h> +#include <asm/barebox-arm.h> #include <asm/unwind.h> #include <init.h> #include <asm/system.h> #include <asm/esr.h> +#include <efi/efi-mode.h> /* Avoid missing prototype warning, called from assembly */ void do_bad_sync (struct pt_regs *pt_regs); @@ -102,7 +90,7 @@ static void __noreturn do_exception(struct pt_regs *pt_regs) unwind_backtrace(pt_regs); - panic("panic: unhandled exception"); + panic_no_stacktrace("panic: unhandled exception"); } /** @@ -156,17 +144,38 @@ void do_bad_error(struct pt_regs *pt_regs) extern volatile int arm_ignore_data_abort; extern volatile int arm_data_abort_occurred; +static const char *data_abort_reason(ulong far) +{ + ulong guard_page; + + if (far < PAGE_SIZE) + return "NULL pointer dereference: "; + + if (IS_ENABLED(CONFIG_STACK_GUARD_PAGE)) { + guard_page = arm_mem_guard_page_get(); + if (guard_page <= far && far < guard_page + PAGE_SIZE) + return "Stack overflow: "; + } + + return NULL; +} + void do_sync(struct pt_regs *pt_regs, unsigned int esr, unsigned long far) { - if ((esr >> ESR_ELx_EC_SHIFT) == ESR_ELx_EC_DABT_CUR && - arm_ignore_data_abort) { - arm_data_abort_occurred = 1; - pt_regs->elr += 4; - return; + const char *extra = NULL; + + if ((esr >> ESR_ELx_EC_SHIFT) == ESR_ELx_EC_DABT_CUR) { + if (arm_ignore_data_abort) { + arm_data_abort_occurred = 1; + pt_regs->elr += 4; + return; + } + + extra = data_abort_reason(far); } - printf("%s exception (ESR 0x%08x) at 0x%016lx\n", esr_get_class_string(esr), - esr, far); + printf("%s%s exception (ESR 0x%08x) at 0x%016lx\n", extra ?: "", + esr_get_class_string(esr), esr, far); do_exception(pt_regs); } @@ -194,16 +203,26 @@ extern unsigned long vectors; static int aarch64_init_vectors(void) { - unsigned int el; - - el = current_el(); - if (el == 1) - asm volatile("msr vbar_el1, %0" : : "r" (&vectors) : "cc"); - else if (el == 2) - asm volatile("msr vbar_el2, %0" : : "r" (&vectors) : "cc"); - else - asm volatile("msr vbar_el3, %0" : : "r" (&vectors) : "cc"); + unsigned int el; + + if (efi_is_payload()) + return 0; + + el = current_el(); + switch (el) { + case 3: + asm volatile("msr vbar_el3, %0" : : "r" (&vectors) : "cc"); + /* Fall through */ + case 2: + asm volatile("msr vbar_el2, %0" : : "r" (&vectors) : "cc"); + /* Fall through */ + case 1: + asm volatile("msr vbar_el1, %0" : : "r" (&vectors) : "cc"); + /* Fall through */ + default: + break; + } return 0; } -pure_initcall(aarch64_init_vectors); +core_initcall(aarch64_init_vectors); diff --git a/arch/arm/cpu/lowlevel.S b/arch/arm/cpu/lowlevel_32.S index 203a4afc47..960a92b78c 100644 --- a/arch/arm/cpu/lowlevel.S +++ b/arch/arm/cpu/lowlevel_32.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> #include <asm/system.h> @@ -7,6 +9,8 @@ ENTRY(arm_cpu_lowlevel_init) /* save lr, since it may be banked away with a processor mode change */ mov r2, lr + /* save sp, because possible HYP -> SVC transition below clobbers it */ + mov r3, sp #ifdef CONFIG_CPU_32v7 /* careful: the hyp install corrupts r0 and r1 */ @@ -75,6 +79,7 @@ THUMB( orr r12, r12, #PSR_T_BIT ) mcr p15, 0, r12, c1, c0, 0 /* SCTLR */ + mov sp, r3 mov pc, r2 ENDPROC(arm_cpu_lowlevel_init) diff --git a/arch/arm/cpu/lowlevel_64.S b/arch/arm/cpu/lowlevel_64.S index 6a23132ed1..ed00c8c470 100644 --- a/arch/arm/cpu/lowlevel_64.S +++ b/arch/arm/cpu/lowlevel_64.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <init.h> #include <asm/system.h> diff --git a/arch/arm/cpu/mmu-common.c b/arch/arm/cpu/mmu-common.c index 287622b203..aeaf6c269d 100644 --- a/arch/arm/cpu/mmu-common.c +++ b/arch/arm/cpu/mmu-common.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #define pr_fmt(fmt) "mmu: " fmt @@ -7,56 +9,17 @@ #include <dma.h> #include <mmu.h> #include <asm/system.h> +#include <asm/barebox-arm.h> #include <memory.h> -#include "mmu.h" - +#include <zero_page.h> +#include "mmu-common.h" +#include <efi/efi-mode.h> -static inline dma_addr_t cpu_to_dma(struct device_d *dev, unsigned long cpu_addr) +void arch_sync_dma_for_cpu(void *vaddr, size_t size, + enum dma_data_direction dir) { - dma_addr_t dma_addr = cpu_addr; - - if (dev) - dma_addr -= dev->dma_offset; - - return dma_addr; -} - -static inline unsigned long dma_to_cpu(struct device_d *dev, dma_addr_t addr) -{ - unsigned long cpu_addr = addr; - - if (dev) - cpu_addr += dev->dma_offset; - - return cpu_addr; -} - -void dma_sync_single_for_cpu(dma_addr_t address, size_t size, - enum dma_data_direction dir) -{ - /* - * FIXME: This function needs a device argument to support non 1:1 mappings - */ if (dir != DMA_TO_DEVICE) - dma_inv_range((void *)address, size); -} - -dma_addr_t dma_map_single(struct device_d *dev, void *ptr, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr = (unsigned long)ptr; - - dma_sync_single_for_device(addr, size, dir); - - return cpu_to_dma(dev, addr); -} - -void dma_unmap_single(struct device_d *dev, dma_addr_t dma_addr, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr = dma_to_cpu(dev, dma_addr); - - dma_sync_single_for_cpu(addr, size, dir); + dma_inv_range(vaddr, size); } void *dma_alloc_map(size_t size, dma_addr_t *dma_handle, unsigned flags) @@ -71,7 +34,7 @@ void *dma_alloc_map(size_t size, dma_addr_t *dma_handle, unsigned flags) memset(ret, 0, size); dma_flush_range(ret, size); - arch_remap_range(ret, size, flags); + remap_range(ret, size, flags); return ret; } @@ -88,24 +51,47 @@ void *dma_alloc_coherent(size_t size, dma_addr_t *dma_handle) void dma_free_coherent(void *mem, dma_addr_t dma_handle, size_t size) { size = PAGE_ALIGN(size); - arch_remap_range(mem, size, MAP_CACHED); + remap_range(mem, size, MAP_CACHED); free(mem); } +void zero_page_access(void) +{ + remap_range(0x0, PAGE_SIZE, MAP_CACHED); +} + +void zero_page_faulting(void) +{ + remap_range(0x0, PAGE_SIZE, MAP_FAULT); +} + static int mmu_init(void) { - if (list_empty(&memory_banks)) + if (efi_is_payload()) + return 0; + + if (list_empty(&memory_banks)) { + resource_size_t start; + int ret; + /* * If you see this it means you have no memory registered. * This can be done either with arm_add_mem_device() in an * initcall prior to mmu_initcall or via devicetree in the * memory node. */ - panic("MMU: No memory bank found! Cannot continue\n"); + pr_emerg("No memory bank registered. Limping along with initial memory\n"); + + start = arm_mem_membase_get(); + ret = barebox_add_memory_bank("initmem", start, + arm_mem_endmem_get() - start); + if (ret) + panic(""); + } __mmu_init(get_cr() & CR_M); return 0; } -mmu_initcall(mmu_init);
\ No newline at end of file +mmu_initcall(mmu_init); diff --git a/arch/arm/cpu/mmu-common.h b/arch/arm/cpu/mmu-common.h index 0a33b138e1..7a69122ee6 100644 --- a/arch/arm/cpu/mmu-common.h +++ b/arch/arm/cpu/mmu-common.h @@ -1,6 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #ifndef __ARM_MMU_COMMON_H #define __ARM_MMU_COMMON_H +#include <printk.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/sizes.h> + void dma_inv_range(void *ptr, size_t size); void dma_flush_range(void *ptr, size_t size); void *dma_alloc_map(size_t size, dma_addr_t *dma_handle, unsigned flags); @@ -17,4 +25,14 @@ static inline void arm_mmu_not_initialized_error(void) panic("MMU not initialized\n"); } -#endif
\ No newline at end of file +static inline size_t resource_first_page(const struct resource *res) +{ + return ALIGN_DOWN(res->start, SZ_4K); +} + +static inline size_t resource_count_pages(const struct resource *res) +{ + return ALIGN(resource_size(res), SZ_4K); +} + +#endif diff --git a/arch/arm/cpu/mmu-early.c b/arch/arm/cpu/mmu-early.c deleted file mode 100644 index b985aa455f..0000000000 --- a/arch/arm/cpu/mmu-early.c +++ /dev/null @@ -1,69 +0,0 @@ -#include <common.h> -#include <asm/mmu.h> -#include <errno.h> -#include <linux/sizes.h> -#include <asm/memory.h> -#include <asm/system.h> -#include <asm/cache.h> -#include <asm-generic/sections.h> - -#include "mmu.h" - -static uint32_t *ttb; - -static inline void map_region(unsigned long start, unsigned long size, - uint64_t flags) - -{ - start = ALIGN_DOWN(start, SZ_1M); - size = ALIGN(size, SZ_1M); - - create_sections(ttb, start, start + size - 1, flags); -} - -void mmu_early_enable(unsigned long membase, unsigned long memsize, - unsigned long _ttb) -{ - ttb = (uint32_t *)_ttb; - - arm_set_cache_functions(); - - set_ttbr(ttb); - - /* For the XN bit to take effect, we can't be using DOMAIN_MANAGER. */ - if (cpu_architecture() >= CPU_ARCH_ARMv7) - set_domain(DOMAIN_CLIENT); - else - set_domain(DOMAIN_MANAGER); - - /* - * This marks the whole address space as uncachable as well as - * unexecutable if possible - */ - create_flat_mapping(ttb); - - /* - * There can be SoCs that have a section shared between device memory - * and the on-chip RAM hosting the PBL. Thus mark this section - * uncachable, but executable. - * On such SoCs, executing from OCRAM could cause the instruction - * prefetcher to speculatively access that device memory, triggering - * potential errant behavior. - * - * If your SoC has such a memory layout, you should rewrite the code - * here to map the OCRAM page-wise. - */ - map_region((unsigned long)_stext, _etext - _stext, PMD_SECT_DEF_UNCACHED); - - /* maps main memory as cachable */ - map_region(membase, memsize, PMD_SECT_DEF_CACHED); - - /* - * With HAB enabled we call into the ROM code later in imx6_hab_get_status(). - * Map the ROM cached which has the effect that the XN bit is not set. - */ - if (IS_ENABLED(CONFIG_HABV4) && IS_ENABLED(CONFIG_ARCH_IMX6)) - map_region(0x0, SZ_1M, PMD_SECT_DEF_CACHED); - - __mmu_cache_on(); -} diff --git a/arch/arm/cpu/mmu-early_64.c b/arch/arm/cpu/mmu-early_64.c deleted file mode 100644 index 94e372637a..0000000000 --- a/arch/arm/cpu/mmu-early_64.c +++ /dev/null @@ -1,90 +0,0 @@ -#include <common.h> -#include <dma-dir.h> -#include <init.h> -#include <mmu.h> -#include <errno.h> -#include <linux/sizes.h> -#include <asm/memory.h> -#include <asm/pgtable64.h> -#include <asm/barebox-arm.h> -#include <asm/system.h> -#include <asm/cache.h> -#include <memory.h> -#include <asm/system_info.h> - -#include "mmu_64.h" - -static void create_sections(void *ttb, uint64_t virt, uint64_t phys, - uint64_t size, uint64_t attr) -{ - uint64_t block_size; - uint64_t block_shift; - uint64_t *pte; - uint64_t idx; - uint64_t addr; - uint64_t *table; - - addr = virt; - - attr &= ~PTE_TYPE_MASK; - - table = ttb; - - while (1) { - block_shift = level2shift(1); - idx = (addr & level2mask(1)) >> block_shift; - block_size = (1ULL << block_shift); - - pte = table + idx; - - *pte = phys | attr | PTE_TYPE_BLOCK; - - if (size < block_size) - break; - - addr += block_size; - phys += block_size; - size -= block_size; - } -} - -#define EARLY_BITS_PER_VA 39 - -void mmu_early_enable(unsigned long membase, unsigned long memsize, - unsigned long ttb) -{ - int el; - - /* - * For the early code we only create level 1 pagetables which only - * allow for a 1GiB granularity. If our membase is not aligned to that - * bail out without enabling the MMU. - */ - if (membase & ((1ULL << level2shift(1)) - 1)) - return; - - memset((void *)ttb, 0, GRANULE_SIZE); - - el = current_el(); - set_ttbr_tcr_mair(el, ttb, calc_tcr(el, EARLY_BITS_PER_VA), MEMORY_ATTRIBUTES); - create_sections((void *)ttb, 0, 0, 1UL << (EARLY_BITS_PER_VA - 1), UNCACHED_MEM); - create_sections((void *)ttb, membase, membase, memsize, CACHED_MEM); - tlb_invalidate(); - isb(); - set_cr(get_cr() | CR_M); -} - -void mmu_early_disable(void) -{ - unsigned int cr; - - cr = get_cr(); - cr &= ~(CR_M | CR_C); - - set_cr(cr); - v8_flush_dcache_all(); - tlb_invalidate(); - - dsb(); - isb(); -}
\ No newline at end of file diff --git a/arch/arm/cpu/mmu.c b/arch/arm/cpu/mmu_32.c index 1f97c28ec6..3a8d025ecd 100644 --- a/arch/arm/cpu/mmu.c +++ b/arch/arm/cpu/mmu_32.c @@ -1,19 +1,5 @@ -/* - * Copyright (c) 2009-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2009-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix #define pr_fmt(fmt) "mmu: " fmt @@ -23,6 +9,7 @@ #include <init.h> #include <mmu.h> #include <errno.h> +#include <zero_page.h> #include <linux/sizes.h> #include <asm/memory.h> #include <asm/barebox-arm.h> @@ -31,13 +18,18 @@ #include <memory.h> #include <asm/system_info.h> #include <asm/sections.h> +#include <linux/pagemap.h> -#include "mmu.h" +#include "mmu_32.h" #define PTRS_PER_PTE (PGDIR_SIZE / PAGE_SIZE) #define ARCH_MAP_WRITECOMBINE ((unsigned)-1) -static uint32_t *ttb; +static inline uint32_t *get_ttb(void) +{ + /* Clear unpredictable bits [13:0] */ + return (uint32_t *)(get_ttbr() & ~0x3fff); +} /* * Do it the simple way for now and invalidate the entire @@ -66,29 +58,36 @@ static inline void tlb_invalidate(void) PMD_SECT_BUFFERABLE | PMD_SECT_XN) #define PGD_FLAGS_UNCACHED_V7 (PMD_SECT_DEF_UNCACHED | PMD_SECT_XN) -/* - * PTE flags to set cached and uncached areas. - * This will be determined at runtime. - */ -static uint32_t pte_flags_cached; -static uint32_t pte_flags_wc; -static uint32_t pte_flags_uncached; -static uint32_t pgd_flags_wc; -static uint32_t pgd_flags_uncached; - -#define PTE_MASK ((1 << 12) - 1) - static bool pgd_type_table(u32 pgd) { return (pgd & PMD_TYPE_MASK) == PMD_TYPE_TABLE; } +#define PTE_SIZE (PTRS_PER_PTE * sizeof(u32)) + +#ifdef __PBL__ +static uint32_t *alloc_pte(void) +{ + static unsigned int idx = 3; + + idx++; + + if (idx * PTE_SIZE >= ARM_EARLY_PAGETABLE_SIZE) + return NULL; + + return get_ttb() + idx * PTE_SIZE; +} +#else +static uint32_t *alloc_pte(void) +{ + return xmemalign(PTE_SIZE, PTE_SIZE); +} +#endif + static u32 *find_pte(unsigned long adr) { u32 *table; - - if (!ttb) - arm_mmu_not_initialized_error(); + uint32_t *ttb = get_ttb(); if (!pgd_type_table(ttb[pgd_index(adr)])) return NULL; @@ -106,6 +105,7 @@ void dma_flush_range(void *ptr, size_t size) unsigned long end = start + size; __dma_flush_range(start, end); + if (outer_cache.flush_range) outer_cache.flush_range(start, end); } @@ -117,6 +117,7 @@ void dma_inv_range(void *ptr, size_t size) if (outer_cache.inv_range) outer_cache.inv_range(start, end); + __dma_inv_range(start, end); } @@ -125,24 +126,24 @@ void dma_inv_range(void *ptr, size_t size) * We initially create a flat uncached mapping on it. * Not yet exported, but may be later if someone finds use for it. */ -static u32 *arm_create_pte(unsigned long virt, uint32_t flags) +static u32 *arm_create_pte(unsigned long virt, unsigned long phys, + uint32_t flags) { + uint32_t *ttb = get_ttb(); u32 *table; int i, ttb_idx; virt = ALIGN_DOWN(virt, PGDIR_SIZE); + phys = ALIGN_DOWN(phys, PGDIR_SIZE); - table = xmemalign(PTRS_PER_PTE * sizeof(u32), - PTRS_PER_PTE * sizeof(u32)); - - if (!ttb) - arm_mmu_not_initialized_error(); + table = alloc_pte(); ttb_idx = pgd_index(virt); for (i = 0; i < PTRS_PER_PTE; i++) { - table[i] = virt | PTE_TYPE_SMALL | flags; + table[i] = phys | PTE_TYPE_SMALL | flags; virt += PAGE_SIZE; + phys += PAGE_SIZE; } dma_flush_range(table, PTRS_PER_PTE * sizeof(u32)); @@ -152,44 +153,125 @@ static u32 *arm_create_pte(unsigned long virt, uint32_t flags) return table; } -int arch_remap_range(void *start, size_t size, unsigned flags) +static u32 pmd_flags_to_pte(u32 pmd) +{ + u32 pte = 0; + + if (pmd & PMD_SECT_BUFFERABLE) + pte |= PTE_BUFFERABLE; + if (pmd & PMD_SECT_CACHEABLE) + pte |= PTE_CACHEABLE; + + if (cpu_architecture() >= CPU_ARCH_ARMv7) { + if (pmd & PMD_SECT_nG) + pte |= PTE_EXT_NG; + if (pmd & PMD_SECT_XN) + pte |= PTE_EXT_XN; + + /* TEX[2:0] */ + pte |= PTE_EXT_TEX((pmd >> 12) & 7); + /* AP[1:0] */ + pte |= ((pmd >> 10) & 0x3) << 4; + /* AP[2] */ + pte |= ((pmd >> 15) & 0x1) << 9; + } else { + pte |= PTE_SMALL_AP_UNO_SRW; + } + + return pte; +} + +static u32 pte_flags_to_pmd(u32 pte) { - u32 addr = (u32)start; - u32 pte_flags; - u32 pgd_flags; - - BUG_ON(!IS_ALIGNED(addr, PAGE_SIZE)); - - switch (flags) { - case MAP_CACHED: - pte_flags = pte_flags_cached; - pgd_flags = PMD_SECT_DEF_CACHED; - break; - case MAP_UNCACHED: - pte_flags = pte_flags_uncached; - pgd_flags = pgd_flags_uncached; - break; - case ARCH_MAP_WRITECOMBINE: - pte_flags = pte_flags_wc; - pgd_flags = pgd_flags_wc; - break; - default: - return -EINVAL; + u32 pmd = 0; + + if (pte & PTE_BUFFERABLE) + pmd |= PMD_SECT_BUFFERABLE; + if (pte & PTE_CACHEABLE) + pmd |= PMD_SECT_CACHEABLE; + + if (cpu_architecture() >= CPU_ARCH_ARMv7) { + if (pte & PTE_EXT_NG) + pmd |= PMD_SECT_nG; + if (pte & PTE_EXT_XN) + pmd |= PMD_SECT_XN; + + /* TEX[2:0] */ + pmd |= ((pte >> 6) & 7) << 12; + /* AP[1:0] */ + pmd |= ((pte >> 4) & 0x3) << 10; + /* AP[2] */ + pmd |= ((pte >> 9) & 0x1) << 15; + } else { + pmd |= PMD_SECT_AP_WRITE | PMD_SECT_AP_READ; } + return pmd; +} + +static uint32_t get_pte_flags(int map_type) +{ + if (cpu_architecture() >= CPU_ARCH_ARMv7) { + switch (map_type) { + case MAP_CACHED: + return PTE_FLAGS_CACHED_V7; + case MAP_UNCACHED: + return PTE_FLAGS_UNCACHED_V7; + case ARCH_MAP_WRITECOMBINE: + return PTE_FLAGS_WC_V7; + case MAP_FAULT: + default: + return 0x0; + } + } else { + switch (map_type) { + case MAP_CACHED: + return PTE_FLAGS_CACHED_V4; + case MAP_UNCACHED: + case ARCH_MAP_WRITECOMBINE: + return PTE_FLAGS_UNCACHED_V4; + case MAP_FAULT: + default: + return 0x0; + } + } +} + +static uint32_t get_pmd_flags(int map_type) +{ + return pte_flags_to_pmd(get_pte_flags(map_type)); +} + +static void __arch_remap_range(void *_virt_addr, phys_addr_t phys_addr, size_t size, unsigned map_type) +{ + u32 virt_addr = (u32)_virt_addr; + u32 pte_flags, pmd_flags; + uint32_t *ttb = get_ttb(); + + BUG_ON(!IS_ALIGNED(virt_addr, PAGE_SIZE)); + BUG_ON(!IS_ALIGNED(phys_addr, PAGE_SIZE)); + + pte_flags = get_pte_flags(map_type); + pmd_flags = pte_flags_to_pmd(pte_flags); + + size = PAGE_ALIGN(size); + while (size) { - const bool pgdir_size_aligned = IS_ALIGNED(addr, PGDIR_SIZE); - u32 *pgd = (u32 *)&ttb[pgd_index(addr)]; + const bool pgdir_size_aligned = IS_ALIGNED(virt_addr, PGDIR_SIZE); + u32 *pgd = (u32 *)&ttb[pgd_index(virt_addr)]; size_t chunk; if (size >= PGDIR_SIZE && pgdir_size_aligned && + IS_ALIGNED(phys_addr, PGDIR_SIZE) && !pgd_type_table(*pgd)) { /* * TODO: Add code to discard a page table and * replace it with a section */ chunk = PGDIR_SIZE; - *pgd = addr | pgd_flags; + *pgd = phys_addr | pmd_flags; + if (map_type != MAP_FAULT) + *pgd |= PMD_TYPE_SECT; dma_flush_range(pgd, sizeof(*pgd)); } else { unsigned int num_ptes; @@ -204,7 +286,7 @@ int arch_remap_range(void *start, size_t size, unsigned flags) * was not aligned on PGDIR_SIZE boundary) */ chunk = pgdir_size_aligned ? - PGDIR_SIZE : ALIGN(addr, PGDIR_SIZE) - addr; + PGDIR_SIZE : ALIGN(virt_addr, PGDIR_SIZE) - virt_addr; /* * At the same time we want to make sure that * we don't go on remapping past requested @@ -214,43 +296,78 @@ int arch_remap_range(void *start, size_t size, unsigned flags) chunk = min(chunk, size); num_ptes = chunk / PAGE_SIZE; - pte = find_pte(addr); + pte = find_pte(virt_addr); if (!pte) { /* * If PTE is not found it means that * we needs to split this section and * create a new page table for it - * - * NOTE: Here we assume that section - * we just split was mapped as cached */ - table = arm_create_pte(addr, pte_flags_cached); - pte = find_pte(addr); + table = arm_create_pte(virt_addr, phys_addr, + pmd_flags_to_pte(*pgd)); + pte = find_pte(virt_addr); BUG_ON(!pte); } for (i = 0; i < num_ptes; i++) { - pte[i] &= ~PTE_MASK; - pte[i] |= pte_flags | PTE_TYPE_SMALL; + pte[i] = phys_addr + i * PAGE_SIZE; + pte[i] |= pte_flags; + if (map_type != MAP_FAULT) + pte[i] |= PTE_TYPE_SMALL; } dma_flush_range(pte, num_ptes * sizeof(u32)); } - addr += chunk; + virt_addr += chunk; + phys_addr += chunk; size -= chunk; } tlb_invalidate(); +} +static void early_remap_range(u32 addr, size_t size, unsigned map_type) +{ + __arch_remap_range((void *)addr, addr, size, map_type); +} + +int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size, unsigned map_type) +{ + __arch_remap_range(virt_addr, phys_addr, size, map_type); + + if (map_type == MAP_UNCACHED) + dma_inv_range(virt_addr, size); + return 0; } +static void create_sections(unsigned long first, unsigned long last, + unsigned int flags) +{ + uint32_t *ttb = get_ttb(); + unsigned long ttb_start = pgd_index(first); + unsigned long ttb_end = pgd_index(last) + 1; + unsigned int i, addr = first; + + for (i = ttb_start; i < ttb_end; i++) { + ttb[i] = addr | flags; + addr += PGDIR_SIZE; + } +} + +static inline void create_flat_mapping(void) +{ + /* create a flat mapping using 1MiB sections */ + create_sections(0, 0xffffffff, attrs_uncached_mem()); +} + void *map_io_sections(unsigned long phys, void *_start, size_t size) { unsigned long start = (unsigned long)_start, sec; + uint32_t *ttb = get_ttb(); for (sec = start; sec < start + size; sec += PGDIR_SIZE, phys += PGDIR_SIZE) - ttb[pgd_index(sec)] = phys | pgd_flags_uncached; + ttb[pgd_index(sec)] = phys | get_pmd_flags(MAP_UNCACHED); dma_flush_range(ttb, 0x4000); tlb_invalidate(); @@ -291,9 +408,9 @@ static void create_vector_table(unsigned long adr) vectors = xmemalign(PAGE_SIZE, PAGE_SIZE); pr_debug("Creating vector table, virt = 0x%p, phys = 0x%08lx\n", vectors, adr); - arm_create_pte(adr, pte_flags_uncached); + arm_create_pte(adr, adr, get_pte_flags(MAP_UNCACHED)); pte = find_pte(adr); - *pte = (u32)vectors | PTE_TYPE_SMALL | pte_flags_cached; + *pte = (u32)vectors | PTE_TYPE_SMALL | get_pte_flags(MAP_CACHED); } arm_fixup_vectors(); @@ -351,21 +468,28 @@ static int set_vector_table(unsigned long adr) static void create_zero_page(void) { - struct resource *zero_sdram; - u32 *zero; + /* + * In case the zero page is in SDRAM request it to prevent others + * from using it + */ + request_sdram_region("zero page", 0x0, PAGE_SIZE); - zero_sdram = request_sdram_region("zero page", 0x0, PAGE_SIZE); - if (zero_sdram) { - /* - * Here we would need to set the second level page table - * entry to faulting. This is not yet implemented. - */ - pr_debug("zero page is in SDRAM area, currently not supported\n"); - } else { - zero = arm_create_pte(0x0, pte_flags_uncached); - zero[0] = 0; - pr_debug("Created zero page\n"); - } + zero_page_faulting(); + pr_debug("Created zero page\n"); +} + +static void create_guard_page(void) +{ + ulong guard_page; + + if (!IS_ENABLED(CONFIG_STACK_GUARD_PAGE)) + return; + + guard_page = arm_mem_guard_page_get(); + request_sdram_region("guard page", guard_page, PAGE_SIZE); + remap_range((void *)guard_page, PAGE_SIZE, MAP_FAULT); + + pr_debug("Created guard page\n"); } /* @@ -373,6 +497,8 @@ static void create_zero_page(void) */ static void vectors_init(void) { + create_guard_page(); + /* * First try to use the vectors where they actually are, works * on ARMv7 and later. @@ -407,67 +533,44 @@ static void vectors_init(void) void __mmu_init(bool mmu_on) { struct memory_bank *bank; + uint32_t *ttb = get_ttb(); - arm_set_cache_functions(); - - if (cpu_architecture() >= CPU_ARCH_ARMv7) { - pte_flags_cached = PTE_FLAGS_CACHED_V7; - pte_flags_wc = PTE_FLAGS_WC_V7; - pgd_flags_wc = PGD_FLAGS_WC_V7; - pgd_flags_uncached = PGD_FLAGS_UNCACHED_V7; - pte_flags_uncached = PTE_FLAGS_UNCACHED_V7; - } else { - pte_flags_cached = PTE_FLAGS_CACHED_V4; - pte_flags_wc = PTE_FLAGS_UNCACHED_V4; - pgd_flags_wc = PMD_SECT_DEF_UNCACHED; - pgd_flags_uncached = PMD_SECT_DEF_UNCACHED; - pte_flags_uncached = PTE_FLAGS_UNCACHED_V4; - } - - if (mmu_on) { + if (!request_sdram_region("ttb", (unsigned long)ttb, + ARM_EARLY_PAGETABLE_SIZE)) /* - * Early MMU code has already enabled the MMU. We assume a - * flat 1:1 section mapping in this case. + * This can mean that: + * - the early MMU code has put the ttb into a place + * which we don't have inside our available memory + * - Somebody else has occupied the ttb region which means + * the ttb will get corrupted. */ - /* Clear unpredictable bits [13:0] */ - ttb = (uint32_t *)(get_ttbr() & ~0x3fff); - - if (!request_sdram_region("ttb", (unsigned long)ttb, SZ_16K)) - /* - * This can mean that: - * - the early MMU code has put the ttb into a place - * which we don't have inside our available memory - * - Somebody else has occupied the ttb region which means - * the ttb will get corrupted. - */ - pr_crit("Critical Error: Can't request SDRAM region for ttb at %p\n", + pr_crit("Critical Error: Can't request SDRAM region for ttb at %p\n", ttb); - } else { - ttb = xmemalign(ARM_TTB_SIZE, ARM_TTB_SIZE); - - set_ttbr(ttb); - /* For the XN bit to take effect, we can't be using DOMAIN_MANAGER. */ - if (cpu_architecture() >= CPU_ARCH_ARMv7) - set_domain(DOMAIN_CLIENT); - else - set_domain(DOMAIN_MANAGER); + pr_debug("ttb: 0x%p\n", ttb); - create_flat_mapping(ttb); - __mmu_cache_flush(); - } + /* + * Early mmu init will have mapped everything but the initial memory area + * (excluding final OPTEE_SIZE bytes) uncached. We have now discovered + * all memory banks, so let's map all pages, excluding reserved memory areas, + * cacheable and executable. + */ + for_each_memory_bank(bank) { + struct resource *rsv; + resource_size_t pos; - pr_debug("ttb: 0x%p\n", ttb); + pos = bank->start; - vectors_init(); + /* Skip reserved regions */ + for_each_reserved_region(bank, rsv) { + remap_range((void *)pos, rsv->start - pos, MAP_CACHED); + pos = rsv->end + 1; + } - for_each_memory_bank(bank) { - create_sections(ttb, bank->start, bank->start + bank->size - 1, - PMD_SECT_DEF_CACHED); - __mmu_cache_flush(); + remap_range((void *)pos, bank->start + bank->size - pos, MAP_CACHED); } - __mmu_cache_on(); + vectors_init(); } /* @@ -488,20 +591,33 @@ void *dma_alloc_writecombine(size_t size, dma_addr_t *dma_handle) return dma_alloc_map(size, dma_handle, ARCH_MAP_WRITECOMBINE); } -void dma_sync_single_for_device(dma_addr_t address, size_t size, - enum dma_data_direction dir) +void mmu_early_enable(unsigned long membase, unsigned long memsize) { + uint32_t *ttb = (uint32_t *)arm_mem_ttb(membase + memsize); + + pr_debug("enabling MMU, ttb @ 0x%p\n", ttb); + + if (get_cr() & CR_M) + return; + + set_ttbr(ttb); + + /* For the XN bit to take effect, we can't be using DOMAIN_MANAGER. */ + if (cpu_architecture() >= CPU_ARCH_ARMv7) + set_domain(DOMAIN_CLIENT); + else + set_domain(DOMAIN_MANAGER); + /* - * FIXME: This function needs a device argument to support non 1:1 mappings + * This marks the whole address space as uncachable as well as + * unexecutable if possible */ + create_flat_mapping(); - if (dir == DMA_FROM_DEVICE) { - __dma_inv_range(address, address + size); - if (outer_cache.inv_range) - outer_cache.inv_range(address, address + size); - } else { - __dma_clean_range(address, address + size); - if (outer_cache.clean_range) - outer_cache.clean_range(address, address + size); - } + /* maps main memory as cachable */ + early_remap_range(membase, memsize - OPTEE_SIZE, MAP_CACHED); + early_remap_range(membase + memsize - OPTEE_SIZE, OPTEE_SIZE, MAP_UNCACHED); + early_remap_range(PAGE_ALIGN_DOWN((uintptr_t)_stext), PAGE_ALIGN(_etext - _stext), MAP_CACHED); + + __mmu_cache_on(); } diff --git a/arch/arm/cpu/mmu.h b/arch/arm/cpu/mmu_32.h index 6e7a4c0350..607d9e8608 100644 --- a/arch/arm/cpu/mmu.h +++ b/arch/arm/cpu/mmu_32.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #ifndef __ARM_MMU_H #define __ARM_MMU_H @@ -39,38 +41,32 @@ static inline void set_ttbr(void *ttb) #define DOMAIN_CLIENT 1 #define DOMAIN_MANAGER 3 -static inline void set_domain(unsigned val) +static inline unsigned long get_domain(void) { - /* Set the Domain Access Control Register */ - asm volatile ("mcr p15,0,%0,c3,c0,0" : : "r"(val) /*:*/); + unsigned long dacr; + + asm volatile ("mrc p15, 0, %0, c3, c0, 0" : "=r"(dacr)); + + return dacr; } -static inline void -create_sections(uint32_t *ttb, unsigned long first, - unsigned long last, unsigned int flags) +static inline void set_domain(unsigned val) { - unsigned long ttb_start = pgd_index(first); - unsigned long ttb_end = pgd_index(last) + 1; - unsigned int i, addr = first; - - for (i = ttb_start; i < ttb_end; i++) { - ttb[i] = addr | flags; - addr += PGDIR_SIZE; - } + /* Set the Domain Access Control Register */ + asm volatile ("mcr p15,0,%0,c3,c0,0" : : "r"(val) /*:*/); } #define PMD_SECT_DEF_UNCACHED (PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT) #define PMD_SECT_DEF_CACHED (PMD_SECT_WB | PMD_SECT_DEF_UNCACHED) -static inline void create_flat_mapping(uint32_t *ttb) +static inline unsigned long attrs_uncached_mem(void) { unsigned int flags = PMD_SECT_DEF_UNCACHED; if (cpu_architecture() >= CPU_ARCH_ARMv7) flags |= PMD_SECT_XN; - /* create a flat mapping using 1MiB sections */ - create_sections(ttb, 0, 0xffffffff, flags); + return flags; } #endif /* __ARM_MMU_H */ diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c index 98cd4c754e..f907421da9 100644 --- a/arch/arm/cpu/mmu_64.c +++ b/arch/arm/cpu/mmu_64.c @@ -1,20 +1,6 @@ -/* - * Copyright (c) 2009-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * Copyright (c) 2016 Raphaël Poggi <poggi.raph@gmail.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2009-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix +// SPDX-FileCopyrightText: 2016 Raphaël Poggi <poggi.raph@gmail.com> #define pr_fmt(fmt) "mmu: " fmt @@ -24,6 +10,7 @@ #include <init.h> #include <mmu.h> #include <errno.h> +#include <zero_page.h> #include <linux/sizes.h> #include <asm/memory.h> #include <asm/pgtable64.h> @@ -32,10 +19,15 @@ #include <asm/cache.h> #include <memory.h> #include <asm/system_info.h> +#include <linux/pagemap.h> +#include <tee/optee.h> #include "mmu_64.h" -static uint64_t *ttb; +static uint64_t *get_ttb(void) +{ + return (uint64_t *)get_ttbr(current_el()); +} static void set_table(uint64_t *pt, uint64_t *table_addr) { @@ -45,7 +37,20 @@ static void set_table(uint64_t *pt, uint64_t *table_addr) *pt = val; } -static uint64_t *create_table(void) +#ifdef __PBL__ +static uint64_t *alloc_pte(void) +{ + static unsigned int idx; + + idx++; + + if (idx * GRANULE_SIZE >= ARM_EARLY_PAGETABLE_SIZE) + return NULL; + + return (void *)get_ttb() + idx * GRANULE_SIZE; +} +#else +static uint64_t *alloc_pte(void) { uint64_t *new_table = xmemalign(GRANULE_SIZE, GRANULE_SIZE); @@ -54,6 +59,7 @@ static uint64_t *create_table(void) return new_table; } +#endif static __maybe_unused uint64_t *find_pte(uint64_t addr) { @@ -62,7 +68,7 @@ static __maybe_unused uint64_t *find_pte(uint64_t addr) uint64_t idx; int i; - pte = ttb; + pte = get_ttb(); for (i = 0; i < 4; i++) { block_shift = level2shift(i); @@ -94,7 +100,10 @@ static void split_block(uint64_t *pte, int level) /* level describes the parent level, we need the child ones */ levelshift = level2shift(level + 1); - new_table = create_table(); + new_table = alloc_pte(); + if (!new_table) + panic("Unable to allocate PTE\n"); + for (i = 0; i < MAX_PTE_ENTRIES; i++) { new_table[i] = old_pte | (i << levelshift); @@ -111,6 +120,7 @@ static void split_block(uint64_t *pte, int level) static void create_sections(uint64_t virt, uint64_t phys, uint64_t size, uint64_t attr) { + uint64_t *ttb = get_ttb(); uint64_t block_size; uint64_t block_shift; uint64_t *pte; @@ -120,13 +130,12 @@ static void create_sections(uint64_t virt, uint64_t phys, uint64_t size, uint64_t type; int level; - if (!ttb) - arm_mmu_not_initialized_error(); - addr = virt; attr &= ~PTE_TYPE_MASK; + size = PAGE_ALIGN(size); + while (size) { table = ttb; for (level = 0; level < 4; level++) { @@ -136,7 +145,8 @@ static void create_sections(uint64_t virt, uint64_t phys, uint64_t size, pte = table + idx; - if (size >= block_size && IS_ALIGNED(addr, block_size)) { + if (size >= block_size && IS_ALIGNED(addr, block_size) && + IS_ALIGNED(phys, block_size)) { type = (level == 3) ? PTE_TYPE_PAGE : PTE_TYPE_BLOCK; *pte = phys | attr | type; @@ -156,21 +166,42 @@ static void create_sections(uint64_t virt, uint64_t phys, uint64_t size, tlb_invalidate(); } -int arch_remap_range(void *_start, size_t size, unsigned flags) +static unsigned long get_pte_attrs(unsigned flags) { switch (flags) { case MAP_CACHED: - flags = CACHED_MEM; - break; + return CACHED_MEM; case MAP_UNCACHED: - flags = UNCACHED_MEM; - break; + return attrs_uncached_mem(); + case MAP_FAULT: + return 0x0; default: - return -EINVAL; + return ~0UL; } +} + +static void early_remap_range(uint64_t addr, size_t size, unsigned flags) +{ + unsigned long attrs = get_pte_attrs(flags); + + if (WARN_ON(attrs == ~0UL)) + return; + + create_sections(addr, addr, size, attrs); +} + +int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size, unsigned flags) +{ + unsigned long attrs = get_pte_attrs(flags); + + if (attrs == ~0UL) + return -EINVAL; + + create_sections((uint64_t)virt_addr, phys_addr, (uint64_t)size, attrs); + + if (flags == MAP_UNCACHED) + dma_inv_range(virt_addr, size); - create_sections((uint64_t)_start, (uint64_t)_start, (uint64_t)size, - flags); return 0; } @@ -180,35 +211,57 @@ static void mmu_enable(void) set_cr(get_cr() | CR_M | CR_C | CR_I); } +static void create_guard_page(void) +{ + ulong guard_page; + + if (!IS_ENABLED(CONFIG_STACK_GUARD_PAGE)) + return; + + guard_page = arm_mem_guard_page_get(); + request_sdram_region("guard page", guard_page, PAGE_SIZE); + remap_range((void *)guard_page, PAGE_SIZE, MAP_FAULT); + + pr_debug("Created guard page\n"); +} + /* * Prepare MMU for usage enable it. */ void __mmu_init(bool mmu_on) { + uint64_t *ttb = get_ttb(); struct memory_bank *bank; - unsigned int el; - - if (mmu_on) - mmu_disable(); - - ttb = create_table(); - el = current_el(); - set_ttbr_tcr_mair(el, (uint64_t)ttb, calc_tcr(el, BITS_PER_VA), - MEMORY_ATTRIBUTES); - pr_debug("ttb: 0x%p\n", ttb); - - /* create a flat mapping */ - create_sections(0, 0, 1UL << (BITS_PER_VA - 1), UNCACHED_MEM); + if (!request_sdram_region("ttb", (unsigned long)ttb, + ARM_EARLY_PAGETABLE_SIZE)) + /* + * This can mean that: + * - the early MMU code has put the ttb into a place + * which we don't have inside our available memory + * - Somebody else has occupied the ttb region which means + * the ttb will get corrupted. + */ + pr_crit("Can't request SDRAM region for ttb at %p\n", ttb); + + for_each_memory_bank(bank) { + struct resource *rsv; + resource_size_t pos; + + pos = bank->start; + + /* Skip reserved regions */ + for_each_reserved_region(bank, rsv) { + remap_range((void *)pos, rsv->start - pos, MAP_CACHED); + pos = rsv->end + 1; + } - /* Map sdram cached. */ - for_each_memory_bank(bank) - create_sections(bank->start, bank->start, bank->size, CACHED_MEM); + remap_range((void *)pos, bank->start + bank->size - pos, MAP_CACHED); + } /* Make zero page faulting to catch NULL pointer derefs */ - create_sections(0x0, 0x0, 0x1000, 0x0); - - mmu_enable(); + zero_page_faulting(); + create_guard_page(); } void mmu_disable(void) @@ -242,15 +295,66 @@ void dma_flush_range(void *ptr, size_t size) v8_flush_dcache_range(start, end); } -void dma_sync_single_for_device(dma_addr_t address, size_t size, - enum dma_data_direction dir) +static void init_range(size_t total_level0_tables) +{ + uint64_t *ttb = get_ttb(); + uint64_t addr = 0; + + while (total_level0_tables--) { + early_remap_range(addr, L0_XLAT_SIZE, MAP_UNCACHED); + split_block(ttb, 0); + addr += L0_XLAT_SIZE; + ttb++; + } +} + +void mmu_early_enable(unsigned long membase, unsigned long memsize) { + int el; + u64 optee_membase; + unsigned long ttb = arm_mem_ttb(membase + memsize); + + if (get_cr() & CR_M) + return; + + pr_debug("enabling MMU, ttb @ 0x%08lx\n", ttb); + + el = current_el(); + set_ttbr_tcr_mair(el, ttb, calc_tcr(el, BITS_PER_VA), MEMORY_ATTRIBUTES); + if (el == 3) + set_ttbr_tcr_mair(2, ttb, calc_tcr(2, BITS_PER_VA), MEMORY_ATTRIBUTES); + + memset((void *)ttb, 0, GRANULE_SIZE); + /* - * FIXME: This function needs a device argument to support non 1:1 mappings + * Assume maximum BITS_PER_PA set to 40 bits. + * Set 1:1 mapping of VA->PA. So to cover the full 1TB range we need 2 tables. */ + init_range(2); + + early_remap_range(membase, memsize, MAP_CACHED); + + if (optee_get_membase(&optee_membase)) + optee_membase = membase + memsize - OPTEE_SIZE; - if (dir == DMA_FROM_DEVICE) - v8_inv_dcache_range(address, address + size - 1); - else - v8_flush_dcache_range(address, address + size - 1); + early_remap_range(optee_membase, OPTEE_SIZE, MAP_FAULT); + + early_remap_range(PAGE_ALIGN_DOWN((uintptr_t)_stext), PAGE_ALIGN(_etext - _stext), MAP_CACHED); + + mmu_enable(); +} + +void mmu_early_disable(void) +{ + unsigned int cr; + + cr = get_cr(); + cr &= ~(CR_M | CR_C); + + set_cr(cr); + v8_flush_dcache_all(); + tlb_invalidate(); + + dsb(); + isb(); } diff --git a/arch/arm/cpu/mmu_64.h b/arch/arm/cpu/mmu_64.h index a2a5477569..e3959e4407 100644 --- a/arch/arm/cpu/mmu_64.h +++ b/arch/arm/cpu/mmu_64.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "mmu-common.h" @@ -8,6 +9,23 @@ PTE_BLOCK_OUTER_SHARE | \ PTE_BLOCK_AF) +static inline unsigned long attrs_uncached_mem(void) +{ + unsigned long attrs = UNCACHED_MEM; + + switch (current_el()) { + case 3: + case 2: + attrs |= PTE_BLOCK_UXN; + break; + default: + attrs |= PTE_BLOCK_UXN | PTE_BLOCK_PXN; + break; + } + + return attrs; +} + /* * Do it the simple way for now and invalidate the entire tlb */ @@ -87,12 +105,27 @@ static inline uint64_t level2mask(int level) return mask; } +/** + * @brief Returns the TCR (Translation Control Register) value + * + * @param el - Exception Level + * @param va_bits - Virtual Address bits + * @return uint64_t TCR + */ static inline uint64_t calc_tcr(int el, int va_bits) { - u64 ips; - u64 tcr; + u64 ips; // Intermediate Physical Address Size + u64 tcr; // Translation Control Register +#if (BITS_PER_PA == 40) ips = 2; +#elif (BITS_PER_PA == 36) + ips = 1; +#elif (BITS_PER_PA == 32) + ips = 0; +#else +#error "Unsupported" +#endif if (el == 1) tcr = (ips << 32) | TCR_EPD1_DISABLE; diff --git a/arch/arm/cpu/mmuinfo.c b/arch/arm/cpu/mmuinfo.c index c7ec76cf7a..44d6980a75 100644 --- a/arch/arm/cpu/mmuinfo.c +++ b/arch/arm/cpu/mmuinfo.c @@ -1,104 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2012 Jan Luebbe <j.luebbe@pengutronix.de>, Pengutronix /* - * mmuinfo.c - Show MMU/cache information from cp15 registers - * - * Copyright (c) Jan Luebbe <j.luebbe@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * + * mmuinfo.c - Show MMU/cache information */ #include <common.h> #include <command.h> +#include <getopt.h> +#include <asm/mmuinfo.h> +#include <asm/system_info.h> +#include <zero_page.h> +#include <mmu.h> -static char *inner_attr[] = { - "0b000 Non-cacheable", - "0b001 Strongly-ordered", - "0b010 (reserved)", - "0b011 Device", - "0b100 (reserved)", - "0b101 Write-Back, Write-Allocate", - "0b110 Write-Through", - "0b111 Write-Back, no Write-Allocate", -}; - -static char *outer_attr[] = { - "0b00 Non-cacheable", - "0b01 Write-Back, Write-Allocate", - "0b10 Write-Through, no Write-Allocate", - "0b11 Write-Back, no Write-Allocate", -}; - -static void decode_par(unsigned long par) +int mmuinfo(void *addr) { - printf(" Physical Address [31:12]: 0x%08lx\n", par & 0xFFFFF000); - printf(" Reserved [11]: 0x%lx\n", (par >> 11) & 0x1); - printf(" Not Outer Shareable [10]: 0x%lx\n", (par >> 10) & 0x1); - printf(" Non-Secure [9]: 0x%lx\n", (par >> 9) & 0x1); - printf(" Impl. def. [8]: 0x%lx\n", (par >> 8) & 0x1); - printf(" Shareable [7]: 0x%lx\n", (par >> 7) & 0x1); - printf(" Inner mem. attr. [6:4]: 0x%lx (%s)\n", (par >> 4) & 0x7, - inner_attr[(par >> 4) & 0x7]); - printf(" Outer mem. attr. [3:2]: 0x%lx (%s)\n", (par >> 2) & 0x3, - outer_attr[(par >> 2) & 0x3]); - printf(" SuperSection [1]: 0x%lx\n", (par >> 1) & 0x1); - printf(" Failure [0]: 0x%lx\n", (par >> 0) & 0x1); + if (IS_ENABLED(CONFIG_CPU_V8)) + return mmuinfo_v8(addr); + if (IS_ENABLED(CONFIG_CPU_V7) && cpu_architecture() == CPU_ARCH_ARMv7) + return mmuinfo_v7(addr); + + return -ENOSYS; } -static int do_mmuinfo(int argc, char *argv[]) +static __maybe_unused int do_mmuinfo(int argc, char *argv[]) { - unsigned long addr = 0, priv_read, priv_write; + unsigned long addr; + int access_zero_page = -1; + int opt; - if (argc < 2) - return COMMAND_ERROR_USAGE; + while ((opt = getopt(argc, argv, "zZ")) > 0) { + switch (opt) { + case 'z': + access_zero_page = true; + break; + case 'Z': + access_zero_page = false; + break; + default: + return COMMAND_ERROR_USAGE; + } + } - addr = strtoul_suffix(argv[1], NULL, 0); + if (access_zero_page >= 0) { + if (argc - optind != 0) + return COMMAND_ERROR_USAGE; - __asm__ __volatile__( - "mcr p15, 0, %0, c7, c8, 0 @ write VA to PA translation (priv read)\n" - : - : "r" (addr) - : "memory"); + if (!zero_page_remappable()) { + pr_warn("No architecture support for zero page remap\n"); + return -ENOSYS; + } - __asm__ __volatile__( - "mrc p15, 0, %0, c7, c4, 0 @ read PAR\n" - : "=r" (priv_read) - : - : "memory"); + if (access_zero_page) + zero_page_access(); + else + zero_page_faulting(); - __asm__ __volatile__( - "mcr p15, 0, %0, c7, c8, 1 @ write VA to PA translation (priv write)\n" - : - : "r" (addr) - : "memory"); + return 0; + } - __asm__ __volatile__( - "mrc p15, 0, %0, c7, c4, 0 @ read PAR\n" - : "=r" (priv_write) - : - : "memory"); + if (argc - optind != 1) + return COMMAND_ERROR_USAGE; - printf("PAR result for 0x%08lx: \n", addr); - printf(" privileged read: 0x%08lx\n", priv_read); - decode_par(priv_read); - printf(" privileged write: 0x%08lx\n", priv_write); - decode_par(priv_write); + addr = strtoul_suffix(argv[1], NULL, 0); - return 0; + return mmuinfo((void *)addr); } +BAREBOX_CMD_HELP_START(mmuinfo) +BAREBOX_CMD_HELP_TEXT("Show MMU/cache information using the cp15/model-specific registers.") +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT ("-z", "enable access to zero page") +BAREBOX_CMD_HELP_OPT ("-Z", "disable access to zero page") +BAREBOX_CMD_HELP_END + +#ifdef CONFIG_COMMAND_SUPPORT BAREBOX_CMD_START(mmuinfo) .cmd = do_mmuinfo, BAREBOX_CMD_DESC("show MMU/cache information of an address") - BAREBOX_CMD_OPTS("ADDRESS") + BAREBOX_CMD_OPTS("[-zZ | ADDRESS]") BAREBOX_CMD_GROUP(CMD_GRP_INFO) + BAREBOX_CMD_HELP(cmd_mmuinfo_help) BAREBOX_CMD_END +#endif diff --git a/arch/arm/cpu/mmuinfo_32.c b/arch/arm/cpu/mmuinfo_32.c new file mode 100644 index 0000000000..e26dabc9b3 --- /dev/null +++ b/arch/arm/cpu/mmuinfo_32.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2012 Jan Luebbe <j.luebbe@pengutronix.de>, Pengutronix +/* + * mmuinfo_32.c - Show MMU/cache information from cp15 registers + */ + +#include <common.h> +#include <asm/mmuinfo.h> + +static char *inner_attr[] = { + "0b000 Non-cacheable", + "0b001 Strongly-ordered", + "0b010 (reserved)", + "0b011 Device", + "0b100 (reserved)", + "0b101 Write-Back, Write-Allocate", + "0b110 Write-Through", + "0b111 Write-Back, no Write-Allocate", +}; + +static char *outer_attr[] = { + "0b00 Non-cacheable", + "0b01 Write-Back, Write-Allocate", + "0b10 Write-Through, no Write-Allocate", + "0b11 Write-Back, no Write-Allocate", +}; + +static void decode_par(unsigned long par) +{ + printf(" Physical Address [31:12]: 0x%08lx\n", par & 0xFFFFF000); + printf(" Reserved [11]: 0x%lx\n", (par >> 11) & 0x1); + printf(" Not Outer Shareable [10]: 0x%lx\n", (par >> 10) & 0x1); + printf(" Non-Secure [9]: 0x%lx\n", (par >> 9) & 0x1); + printf(" Impl. def. [8]: 0x%lx\n", (par >> 8) & 0x1); + printf(" Shareable [7]: 0x%lx\n", (par >> 7) & 0x1); + printf(" Inner mem. attr. [6:4]: 0x%lx (%s)\n", (par >> 4) & 0x7, + inner_attr[(par >> 4) & 0x7]); + printf(" Outer mem. attr. [3:2]: 0x%lx (%s)\n", (par >> 2) & 0x3, + outer_attr[(par >> 2) & 0x3]); + printf(" SuperSection [1]: 0x%lx\n", (par >> 1) & 0x1); + printf(" Failure [0]: 0x%lx\n", (par >> 0) & 0x1); +} + +int mmuinfo_v7(void *_addr) +{ + unsigned long addr = (unsigned long)_addr; + unsigned long priv_read, priv_write; + + __asm__ __volatile__( + "mcr p15, 0, %0, c7, c8, 0 @ write VA to PA translation (priv read)\n" + : + : "r" (addr) + : "memory"); + + __asm__ __volatile__( + "mrc p15, 0, %0, c7, c4, 0 @ read PAR\n" + : "=r" (priv_read) + : + : "memory"); + + __asm__ __volatile__( + "mcr p15, 0, %0, c7, c8, 1 @ write VA to PA translation (priv write)\n" + : + : "r" (addr) + : "memory"); + + __asm__ __volatile__( + "mrc p15, 0, %0, c7, c4, 0 @ read PAR\n" + : "=r" (priv_write) + : + : "memory"); + + printf("PAR result for 0x%08lx: \n", addr); + printf(" privileged read: 0x%08lx\n", priv_read); + decode_par(priv_read); + printf(" privileged write: 0x%08lx\n", priv_write); + decode_par(priv_write); + + return 0; +} diff --git a/arch/arm/cpu/mmuinfo_64.c b/arch/arm/cpu/mmuinfo_64.c new file mode 100644 index 0000000000..de4945f43e --- /dev/null +++ b/arch/arm/cpu/mmuinfo_64.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2023 Ahmad Fatoum <a.fatoum@pengutronix.de>, Pengutronix +/* + * mmuinfo_64.c - Show MMU/cache information via AT instruction + */ + +#include <common.h> +#include <asm/mmuinfo.h> +#include <asm/system.h> +#include <asm/sysreg.h> +#include <linux/bitfield.h> + +#define at_par(reg, addr) ({ \ + asm volatile("at " reg ", %0\n" :: "r" (addr)); \ + isb(); \ + read_sysreg_par(); \ +}) + +#define BITS(from, to, val) FIELD_GET(GENMASK(from, to), val) + +static const char *decode_devmem_attr(u8 attr) +{ + switch (attr & ~0x1) { + case 0b00000000: + return "0b0000 Device-nGnRnE memory"; + case 0b00000100: + return "0b0100 Device-nGnRE memory"; + case 0b00001000: + return "0b1000 Device-nGRE memory"; + case 0b00001100: + return "0b1100 Device-GRE memory"; + default: + return "unknown"; + }; +} + +static char *cache_attr[] = { + "0b0000 Wrongly decoded", + "0b0001 Write-Through Transient, Write-Allocate, no Read-Allocate", + "0b0010 Write-Through Transient, no Write-Allocate", + "0b0011 Write-Through Transient, Write-Allocate", + "0b0100 Non-Cacheable", + "0b0101 Write-Back Transient, Write-Allocate, no Read-Allocate", + "0b0110 Write-Back Transient, no Write-Allocate", + "0b0111 Write-Back Transient, Write-Allocate", + "0b1000 Write-Through Non-transient, no Write-Allocate no Read-Allocate", + "0b1001 Write-Through Non-transient, Write-Allocate no Read-Allocate", + "0b1010 Write-Through Non-transient, no Write-Allocate", + "0b1011 Write-Through Non-transient, Write-Allocate", + "0b1100 Write-Back Non-transient, no Write-Allocate no Read-Allocate", + "0b1101 Write-Back Non-transient, Write-Allocate no Read-Allocate", + "0b1110 Write-Back Non-transient, no Write-Allocate", + "0b1111 Write-Back Non-transient, Write-Allocate", +}; + +static char *share_attr[] = { + "0b00 Non-Shareable", + "0b01 Reserved", + "0b10 Outer Shareable", + "0b11 Inner Shareable", +}; + +static char *stage_fault[] = { + "stage 1 translation", + "stage 2 translation", +}; + +static char *fault_status_leveled[] = { + "Address size fault", /* of translation or translation table base register */ + "Translation fault", + "Access flag fault", + "Permission fault", + "Synchronous External abort", /* level -1 */ + "Synchronous External abort", /* on translation table walk or hardware update of translation table */ + "Synchronous parity or ECC error", /* level -1 */ + "Synchronous parity or ECC error", /* on memory access on translation table walk or hardware update of translation table */ +}; + +static const char *decode_fault_status_level(u8 fst) +{ + if (!(fst & BIT(5))) + return ""; + + switch (BITS(5, 0, fst)) { + case 0b010011: + case 0b011011: + return ", level -1"; + } + + switch (BITS(1, 0, fst)) { + case 0b00: + return ", level 0"; + case 0b01: + return ", level 1"; + case 0b10: + return ", level 2"; + case 0b11: + return ", level 3"; + } + + BUG(); +} + +static const char *decode_fault_status(u8 fst) +{ + + switch (BITS(5, 0, fst)) { + case 0b101001: /* When FEAT_LPA2 is implemented */ + return "Address size fault, level -1"; + case 0b101011: /* When FEAT_LPA2 is implemented */ + return "Translation fault, level -1"; + case 0b110000: + return "TLB conflict abort"; + case 0b110001: /* When FEAT_HAFDBS is implemented */ + return "Unsupported atomic hardware update fault"; + case 0b111101: /* When EL1 is capable of using AArch32 */ + return "Section Domain fault, from an AArch32 stage 1 EL1&0 " + "translation regime using Short-descriptor translation " + "table format"; + case 0b111110: /* When EL1 is capable of using AArch32 */ + return "Page Domain fault, from an AArch32 stage 1 EL1&0 " + "translation regime using Short-descriptor translation " + "table format"; + default: + if (fst & BIT(5)) + return fault_status_leveled[BITS(4, 2, fst)]; + + return "Reserved"; + } +}; + +static void decode_par(unsigned long par) +{ + u8 devmem_attr = BITS(63, 56, par); + + if (par & 1) { + printf(" Translation aborted [9:8]: because of a fault in the %s%s\n", + stage_fault[BITS(9, 9, par)], + BITS(8, 8, par) ? " during a stage 1 translation table walk" : ""); + printf(" Fault Status Code [6:1]: 0x%02lx (%s%s)\n", BITS(6, 1, par), + decode_fault_status(BITS(6, 1, par)), + decode_fault_status_level(BITS(6, 1, par))); + printf(" Failure [0]: 0x1\n"); + } else { + if ((devmem_attr & 0xf0) && (devmem_attr & 0x0f)) { + printf(" Outer mem. attr. [63:60]: 0x%02lx (%s)\n", BITS(63, 60, par), + cache_attr[BITS(63, 60, par)]); + printf(" Inner mem. attr. [59:56]: 0x%02lx (%s)\n", BITS(59, 56, par), + cache_attr[BITS(59, 56, par)]); + } else if ((devmem_attr & 0b11110010) == 0) { + printf(" Memory attr. [63:56]: 0x%02x (%s)\n", + devmem_attr, decode_devmem_attr(devmem_attr)); + if (devmem_attr & 1) + printf(" (XS == 0 if FEAT_XS implemented)\n"); + } else if (devmem_attr == 0b01000000) { + printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Non-Cacheable"); + printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Non-Cacheable"); + printf(" (XS == 0 if FEAT_XS implemented)\n"); + } else if (devmem_attr == 0b10100000) { + printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Write-Through, No Write-Allocate"); + printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Write-Through"); + printf(" (XS == 0 if FEAT_XS implemented)\n"); + } else if (devmem_attr == 0b11110000) { + printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Write-Back"); + printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par), + "Write-Back, Write-Allocate, Non-transient"); + printf(" (if FEAT_MTE2 implemented)\n"); + } + printf(" Physical Address [51:12]: 0x%08lx\n", par & GENMASK(51, 12)); + printf(" Non-Secure [9]: 0x%lx\n", BITS(9, 9, par)); + printf(" Shareability attr. [8:7]: 0x%02lx (%s)\n", BITS(8, 7, par), + share_attr[BITS(8, 7, par)]); + printf(" Failure [0]: 0x0\n"); + } +} + +int mmuinfo_v8(void *_addr) +{ + unsigned long addr = (unsigned long)_addr; + unsigned long priv_read, priv_write; + + switch (current_el()) { + case 3: + priv_read = at_par("s1e3r", addr); + priv_write = at_par("s1e3w", addr); + break; + case 2: + priv_read = at_par("s1e2r", addr); + priv_write = at_par("s1e2w", addr); + break; + case 1: + priv_read = at_par("s1e1r", addr); + priv_write = at_par("s1e1w", addr); + break; + case 0: + priv_read = at_par("s1e0r", addr); + priv_write = at_par("s1e0w", addr); + break; + default: + return -EINVAL; + } + + printf("PAR result for 0x%08lx: \n", addr); + printf(" privileged read: 0x%08lx\n", priv_read); + decode_par(priv_read); + printf(" privileged write: 0x%08lx\n", priv_write); + decode_par(priv_write); + + return 0; +} diff --git a/arch/arm/cpu/no-mmu.c b/arch/arm/cpu/no-mmu.c index 7268fa9b9d..be3cfaf12b 100644 --- a/arch/arm/cpu/no-mmu.c +++ b/arch/arm/cpu/no-mmu.c @@ -1,17 +1,7 @@ -/* - * Copyright (c) 2015 Zodiac Inflight Innovation - * Author: Andrey Smirnov <andrew.smirnov@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2015 Zodiac Inflight Innovation + +/* Author: Andrey Smirnov <andrew.smirnov@gmail.com> */ #define pr_fmt(fmt) "nommu: " fmt diff --git a/arch/arm/cpu/psci-client.c b/arch/arm/cpu/psci-client.c index b5d0d37497..c865e754fd 100644 --- a/arch/arm/cpu/psci-client.c +++ b/arch/arm/cpu/psci-client.c @@ -15,7 +15,7 @@ static struct restart_handler restart; -static void __noreturn psci_invoke_noreturn(int function) +static void __noreturn psci_invoke_noreturn(ulong function) { int ret; @@ -108,12 +108,12 @@ static u32 invoke_psci_fn_smc(ulong function, ulong arg0, ulong arg1, ulong arg2 return res.a0; } -static int of_psci_do_fixup(struct device_node *root, void *context) +static int of_psci_do_fixup(struct device_node *root, void *method) { - return of_psci_fixup(root, *(u32 *)context); + return of_psci_fixup(root, version, (const void *)method); } -static int __init psci_probe(struct device_d *dev) +static int __init psci_probe(struct device *dev) { const char *method; ulong of_version, actual_version; @@ -123,7 +123,7 @@ static int __init psci_probe(struct device_d *dev) if (ret) return -ENODEV; - ret = of_property_read_string(dev->device_node, "method", &method); + ret = of_property_read_string(dev->of_node, "method", &method); if (ret) { dev_warn(dev, "missing \"method\" property\n"); return -ENXIO; @@ -156,7 +156,7 @@ static int __init psci_probe(struct device_d *dev) version >> 16, version & 0xffff); if (actual_version != of_version) - of_register_fixup(of_psci_do_fixup, &version); + of_register_fixup(of_psci_do_fixup, (void *)method); ret = poweroff_handler_register_fn(psci_poweroff); if (ret) @@ -181,8 +181,9 @@ static __maybe_unused struct of_device_id psci_dt_ids[] = { { .compatible = "arm,psci-1.0", .data = (void*)ARM_PSCI_VER(1,0) }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, psci_dt_ids); -static struct driver_d psci_driver = { +static struct driver psci_driver = { .name = "psci", .probe = psci_probe, .of_compatible = DRV_OF_COMPAT(psci_dt_ids), diff --git a/arch/arm/cpu/psci-of.c b/arch/arm/cpu/psci-of.c index ef83b0edee..1b6371ddd6 100644 --- a/arch/arm/cpu/psci-of.c +++ b/arch/arm/cpu/psci-of.c @@ -7,7 +7,8 @@ #include <asm/psci.h> #include <linux/arm-smccc.h> -int of_psci_fixup(struct device_node *root, unsigned long psci_version) +int of_psci_fixup(struct device_node *root, unsigned long psci_version, + const char *method) { struct device_node *psci; int ret; @@ -39,14 +40,14 @@ int of_psci_fixup(struct device_node *root, unsigned long psci_version) if (!cpu) break; of_property_write_string(cpu, "enable-method", "psci"); - pr_debug("Fixed %s\n", cpu->full_name); + pr_debug("Fixed %pOF\n", cpu); } ret = of_property_write_string(psci, "compatible", compat); if (ret) return ret; - ret = of_property_write_string(psci, "method", "smc"); + ret = of_property_write_string(psci, "method", method); if (ret) return ret; diff --git a/arch/arm/cpu/psci.c b/arch/arm/cpu/psci.c index 5a69aaa810..70c97e03a5 100644 --- a/arch/arm/cpu/psci.c +++ b/arch/arm/cpu/psci.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "psci: " fmt @@ -98,7 +89,7 @@ static unsigned long psci_cpu_suspend(u32 power_state, unsigned long entry, { psci_printf("%s\n", __func__); - if (psci_ops->cpu_off) + if (psci_ops->cpu_suspend) return psci_ops->cpu_suspend(power_state, entry, context_id); return ARM_PSCI_RET_NOT_SUPPORTED; @@ -198,7 +189,7 @@ static int of_psci_do_fixup(struct device_node *root, void *unused) if (bootm_arm_security_state() < ARM_STATE_NONSECURE) return 0; - return of_psci_fixup(root, ARM_PSCI_VER_1_0); + return of_psci_fixup(root, ARM_PSCI_VER_1_0, "smc"); } int psci_cpu_entry_c(void) diff --git a/arch/arm/cpu/sections.c b/arch/arm/cpu/sections.c index a53236d900..f310578ba2 100644 --- a/arch/arm/cpu/sections.c +++ b/arch/arm/cpu/sections.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <asm/sections.h> #include <linux/types.h> diff --git a/arch/arm/cpu/setupc.S b/arch/arm/cpu/setupc_32.S index 8ae7c89a2c..eafc9b52c6 100644 --- a/arch/arm/cpu/setupc.S +++ b/arch/arm/cpu/setupc_32.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <asm/sections.h> @@ -21,12 +23,12 @@ ENTRY(setup_c) ldr r2,=__bss_start sub r2, r2, r0 add r1, r0, r4 - bl memcpy /* memcpy(_text, _text + offset, __bss_start - _text) */ + bl __memcpy /* memcpy(_text, _text + offset, __bss_start - _text) */ 1: ldr r0, =__bss_start mov r1, #0 ldr r2, =__bss_stop sub r2, r2, r0 - bl memset /* clear bss */ + bl __memset /* clear bss */ bl sync_caches_for_execution sub lr, r5, r4 /* adjust return address to new location */ pop {r4, r5} @@ -67,7 +69,7 @@ ENTRY(relocate_to_adr) sub r7, r7, r1 /* sub address where we are actually running */ add r7, r7, r0 /* add address where we are going to run */ - bl memcpy /* copy binary */ + bl __memcpy /* copy binary */ bl sync_caches_for_execution diff --git a/arch/arm/cpu/setupc_64.S b/arch/arm/cpu/setupc_64.S index ee9ea6cfc0..2138c2a600 100644 --- a/arch/arm/cpu/setupc_64.S +++ b/arch/arm/cpu/setupc_64.S @@ -1,4 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> +#include <asm/assembler.h> #include <asm/sections.h> .section .text.setupc @@ -8,11 +11,11 @@ */ ENTRY(setup_c) mov x15, x30 - ldr x0, =__bss_start + adr_l x0, __bss_start mov x1, #0 - ldr x2, =__bss_stop + adr_l x2, __bss_stop sub x2, x2, x0 - bl memset /* clear bss */ + bl __memset /* clear bss */ mov x30, x15 ret ENDPROC(setup_c) @@ -24,9 +27,18 @@ ENDPROC(setup_c) * executing at new address. */ .section .text.relocate_to_adr -ENTRY(relocate_to_adr) /* x0: target address */ +#ifdef __PBL__ +ENTRY(relocate_to_adr_full) + adr_l x2, __image_end + b 1f +#endif + +ENTRY(relocate_to_adr) + adr_l x2, __bss_start + b 1f +1: stp x19, x20, [sp, #-16]! stp x21, x22, [sp, #-16]! @@ -34,34 +46,27 @@ ENTRY(relocate_to_adr) mov x21, x0 - bl get_runtime_offset - mov x5, x0 - - ldr x0, =_text - mov x20, x0 - - add x1, x0, x5 /* x1: from address */ + adr_l x1, _text + mov x20, x1 cmp x1, x21 /* already at correct address? */ beq 1f /* yes, skip copy to new address */ - ldr x2, =__bss_start - - sub x2, x2, x0 /* x2: size */ + sub x2, x2, x1 /* x2: size */ mov x0, x21 /* x0: target */ /* adjust return address */ sub x19, x19, x1 /* sub address where we are actually running */ add x19, x19, x0 /* add address where we are going to run */ - bl memcpy /* copy binary */ + bl __memcpy /* copy binary */ bl sync_caches_for_execution mov x0,#0 ic ivau, x0 /* flush icache */ - ldr x0,=1f + adr_l x0, 1f sub x0, x0, x20 add x0, x0, x21 br x0 /* jump to relocated address */ diff --git a/arch/arm/cpu/sm.c b/arch/arm/cpu/sm.c index 1f2c236d5a..53f5142b63 100644 --- a/arch/arm/cpu/sm.c +++ b/arch/arm/cpu/sm.c @@ -19,14 +19,13 @@ #include <linux/arm-smccc.h> #include <asm-generic/sections.h> #include <asm/secure.h> - -#include "mmu.h" +#include "mmu_32.h" static unsigned int read_id_pfr1(void) { unsigned int reg; - asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg)); + asm volatile ("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg)); return reg; } @@ -34,18 +33,18 @@ static u32 read_nsacr(void) { unsigned int reg; - asm("mrc p15, 0, %0, c1, c1, 2\n" : "=r"(reg)); + asm volatile ("mrc p15, 0, %0, c1, c1, 2\n" : "=r"(reg)); return reg; } static void write_nsacr(u32 val) { - asm("mcr p15, 0, %0, c1, c1, 2" : : "r"(val)); + asm volatile ("mcr p15, 0, %0, c1, c1, 2" : : "r"(val)); } static void write_mvbar(u32 val) { - asm("mcr p15, 0, %0, c12, c0, 1" : : "r"(val)); + asm volatile ("mcr p15, 0, %0, c12, c0, 1" : : "r"(val)); } static int cpu_is_virt_capable(void) @@ -150,7 +149,7 @@ static bool armv7_have_security_extensions(void) int armv7_secure_monitor_install(void) { int mmuon; - unsigned long ttb, vbar; + unsigned long ttb, vbar, dacr; if (!armv7_have_security_extensions()) { pr_err("Security extensions not implemented.\n"); @@ -164,12 +163,14 @@ int armv7_secure_monitor_install(void) vbar = get_vbar(); ttb = get_ttbr(); + dacr = get_domain(); armv7_init_nonsec(); __armv7_secure_monitor_install(); set_ttbr((void *)ttb); set_vbar(vbar); + set_domain(dacr); if (mmuon) { /* diff --git a/arch/arm/cpu/sm_as.S b/arch/arm/cpu/sm_as.S index de6cd0406f..f55ac8661c 100644 --- a/arch/arm/cpu/sm_as.S +++ b/arch/arm/cpu/sm_as.S @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + #include <linux/linkage.h> #include <asm/ptrace.h> #include <asm-generic/memory_layout.h> diff --git a/arch/arm/cpu/smccc-call.S b/arch/arm/cpu/smccc-call_32.S index b6bdc8b3b5..9875e1f947 100644 --- a/arch/arm/cpu/smccc-call.S +++ b/arch/arm/cpu/smccc-call_32.S @@ -1,16 +1,6 @@ -/* - * Copyright (c) 2015, Linaro Limited - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-FileCopyrightText: 2015 Linaro Limited */ + #include <linux/linkage.h> #include <asm/unwind.h> diff --git a/arch/arm/cpu/smccc-call_64.S b/arch/arm/cpu/smccc-call_64.S index 44888fb594..c2959050d2 100644 --- a/arch/arm/cpu/smccc-call_64.S +++ b/arch/arm/cpu/smccc-call_64.S @@ -1,16 +1,6 @@ -/* - * Copyright (c) 2015, Linaro Limited - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License Version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-FileCopyrightText: 2015 Linaro Limited */ + #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <asm/asm-offsets.h> diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index d99dd147b0..0351dcb927 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -1,21 +1,12 @@ -/* - * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + #define pr_fmt(fmt) "start.c: " fmt +#ifdef CONFIG_DEBUG_INITCALLS +#define DEBUG +#endif + #include <common.h> #include <init.h> #include <linux/sizes.h> @@ -28,8 +19,10 @@ #include <asm/unaligned.h> #include <asm/cache.h> #include <asm/mmu.h> +#include <linux/kasan.h> #include <memory.h> #include <uncompress.h> +#include <compressed-dtb.h> #include <malloc.h> #include <debug_ll.h> @@ -39,44 +32,36 @@ unsigned long arm_stack_top; static unsigned long arm_barebox_size; static unsigned long arm_endmem; +static unsigned long arm_membase; static void *barebox_boarddata; static unsigned long barebox_boarddata_size; -static bool blob_is_fdt(const void *blob) -{ - return get_unaligned_be32(blob) == FDT_MAGIC; -} - -static bool blob_is_compressed_fdt(const void *blob) +static bool blob_is_arm_boarddata(const void *blob) { - const struct barebox_arm_boarddata_compressed_dtb *dtb = blob; + const struct barebox_arm_boarddata *bd = blob; - return dtb->magic == BAREBOX_ARM_BOARDDATA_COMPRESSED_DTB_MAGIC; + return bd->magic == BAREBOX_ARM_BOARDDATA_MAGIC; } -static bool blob_is_arm_boarddata(const void *blob) +const struct barebox_boarddata *barebox_get_boarddata(void) { - const struct barebox_arm_boarddata *bd = blob; + if (!barebox_boarddata || !blob_is_arm_boarddata(barebox_boarddata)) + return NULL; - return bd->magic == BAREBOX_ARM_BOARDDATA_MAGIC; + return barebox_boarddata; } u32 barebox_arm_machine(void) { - if (barebox_boarddata && blob_is_arm_boarddata(barebox_boarddata)) { - const struct barebox_arm_boarddata *bd = barebox_boarddata; - return bd->machine; - } else { - return 0; - } + const struct barebox_boarddata *bd = barebox_get_boarddata(); + return bd ? bd->machine : 0; } void *barebox_arm_boot_dtb(void) { void *dtb; - void *data; - int ret; - struct barebox_arm_boarddata_compressed_dtb *compressed_dtb; + int ret = 0; + struct barebox_boarddata_compressed_dtb *compressed_dtb; static void *boot_dtb; if (boot_dtb) @@ -87,8 +72,7 @@ void *barebox_arm_boot_dtb(void) return barebox_boarddata; } - if (!IS_ENABLED(CONFIG_ARM_USE_COMPRESSED_DTB) || !barebox_boarddata - || !blob_is_compressed_fdt(barebox_boarddata)) + if (!fdt_blob_can_be_decompressed(barebox_boarddata)) return NULL; compressed_dtb = barebox_boarddata; @@ -99,10 +83,13 @@ void *barebox_arm_boot_dtb(void) if (!dtb) return NULL; - data = compressed_dtb + 1; + if (IS_ENABLED(CONFIG_IMAGE_COMPRESSION_NONE)) + memcpy(dtb, compressed_dtb->data, + compressed_dtb->datalen_uncompressed); + else + ret = uncompress(compressed_dtb->data, compressed_dtb->datalen, + NULL, NULL, dtb, NULL, NULL); - ret = uncompress(data, compressed_dtb->datalen, NULL, NULL, - dtb, NULL, NULL); if (ret) { pr_err("uncompressing dtb failed\n"); free(dtb); @@ -128,7 +115,7 @@ static inline unsigned long arm_mem_boarddata(unsigned long membase, unsigned long arm_mem_ramoops_get(void) { - return arm_mem_ramoops(0, arm_stack_top); + return arm_mem_ramoops(arm_stack_top); } EXPORT_SYMBOL_GPL(arm_mem_ramoops_get); @@ -138,17 +125,27 @@ unsigned long arm_mem_endmem_get(void) } EXPORT_SYMBOL_GPL(arm_mem_endmem_get); +unsigned long arm_mem_membase_get(void) +{ + return arm_membase; +} +EXPORT_SYMBOL_GPL(arm_mem_membase_get); + static int barebox_memory_areas_init(void) { if(barebox_boarddata) request_sdram_region("board data", (unsigned long)barebox_boarddata, barebox_boarddata_size); + if (IS_ENABLED(CONFIG_KASAN)) + request_sdram_region("kasan shadow", kasan_shadow_base, + mem_malloc_start() - kasan_shadow_base); + return 0; } device_initcall(barebox_memory_areas_init); -__noreturn void barebox_non_pbl_start(unsigned long membase, +__noreturn __prereloc void barebox_non_pbl_start(unsigned long membase, unsigned long memsize, void *boarddata) { unsigned long endmem = membase + memsize; @@ -172,23 +169,12 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); + arm_membase = membase; arm_endmem = endmem; - arm_stack_top = arm_mem_stack_top(membase, endmem); + arm_stack_top = arm_mem_stack_top(endmem); arm_barebox_size = barebox_size; malloc_end = barebox_base; - if (IS_ENABLED(CONFIG_MMU_EARLY)) { - unsigned long ttb = arm_mem_ttb(membase, endmem); - - if (IS_ENABLED(CONFIG_PBL_IMAGE)) { - arm_set_cache_functions(); - } else { - pr_debug("enabling MMU, ttb @ 0x%08lx\n", ttb); - arm_early_mmu_cache_invalidate(); - mmu_early_enable(membase, memsize, ttb); - } - } - if (boarddata) { uint32_t totalsize = 0; const char *name; @@ -197,24 +183,12 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, totalsize = get_unaligned_be32(boarddata + 4); name = "DTB"; } else if (blob_is_compressed_fdt(boarddata)) { - struct barebox_arm_boarddata_compressed_dtb *bd = boarddata; + struct barebox_boarddata_compressed_dtb *bd = boarddata; totalsize = bd->datalen + sizeof(*bd); name = "Compressed DTB"; } else if (blob_is_arm_boarddata(boarddata)) { totalsize = sizeof(struct barebox_arm_boarddata); name = "machine type"; - } else if ((unsigned long)boarddata < 8192) { - struct barebox_arm_boarddata *bd; - uint32_t machine_type = (unsigned long)boarddata; - unsigned long mem = arm_mem_boarddata(membase, endmem, - sizeof(*bd)); - pr_debug("found machine type %d in boarddata\n", - machine_type); - bd = barebox_boarddata = (void *)mem; - barebox_boarddata_size = sizeof(*bd); - bd->magic = BAREBOX_ARM_BOARDDATA_MAGIC; - bd->machine = machine_type; - malloc_end = mem; } if (totalsize) { @@ -246,8 +220,15 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, pr_debug("initializing malloc pool at 0x%08lx (size 0x%08lx)\n", malloc_start, malloc_end - malloc_start); + kasan_init(membase, memsize, malloc_start - (memsize >> KASAN_SHADOW_SCALE_SHIFT)); + mem_malloc_init((void *)malloc_start, (void *)malloc_end - 1); + if (IS_ENABLED(CONFIG_MMU) && !IS_ENABLED(CONFIG_PBL_IMAGE)) { + arm_early_mmu_cache_invalidate(); + mmu_early_enable(membase, memsize); + } + if (IS_ENABLED(CONFIG_BOOTM_OPTEE)) of_add_reserve_entry(endmem - OPTEE_SIZE, endmem - 1); @@ -272,7 +253,7 @@ void start(unsigned long membase, unsigned long memsize, void *boarddata); * First function in the uncompressed image. We get here from * the pbl. The stack already has been set up by the pbl. */ -void NAKED __section(.text_entry) start(unsigned long membase, +void NAKED __prereloc __section(.text_entry) start(unsigned long membase, unsigned long memsize, void *boarddata) { barebox_non_pbl_start(membase, memsize, boarddata); diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c index 88d073ebdd..a481c4634d 100644 --- a/arch/arm/cpu/uncompress.c +++ b/arch/arm/cpu/uncompress.c @@ -1,22 +1,9 @@ -/* - * uncompress.c - uncompressor code for self extracing pbl image - * - * Copyright (c) 2010-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix +// SPDX-FileCopyrightText: 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + +/* uncompress.c - uncompressor code for self extracing pbl image */ + #define pr_fmt(fmt) "uncompress.c: " fmt #include <common.h> @@ -65,8 +52,9 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize, void *pg_start, *pg_end; unsigned long pc = get_pc(); - pg_start = input_data + global_variable_offset(); - pg_end = input_data_end + global_variable_offset(); + /* piggy data is not relocated, so determine the bounds now */ + pg_start = runtime_address(input_data); + pg_end = runtime_address(input_data_end); if (IS_ENABLED(CONFIG_PBL_RELOCATABLE)) { /* @@ -93,14 +81,11 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize, pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); - if (IS_ENABLED(CONFIG_MMU_EARLY)) { - unsigned long ttb = arm_mem_ttb(membase, endmem); - pr_debug("enabling MMU, ttb @ 0x%08lx\n", ttb); - mmu_early_enable(membase, memsize, ttb); - } + if (IS_ENABLED(CONFIG_MMU)) + mmu_early_enable(membase, memsize); - free_mem_ptr = arm_mem_early_malloc(membase, endmem); - free_mem_end_ptr = arm_mem_early_malloc_end(membase, endmem); + free_mem_ptr = arm_mem_early_malloc(endmem); + free_mem_end_ptr = arm_mem_early_malloc_end(endmem); pr_debug("uncompressing barebox binary at 0x%p (size 0x%08x) to 0x%08lx (uncompressed size: 0x%08x)\n", pg_start, pg_len, barebox_base, uncompressed_len); |