1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <autoconf.h>
8#include <elfloader/gen_config.h>
9#include <elfloader.h>
10#include <printf.h>
11
12/* TODO: get rid of this, make GIC initialisation part of a driver. */
13#define TK1_GICD_PADDR    0x50041000
14#define TK1_GICC_PADDR    0x50042000
15
16extern void flush_dcache(void);
17extern void invalidate_icache(void);
18
19/* non-secure bit: 0 secure; 1 nonsecure */
20#define SCR_NS      (0)
21
22/* controls which mode takes IRQ exceptions: 0 IRQ mode; 1 monitor mode */
23#define SCR_IRQ     (1)
24
25/* FIQ mode control */
26#define SCR_FIQ     (2)
27
28/* external abort handler. 0 abort mode; 1 monitor mode */
29#define SCR_EA      (3)
30
31/* CPSR.F can be modified in nonsecure mode */
32#define SCR_FW      (4)
33
34/* CPSR.A can be modified in nonsecure mode */
35#define SCR_AW      (5)
36
37/* not early terminination. not implmented */
38#define SCR_NET     (6)
39
40/* secure monitor call disabled: 0 smc executes in nonsecure state;
41 * 1 undefined instruction in nonsecure state
42 */
43#define SCR_SCD     (7)
44
45/* hyp call enable: 0 hvc instruction is undefined in nonsecure pl1 mode
46 *                    and unpredictable in hyp mode
47 *                  1 hvc is enabled in nonsecure pl1.
48 */
49#define SCR_HCE     (8)
50
51/* secure instruction fetch. when in secure state, the bit disables
52 * instruction fetches from non-secure memory */
53#define SCR_SIF     (9)
54
55#define MONITOR_MODE        (0x16)
56#define SUPERVISOR_MODE     (0x13)
57#define HYPERVISOR_MODE     (0x1a)
58
59/* if seL4 is used a hypervior, we should enable both HCe and SCE bits.
60 * the secure monitor exception handler does very limited things, so
61 * we let the seL4 handle interrupts/exceptions.
62 */
63
64#define MONITOR_MODE        (0x16)
65#define SUPERVISOR_MODE     (0x13)
66#define HYPERVISOR_MODE     (0x1a)
67
68void arm_halt(void)
69{
70    while (1) {
71        asm volatile("wfe");
72    }
73}
74
75/* steal the last 1 MiB physical memory for monitor mode */
76
77#define MON_PA_START        (0x80000000 + 0x27f00000)
78#define MON_PA_SIZE         (1 << 20)
79#define MON_PA_END          (MON_PA_START + MON_PA_SIZE)
80#define MON_PA_STACK        (MON_PA_END - 0x10)
81#define MON_VECTOR_START    (MON_PA_START)
82#define MON_HANDLER_START   (MON_PA_START + 0x10000)
83#define LOADED_OFFSET       0x90000000
84
85#if defined(CONFIG_ARM_MONITOR_HOOK) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE) \
86    || defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_MONITOR_MODE)
87static int mon_init_done = 0;
88
89static void switch_to_mon_mode(void)
90{
91    if (mon_init_done == 0) {
92        /* first need to make sure that we are in secure world */
93        uint32_t scr = 0;
94
95        /* read the secure configuration register, note if we are
96         * in nonsecure world, the instruction fails.
97         */
98
99        asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
100
101        if (scr & BIT(SCR_NS)) {
102            printf("In nonsecure world, you should never see this!\n");
103            arm_halt();
104        }
105
106        /* enable hyper call */
107        scr = BIT(SCR_HCE);
108
109        asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
110
111        /* now switch to secure monitor mode. restoring our stack and link register in the process
112         * as these two registers are banked. */
113        uint32_t sp_temp = 0;
114        uint32_t lr_temp = 0;
115        asm volatile("mov %[SP_TEMP], sp\n"
116                     "mov %[LR_TEMP], lr\n"
117                     "cps %[MON_MODE]\n\t"
118                     "isb\n"
119                     "mov sp, %[SP_TEMP]\n"
120                     "mov lr, %[LR_TEMP]\n"
121                     : [SP_TEMP]"+r"(sp_temp),
122                     [LR_TEMP]"+r"(lr_temp)
123                     : [MON_MODE]"I"(MONITOR_MODE));
124        mon_init_done = 1;
125        printf("ELF loader: monitor mode init done\n");
126    }
127}
128
129#endif
130
131#ifdef CONFIG_ARM_MONITOR_HOOK
132
133extern void arm_monitor_vector(void);
134extern void arm_monitor_vector_end(void);
135extern void *memcpy(void *dest, void *src, size_t n);
136extern char _bootstack_top[1];
137
138static void install_monitor_hook(void)
139{
140    uint32_t size = arm_monitor_vector_end - arm_monitor_vector;
141    /* switch monitor mode if not already */
142    switch_to_mon_mode();
143    printf("Copy monitor mode vector from %x to %x size %x\n", (arm_monitor_vector), MON_VECTOR_START, size);
144    memcpy((void *)MON_VECTOR_START, (void *)(arm_monitor_vector), size);
145
146    asm volatile("mcr p15, 0, %0, c12, c0, 1"::"r"(MON_VECTOR_START));
147}
148
149#endif
150
151#ifdef CONFIG_ARM_HYPERVISOR_MODE
152static void switch_to_hyp_mode(void)
153{
154    uint32_t scr = 0;
155
156    /*
157     * Need to make sure anything in the write buffer is
158     * in RAM, and nothing in the cache that we want to access is tagged
159     * 'secure' because we're about to switch to non-secure mode.
160     * Note: flush_dcache() does a flush-and-invalidate.
161     */
162    flush_dcache();
163    invalidate_icache();
164
165    asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
166    scr |= BIT(SCR_HCE);
167    scr &= ~BIT(SCR_SCD);
168    scr |= BIT(SCR_NS);
169    scr &= ~BIT(SCR_SIF);
170    asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
171    /* now switch to hypervisor mode. restoring our stack and link register in the process
172     * as these two registers are banked. */
173    uint32_t sp_temp = 0;
174    uint32_t lr_temp = 0;
175    asm volatile("mov %[SP_TEMP], sp\n"
176                 "mov %[LR_TEMP], lr\n"
177                 "cps %[HYP_MODE]\n\t"
178                 "isb\n"
179                 "mov sp, %[SP_TEMP]\n"
180                 "mov lr, %[LR_TEMP]\n"
181                 : [SP_TEMP]"+r"(sp_temp),
182                 [LR_TEMP]"+r"(lr_temp)
183                 : [HYP_MODE]"I"(HYPERVISOR_MODE));
184
185    asm volatile("mrs %0, cpsr":"=r"(scr));
186    printf("Load seL4 in nonsecure HYP mode %x", scr);
187}
188#endif
189
190#ifdef CONFIG_ARM_NS_SUPERVISOR_MODE
191static void switch_to_ns_svc_mode(void)
192{
193    uint32_t scr = 0;
194
195    asm volatile("cps %0\n\t"
196                 "isb\n"
197                 ::"I"(SUPERVISOR_MODE));
198
199    asm volatile("mov r0, sp");
200    asm volatile("mrc p15, 0, %0, c1, c1, 0":"=r"(scr));
201    scr |= BIT(SCR_NS);
202
203    asm volatile("mcr p15, 0, %0, c1, c1, 0"::"r"(scr));
204    asm volatile("mov sp, r0");
205
206    printf("Load seL4 in nonsecure SVC mode\n");
207}
208#endif
209
210extern void arm_monitor_vector(void);
211
212#if defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE)
213/* tk1 uses GIC v2 */
214struct gicd_map {
215    uint32_t enable;
216    uint32_t ic_type;
217    uint32_t dist_ident;
218    uint32_t res1[29];
219    uint32_t security[32];
220    uint32_t enable_set[32];
221    uint32_t enable_clr[32];
222    uint32_t pending_set[32];
223    uint32_t pending_clr[32];
224    uint32_t active[32];
225    uint32_t res2[32];
226    uint32_t priority[255];
227};
228
229struct gicc_map {
230    uint32_t ctrl;
231    uint32_t pri_mask;
232    uint32_t pb_c;
233    uint32_t int_ack;
234    uint32_t eoi;
235};
236
237volatile struct gicd_map *gicd = (volatile struct gicd_map *)(TK1_GICD_PADDR);
238volatile struct gicc_map *gicc = (volatile struct gicc_map *)(TK1_GICC_PADDR);
239
240static void route_irqs_to_nonsecure(void)
241{
242    int i = 0;
243    int nirqs = 32 * ((gicd->ic_type & 0x1f) + 1);
244    printf("Number of IRQs: %d\n", nirqs);
245    gicd->enable = 0;
246
247    /* note: the security and priority initialisations in
248     * non-secure mode will not work, but use the values
249     * set by secure mode.
250     */
251
252    /* set all irqs to group 1 - nonsecure */
253    for (i = 0; i < nirqs; i += 32) {
254        gicd->security[i >> 5] = 0xffffffff;
255    }
256
257    /* assign the irqs in a single priority group: no preemptions */
258    for (i = 0; i < nirqs; i += 4) {
259        gicd->priority[i >> 2] = 0x80808080;
260    }
261
262    gicc->ctrl = 0;
263
264    /* writing 255 always set the largest (lowest) priority value.
265     * missing this hurts health */
266    gicc->pri_mask = 0xff;
267}
268#endif
269
270static void enable_ns_access_cp(void)
271{
272    uint32_t nsacr = 0;
273    asm volatile("mrc p15, 0, %0, c1, c1, 2":"=r"(nsacr));
274
275    /* enable cp10, cp11 */
276    nsacr |= BIT(10) |  BIT(11);
277    asm volatile("mcr p15, 0, %0, c1, c1, 2"::"r"(nsacr));
278
279    asm volatile("isb");
280}
281
282void platform_init(void)
283{
284#if defined(CONFIG_ARM_MONITOR_HOOK) || defined(CONFIG_ARM_NS_SUPERVISOR_MODE) \
285    || defined(CONFIG_ARM_HYPERVISOR_MODE) || defined(CONFIG_ARM_MONITOR_MODE)
286    /* mon_init_done needs to explicitly initialised when booting a binary image */
287    mon_init_done = 0;
288#endif
289#ifdef CONFIG_ARM_MONITOR_HOOK
290    install_monitor_hook();
291#endif
292
293    enable_ns_access_cp();
294
295#ifdef CONFIG_ARM_NS_SUPERVISOR_MODE
296    switch_to_mon_mode();
297    route_irqs_to_nonsecure();
298    switch_to_ns_svc_mode();
299#endif
300
301#ifdef CONFIG_ARM_HYPERVISOR_MODE
302    switch_to_mon_mode();
303    route_irqs_to_nonsecure();
304    switch_to_hyp_mode();
305#endif
306
307#ifdef CONFIG_ARM_MONITOR_MODE
308    switch_to_mon_mode();
309#endif
310
311}
312