blob: 4ea6da26bccd11b1760a95ca0dd06bab658d1b9a [file]
/*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <autoconf.h>
#include <mode/arm_generic_timer.h>
#include <elfloader/gen_config.h>
#include <elfloader.h>
#include <printf.h>
/* TODO: get rid of this, make GIC initialisation part of a driver. */
#define TK1_GICD_PADDR 0x50041000
#define TK1_GICC_PADDR 0x50042000
extern void flush_dcache(void);
extern void invalidate_icache(void);
/* non-secure bit: 0 secure; 1 nonsecure */
#define SCR_NS (0)
/* controls which mode takes IRQ exceptions: 0 IRQ mode; 1 monitor mode */
#define SCR_IRQ (1)
/* FIQ mode control */
#define SCR_FIQ (2)
/* external abort handler. 0 abort mode; 1 monitor mode */
#define SCR_EA (3)
/* CPSR.F can be modified in nonsecure mode */
#define SCR_FW (4)
/* CPSR.A can be modified in nonsecure mode */
#define SCR_AW (5)
/* not early terminination. not implmented */
#define SCR_NET (6)
/* secure monitor call disabled: 0 smc executes in nonsecure state;
* 1 undefined instruction in nonsecure state
*/
#define SCR_SCD (7)
/* hyp call enable: 0 hvc instruction is undefined in nonsecure pl1 mode
* and unpredictable in hyp mode
* 1 hvc is enabled in nonsecure pl1.
*/
#define SCR_HCE (8)
/* secure instruction fetch. when in secure state, the bit disables
* instruction fetches from non-secure memory */
#define SCR_SIF (9)
#define MONITOR_MODE (0x16)
#define SUPERVISOR_MODE (0x13)
#define HYPERVISOR_MODE (0x1a)
/* if seL4 is used a hypervior, we should enable both HCe and SCE bits.
* the secure monitor exception handler does very limited things, so
* we let the seL4 handle interrupts/exceptions.
*/
#define MONITOR_MODE (0x16)
#define SUPERVISOR_MODE (0x13)
#define HYPERVISOR_MODE (0x1a)
void arm_halt(void)
{
while (1) {
asm volatile("wfe");
}
}
/* steal the last 1 MiB physical memory for monitor mode */
#define MON_PA_START (0x80000000 + 0x27f00000)
#define MON_PA_SIZE (1 << 20)
#define MON_PA_END (MON_PA_START + MON_PA_SIZE)
#define MON_PA_STACK (MON_PA_END - 0x10)
#define MON_VECTOR_START (MON_PA_START)
#define MON_HANDLER_START (MON_PA_START + 0x10000)
#define LOADED_OFFSET 0x90000000
#if defined(CONFIG_ARM_MONITOR_HOOK) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE) \
|| defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_MONITOR_MODE)
static int mon_init_done = 0;
static void switch_to_mon_mode(void)
{
if (mon_init_done == 0) {
/* first need to make sure that we are in secure world */
uint32_t scr = 0;
/* read the secure configuration register, note if we are
* in nonsecure world, the instruction fails.
*/
asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
if (scr & BIT(SCR_NS)) {
printf("In nonsecure world, you should never see this!\n");
arm_halt();
}
/* enable hyper call */
scr = BIT(SCR_HCE);
asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
/* now switch to secure monitor mode. restoring our stack and link register in the process
* as these two registers are banked. */
uint32_t sp_temp = 0;
uint32_t lr_temp = 0;
asm volatile("mov %[SP_TEMP], sp\n"
"mov %[LR_TEMP], lr\n"
"cps %[MON_MODE]\n\t"
"isb\n"
"mov sp, %[SP_TEMP]\n"
"mov lr, %[LR_TEMP]\n"
: [SP_TEMP]"+r"(sp_temp),
[LR_TEMP]"+r"(lr_temp)
: [MON_MODE]"I"(MONITOR_MODE));
mon_init_done = 1;
printf("ELF loader: monitor mode init done\n");
}
}
#endif
#ifdef CONFIG_ARM_MONITOR_HOOK
extern void arm_monitor_vector(void);
extern void arm_monitor_vector_end(void);
extern void *memcpy(void *dest, void *src, size_t n);
extern char _bootstack_top[1];
static void install_monitor_hook(void)
{
uint32_t size = arm_monitor_vector_end - arm_monitor_vector;
/* switch monitor mode if not already */
switch_to_mon_mode();
printf("Copy monitor mode vector from %x to %x size %x\n", (arm_monitor_vector), MON_VECTOR_START, size);
memcpy((void *)MON_VECTOR_START, (void *)(arm_monitor_vector), size);
asm volatile("mcr p15, 0, %0, c12, c0, 1"::"r"(MON_VECTOR_START));
}
#endif
#ifdef CONFIG_ARM_HYPERVISOR_MODE
static void switch_to_hyp_mode(void)
{
uint32_t scr = 0;
/*
* Need to make sure anything in the write buffer is
* in RAM, and nothing in the cache that we want to access is tagged
* 'secure' because we're about to switch to non-secure mode.
* Note: flush_dcache() does a flush-and-invalidate.
*/
flush_dcache();
invalidate_icache();
asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
scr |= BIT(SCR_HCE);
scr &= ~BIT(SCR_SCD);
scr |= BIT(SCR_NS);
scr &= ~BIT(SCR_SIF);
asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
/* now switch to hypervisor mode. restoring our stack and link register in the process
* as these two registers are banked. */
uint32_t sp_temp = 0;
uint32_t lr_temp = 0;
asm volatile("mov %[SP_TEMP], sp\n"
"mov %[LR_TEMP], lr\n"
"cps %[HYP_MODE]\n\t"
"isb\n"
"mov sp, %[SP_TEMP]\n"
"mov lr, %[LR_TEMP]\n"
: [SP_TEMP]"+r"(sp_temp),
[LR_TEMP]"+r"(lr_temp)
: [HYP_MODE]"I"(HYPERVISOR_MODE));
asm volatile("mrs %0, cpsr":"=r"(scr));
printf("Load seL4 in nonsecure HYP mode %x", scr);
}
#endif
#ifdef CONFIG_ARM_NS_SUPERVISOR_MODE
static void switch_to_ns_svc_mode(void)
{
uint32_t scr = 0;
asm volatile("cps %0\n\t"
"isb\n"
::"I"(SUPERVISOR_MODE));
asm volatile("mov r0, sp");
asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
scr |= BIT(SCR_NS);
asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
asm volatile("mov sp, r0");
printf("Load seL4 in nonsecure SVC mode\n");
}
#endif
extern void arm_monitor_vector(void);
#if defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE)
/* tk1 uses GIC v2 */
struct gicd_map {
uint32_t enable;
uint32_t ic_type;
uint32_t dist_ident;
uint32_t res1[29];
uint32_t security[32];
uint32_t enable_set[32];
uint32_t enable_clr[32];
uint32_t pending_set[32];
uint32_t pending_clr[32];
uint32_t active[32];
uint32_t res2[32];
uint32_t priority[255];
};
struct gicc_map {
uint32_t ctrl;
uint32_t pri_mask;
uint32_t pb_c;
uint32_t int_ack;
uint32_t eoi;
};
volatile struct gicd_map *gicd = (volatile struct gicd_map *)(TK1_GICD_PADDR);
volatile struct gicc_map *gicc = (volatile struct gicc_map *)(TK1_GICC_PADDR);
static void route_irqs_to_nonsecure(void)
{
int i = 0;
int nirqs = 32 * ((gicd->ic_type & 0x1f) + 1);
printf("Number of IRQs: %d\n", nirqs);
gicd->enable = 0;
/* note: the security and priority initialisations in
* non-secure mode will not work, but use the values
* set by secure mode.
*/
/* set all irqs to group 1 - nonsecure */
for (i = 0; i < nirqs; i += 32) {
gicd->security[i >> 5] = 0xffffffff;
}
/* assign the irqs in a single priority group: no preemptions */
for (i = 0; i < nirqs; i += 4) {
gicd->priority[i >> 2] = 0x80808080;
}
gicc->ctrl = 0;
/* writing 255 always set the largest (lowest) priority value.
* missing this hurts health */
gicc->pri_mask = 0xff;
}
#endif
static void enable_ns_access_cp(void)
{
uint32_t nsacr = 0;
asm volatile("mrc p15, 0, %0, c1, c1, 2":"=r"(nsacr));
/* enable cp10, cp11 */
nsacr |= BIT(10) | BIT(11);
asm volatile("mcr p15, 0, %0, c1, c1, 2"::"r"(nsacr));
asm volatile("isb");
}
void platform_init(void)
{
#if defined(CONFIG_ARM_MONITOR_HOOK) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE) \
|| defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_MONITOR_MODE)
/* mon_init_done needs to explicitly initialised when booting a binary image */
mon_init_done = 0;
#endif
#ifdef CONFIG_ARM_MONITOR_HOOK
install_monitor_hook();
#endif
enable_ns_access_cp();
#ifdef CONFIG_ARM_NS_SUPERVISOR_MODE
switch_to_mon_mode();
route_irqs_to_nonsecure();
switch_to_ns_svc_mode();
#endif
#ifdef CONFIG_ARM_HYPERVISOR_MODE
switch_to_mon_mode();
route_irqs_to_nonsecure();
switch_to_hyp_mode();
#endif
#ifdef CONFIG_ARM_MONITOR_MODE
switch_to_mon_mode();
#endif
/* Reset the virtual offset for the platform timer to 0 */
reset_cntvoff();
}
#if CONFIG_MAX_NUM_NODES > 1
void non_boot_init(void)
{
/* Reset the virtual offset for the platform timer to 0 */
reset_cntvoff();
}
#endif