/* * (C) Copyright 2013 * Andre Przywara, Linaro * * Routines to transition ARMv7 processors from secure into non-secure state * and from non-secure SVC into HYP mode * needed to enable ARMv7 virtualization for current hypervisors * * SPDX-License-Identifier: GPL-2.0+ */ #define pr_fmt(fmt) "secure: " fmt #include #include #include #include #include #include #include #include #include #include "mmu.h" /* valid bits in CBAR register / PERIPHBASE value */ #define CBAR_MASK 0xFFFF8000 static unsigned int read_id_pfr1(void) { unsigned int reg; asm("mrc p15, 0, %0, c0, c1, 1\n" : "=r"(reg)); return reg; } static u32 read_nsacr(void) { unsigned int reg; asm("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)); } static void write_mvbar(u32 val) { asm("mcr p15, 0, %0, c12, c0, 1" : : "r"(val)); } static unsigned long get_cbar(void) { unsigned periphbase; /* get the GIC base address from the CBAR register */ asm("mrc p15, 4, %0, c15, c0, 0\n" : "=r" (periphbase)); /* the PERIPHBASE can be mapped above 4 GB (lower 8 bits used to * encode this). Bail out here since we cannot access this without * enabling paging. */ if ((periphbase & 0xff) != 0) { pr_err("PERIPHBASE is above 4 GB, no access.\n"); return -1; } return periphbase & CBAR_MASK; } static unsigned long get_gicd_base_address(void) { return get_cbar() + GIC_DIST_OFFSET; } static int cpu_is_virt_capable(void) { return read_id_pfr1() & (1 << 12); } static unsigned long get_gicc_base_address(void) { unsigned long adr = get_cbar(); if (cpu_is_virt_capable()) adr += GIC_CPU_OFFSET_A15; else adr += GIC_CPU_OFFSET_A9; return adr; } #define GICD_IGROUPRn 0x0080 int armv7_init_nonsec(void) { void __iomem *gicd = IOMEM(get_gicd_base_address()); unsigned itlinesnr, i; u32 val; /* * the SCR register will be set directly in the monitor mode handler, * according to the spec one should not tinker with it in secure state * in SVC mode. Do not try to read it once in non-secure state, * any access to it will trap. */ /* enable the GIC distributor */ val = readl(gicd + GICD_CTLR); val |= 0x3; writel(val, gicd + GICD_CTLR); /* TYPER[4:0] contains an encoded number of available interrupts */ itlinesnr = readl(gicd + GICD_TYPER) & 0x1f; /* * Set all bits in the GIC group registers to one to allow access * from non-secure state. The first 32 interrupts are private per * CPU and will be set later when enabling the GIC for each core */ for (i = 1; i <= itlinesnr; i++) writel(0xffffffff, gicd + GICD_IGROUPRn + 4 * i); return 0; } /* * armv7_secure_monitor_install - install secure monitor * * This function is entered in secure mode. It installs the secure * monitor code and enters it using a smc call. This function is executed * on every CPU. We leave this function returns in nonsecure mode. */ int __armv7_secure_monitor_install(void) { struct arm_smccc_res res; void __iomem *gicd = IOMEM(get_gicd_base_address()); void __iomem *gicc = IOMEM(get_gicc_base_address()); u32 nsacr; writel(0xffffffff, gicd + GICD_IGROUPRn); writel(0x3, gicc + GICC_CTLR); writel(0xff, gicc + GICC_PMR); nsacr = read_nsacr(); nsacr |= 0x00043fff; /* all copros allowed in non-secure mode */ write_nsacr(nsacr); write_mvbar((unsigned long)secure_monitor_init_vectors); isb(); /* Initialize the secure monitor */ arm_smccc_smc(0, 0, 0, 0, 0, 0, 0, 0, &res); /* We're in nonsecure mode now */ return 0; } static bool armv7_have_security_extensions(void) { return (read_id_pfr1() & 0xf0) != 0; } /* * armv7_secure_monitor_install - install secure monitor * * This function is entered in secure mode. It installs the secure * monitor code and enters it using a smc call. This function is executed * once on the primary CPU only. We leave this function returns in nonsecure * mode. */ int armv7_secure_monitor_install(void) { int mmuon; unsigned long ttb, vbar; if (!armv7_have_security_extensions()) { pr_err("Security extensions not implemented.\n"); return -EINVAL; } mmuon = get_cr() & CR_M; vbar = get_vbar(); asm volatile ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb)); armv7_init_nonsec(); __armv7_secure_monitor_install(); asm volatile ("mcr p15, 0, %0, c2, c0, 0" : : "r"(ttb)); set_vbar(vbar); if (mmuon) { /* * If the MMU was already turned on in secure mode, enable it in * non-secure mode aswell */ __mmu_cache_on(); } pr_debug("Initialized secure monitor\n"); return 0; } /* * of_secure_monitor_fixup - reserve memory region for secure monitor * * We currently do not support putting the secure monitor into onchip RAM, * hence it runs in SDRAM and we must reserve the memory region so that we * won't get overwritten from the Kernel. * Beware: despite the name this is not secure in any way. The Kernel obeys * the reserve map, but only because it's nice. It could always overwrite the * secure monitor and hijack secure mode. */ static int of_secure_monitor_fixup(struct device_node *root, void *unused) { unsigned long res_start, res_end; res_start = (unsigned long)_stext; res_end = (unsigned long)__bss_stop; of_add_reserve_entry(res_start, res_end); pr_debug("Reserved memory range from 0x%08lx to 0x%08lx\n", res_start, res_end); return 0; } static enum arm_security_state bootm_secure_state; static const char * const bootm_secure_state_names[] = { [ARM_STATE_SECURE] = "secure", [ARM_STATE_NONSECURE] = "nonsecure", [ARM_STATE_HYP] = "hyp", }; enum arm_security_state bootm_arm_security_state(void) { return bootm_secure_state; } const char *bootm_arm_security_state_name(enum arm_security_state state) { return bootm_secure_state_names[state]; } static int sm_init(void) { of_register_fixup(of_secure_monitor_fixup, NULL); globalvar_add_simple_enum("bootm.secure_state", (unsigned int *)&bootm_secure_state, bootm_secure_state_names, ARRAY_SIZE(bootm_secure_state_names)); return 0; } device_initcall(sm_init);