Improve lpuart kernel driver
[barrelfish] / kernel / arch / armv8 / boot / boot_generic.c
1 /*
2  * Copyright (c) 2016, 2017, ETH Zurich.
3  * Copyright (c) 2016, Hewlett Packard Enterprise Development LP.
4  * All rights reserved.
5  *
6  * This file is distributed under the terms in the attached LICENSE file.
7  * If you do not find this file, copies can be found by writing to:
8  * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
9  */
10
11 /* CPU driver VM initialisation.
12
13    This is the entry point on booting the first core, and needs to deal with
14    the state left by UEFI.  The CPU is mostly configured, in particular
15    translation is enabled, and all RAM is mapped 1-1.  We'll also be in either
16    EL3 or EL2.  We need to map the EL1 kernel window (TTBR1), drop to EL1, and
17    jump to the next routine, which has already been relocated for us.
18  */
19
20 #include <stdio.h>
21 #include <stdbool.h>
22
23 #include <barrelfish_kpi/types.h>
24 #include <init.h>
25 #include <offsets.h>
26 #include <sysreg.h>
27 #include <dev/armv8_dev.h>
28
29 #include <multiboot2.h>
30 #include <barrelfish_kpi/arm_core_data.h>
31
32 #define DEBUG 1
33 #define IMX8X
34
35 void eret(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3);
36
37 void boot_bsp_init(uint32_t magic, lpaddr_t pointer)
38     __attribute__((noreturn));
39 void boot_app_init(lpaddr_t pointer)
40     __attribute__((noreturn));
41
42 /* low level debugging facilities */
43 #if DEBUG
44 #ifdef THUNDERX
45 #include <dev/pl011_uart_dev.h>
46
47 #define CN88XX_MAP_UART0_OFFSET 0x87E024000000UL
48
49 static pl011_uart_t uart;
50
51 static void debug_uart_initialize(void) {
52     pl011_uart_initialize(&uart, (mackerel_addr_t) CN88XX_MAP_UART0_OFFSET);
53 }
54
55 static void debug_serial_putc(char c)
56 {
57     while(pl011_uart_FR_txff_rdf(&uart) == 1) ;
58     pl011_uart_DR_rawwr(&uart, c);
59 }
60 #elif defined(XGENE)
61 #include <dev/apm88xxxx/apm88xxxx_pc16550_dev.h>
62
63 #define CN88XX_MAP_UART0_OFFSET 0x87E024000000UL
64
65 apm88xxxx_pc16550_t uart;
66
67 static void debug_uart_initialize(void) {
68     apm88xxxx_pc16550_initialize(&uart, (mackerel_addr_t)0x1C020000);
69 }
70
71 static void debug_serial_putc(char c)
72 {
73     // Wait until FIFO can hold more characters
74     while(!apm88xxxx_pc16550_LSR_thre_rdf(&uart));
75     // Write character
76     apm88xxxx_pc16550_THR_thr_wrf(&uart, c);
77 }
78 #elif defined(QEMU)
79 #include <dev/pl011_uart_dev.h>
80
81 #define QEMU_MAP_UART0_OFFSET 0x9000000UL
82
83 static pl011_uart_t uart;
84
85 static void debug_uart_initialize(void) {
86     pl011_uart_initialize(&uart, (mackerel_addr_t) QEMU_MAP_UART0_OFFSET);
87 }
88
89 static void debug_serial_putc(char c)
90 {
91     while(pl011_uart_FR_txff_rdf(&uart) == 1) ;
92     pl011_uart_DR_rawwr(&uart, c);
93 }
94 #elif defined(IMX8X)
95 #include <dev/lpuart_dev.h>
96
97 #define IMX8X8_MAP_UART0_OFFSET 0x5A090000UL
98
99 static lpuart_t uart;
100
101 static void debug_uart_initialize(void) {
102     lpuart_initialize(&uart, (mackerel_addr_t) IMX8X8_MAP_UART0_OFFSET);
103 }
104
105 static void debug_serial_putc(char c)
106 {
107     while(lpuart_stat_tdre_rdf(&uart) == 0);
108     lpuart_data_buf_wrf(&uart, c);
109 }
110 #endif
111
112 static void debug_serial_putchar(char c) {
113     if (c == '\n') {
114         debug_serial_putc('\r');
115     }
116     debug_serial_putc(c);
117 }
118
119
120 static void debug_print_string(char *str)
121 {
122     while (str && *str) {
123         debug_serial_putchar(*str);
124         str++;
125     }
126 }
127
128 /**
129  * \brief Very basic hex print support
130  */
131 static inline void debug_print_hex(uint64_t num) {
132     static char chars[] = {
133         '0',
134         '1',
135         '2',
136         '3',
137         '4',
138         '5',
139         '6',
140         '7',
141         '8',
142         '9',
143         'a',
144         'b',
145         'c',
146         'd',
147         'e',
148         'f',
149     };
150
151     char buf[17];
152     for (int i = 0; i < 16; i++) {
153         int d = (num >> 4*i) & 0xf;
154         buf[15-i] = chars[d];
155     }
156     buf[16] = '\0';
157     debug_print_string(buf);
158 }
159 #else
160 #define debug_print_string(x)
161 #define debug_uart_initialize()
162 #define debug_print_hex(x)
163 #endif
164
165
166 void (*cpu_driver_entry)(lvaddr_t pointer);
167
168 static void configure_tcr(void) {
169     armv8_TCR_EL1_t tcr_el1 = armv8_TCR_EL1_rd(NULL);
170     // disable top byte ignored, EL1
171     tcr_el1 = armv8_TCR_EL1_TBI1_insert(tcr_el1, 0);
172     // disable top byte ignored, EL0
173     tcr_el1 = armv8_TCR_EL1_TBI0_insert(tcr_el1, 0);
174     // 48b IPA
175     tcr_el1 = armv8_TCR_EL1_IPS_insert(tcr_el1, 5);
176     // 4kB granule
177     tcr_el1 = armv8_TCR_EL1_TG1_insert(tcr_el1, armv8_KB_4);
178     // Walks inner shareable
179     tcr_el1 = armv8_TCR_EL1_SH1_insert(tcr_el1, armv8_inner_shareable);
180     // Walks outer WB WA
181     tcr_el1 = armv8_TCR_EL1_ORGN1_insert(tcr_el1, armv8_WbRaWa_cache);
182     // Walks inner WB WA
183     tcr_el1 = armv8_TCR_EL1_IRGN1_insert(tcr_el1, armv8_WbRaWa_cache);
184     // enable EL1 translation
185     tcr_el1 = armv8_TCR_EL1_EPD1_insert(tcr_el1, 0);
186     // 48b kernel VA
187     tcr_el1 = armv8_TCR_EL1_T1SZ_insert(tcr_el1, 16);
188     // 4kB granule
189     tcr_el1 = armv8_TCR_EL1_TG0_insert(tcr_el1, armv8_KB_4);
190     // Walks inner shareable
191     tcr_el1 = armv8_TCR_EL1_SH0_insert(tcr_el1, armv8_inner_shareable);
192     // Walks outer WB WA
193     tcr_el1 = armv8_TCR_EL1_ORGN0_insert(tcr_el1, armv8_WbRaWa_cache);
194     // Walks inner WB WA
195     tcr_el1 = armv8_TCR_EL1_IRGN0_insert(tcr_el1, armv8_WbRaWa_cache);
196     // enable EL0 translation
197     tcr_el1 = armv8_TCR_EL1_EPD0_insert(tcr_el1, 0);
198     // 48b user VA
199     tcr_el1 = armv8_TCR_EL1_T0SZ_insert(tcr_el1, 16);
200     armv8_TCR_EL1_wr(NULL, tcr_el1);
201 }
202
203
204 #define    DAIF_FIQ_BIT   (1 << 0)
205 #define    DAIF_IRQ_BIT   (1 << 1)
206
207
208 static void armv8_disable_interrupts(void)
209 {
210     __asm volatile("msr DAIFSet, #3\n");
211 }
212
213 static void armv8_set_tcr(uint8_t el)
214 {
215     switch(el) {
216         case 3:
217             //sysreg_write_ttbr0_el2(addr);
218         case 2:
219         {
220             armv8_TCR_EL2_t reg = 0;
221             reg = armv8_TCR_EL2_PS_insert(reg, 5);
222             reg = armv8_TCR_EL2_T0SZ_insert(reg, (64 - 48));
223             armv8_TCR_EL2_wr(NULL, reg);
224             break;
225         }
226         case 1:
227         {
228             armv8_TCR_EL1_t reg = 0;
229           // TODO: figure out what to set  reg = armv8_TCR_EL1_PS_insert(reg, 5);
230             reg = armv8_TCR_EL1_T0SZ_insert(reg, (64 - 48));
231             armv8_TCR_EL1_wr(NULL, reg);
232             break;
233         }
234         default:
235             assert("should not happen");
236             return;
237     }
238 }
239
240 static void armv8_set_ttbr0(uint8_t el, lpaddr_t addr)
241 {
242     switch(el) {
243         case 3:
244             //sysreg_write_ttbr0_el2(addr);
245         case 2:
246             armv8_TTBR0_EL2_wr(NULL, addr);
247             armv8_TTBR0_EL1_wr(NULL, addr);
248             break;
249         case 1:
250             armv8_TTBR0_EL1_wr(NULL, addr);
251             break;
252         default:
253             assert("should not happen");
254             return;
255     }
256     __asm volatile("isb");
257 }
258
259 static void armv8_enable_mmu(uint8_t el)
260 {
261     switch(el) {
262         case 3:
263             armv8_SCTLR_EL3_M_wrf(NULL, 0x1);
264             __asm volatile("tlbi    alle3\n  isb");
265             break;
266         case 2:
267             armv8_SCTLR_EL2_M_wrf(NULL, 0x1);
268             __asm volatile("tlbi    alle2\n  isb");
269             break;
270         case 1:
271             armv8_SCTLR_EL1_M_wrf(NULL, 0x1);
272             __asm volatile("tlbi    vmalle1\n  isb");
273             break;
274         default:
275             assert("should not happen");
276             return;
277     }
278     __asm volatile("dsb sy\n isb");
279 }
280
281 static void armv8_invalidate_tlb(uint8_t el)
282 {
283     switch(el) {
284         case 3:
285             armv8_SCTLR_EL3_M_wrf(NULL, 0x1);
286             __asm volatile("tlbi    alle3");
287             break;
288         case 2:
289             armv8_SCTLR_EL2_M_wrf(NULL, 0x1);
290             __asm volatile("tlbi    alle2");
291             break;
292         case 1:
293             armv8_SCTLR_EL1_M_wrf(NULL, 0x1);
294             __asm volatile("tlbi    vmalle1");
295             break;
296         default:
297             assert("should not happen");
298             return;
299     }
300     __asm volatile("dsb sy\n isb");
301 }
302
303 static void armv8_invalidate_icache(void)
304 {
305     __asm volatile(
306       "ic      iallu \n"
307       "dsb     sy \n"
308       "isb \n"
309       );
310 }
311
312 static void armv8_instruction_synchronization_barrier(void)
313 {
314     __asm volatile("isb");
315 }
316
317 static void configure_spsr(uint8_t el) {
318     armv8_SPSR_EL2_t spsr = 0;
319     /* mask the exceptions */
320     spsr = armv8_SPSR_EL2_D_insert(spsr, 1);
321     spsr = armv8_SPSR_EL2_A_insert(spsr, 1);
322     spsr = armv8_SPSR_EL2_I_insert(spsr, 1);
323     spsr = armv8_SPSR_EL2_F_insert(spsr, 1);
324
325     /* set el1 and use the SP_ELx stack */
326     spsr = armv8_SPSR_EL2_M_lo_insert(spsr, (1<<2) | 1);
327
328     switch(el) {
329     case 3:
330         armv8_SPSR_EL3_wr(NULL, spsr);
331         return;
332     case 2:
333         armv8_SPSR_EL2_wr(NULL, spsr);
334         break;
335     case 1:
336         armv8_SPSR_EL1_wr(NULL, spsr);
337         return;
338     default:
339         return;
340     }
341 }
342
343 static void configure_ttbr1(lpaddr_t addr)
344 {
345     armv8_TTBR1_EL1_rawwr(NULL, addr);
346 }
347
348 static void configure_mair(void)
349 {
350     /* Set memory type 0, for kernel use. */
351     // attr0 = Normal Memory, Inner Write-back non transient
352     // attr1 = Device-nGnRnE memory
353     armv8_MAIR_EL1_wr(NULL, 0x00ff);
354 }
355
356 static void configure_sctlr(void)
357 /* Enable EL0/1 translation. */
358 {
359
360     armv8_SCTLR_EL1_t val = 0;
361
362     /* Traps EL0 execution of cache maintenance instructions to EL1 */
363     val = armv8_SCTLR_EL1_UCI_insert(val, 0x1);
364
365     /* write permissions implies execute never */
366     //val = armv8_SCTLR_EL1_WXN_insert(val, 0x1);
367
368     /* don't trap WFI/WFE instructions to EL1 */
369     val = armv8_SCTLR_EL1_nTWE_insert(val, 0x1);
370     val = armv8_SCTLR_EL1_nTWI_insert(val, 0x1);
371
372     /* disable Traps EL0 accesses to the CTR_EL0 to EL1*/
373     val = armv8_SCTLR_EL1_UCT_insert(val, 0x1);
374
375     /* Allow EL0 to do DC ZVA */
376     val = armv8_SCTLR_EL1_DZE_insert(val, 0x1);
377
378     /* enable instruction cache */
379     val = armv8_SCTLR_EL1_I_insert(val, 0x1);
380
381     /*
382      * EL0 execution of MRS , MSR(register) , or MSR(immediate) instructions
383      * that access the DAIF is not trapped to EL1.
384      */
385     //val = armv8_SCTLR_EL1_UMA_insert(val, 0x1);
386
387     /*
388      * Enables accesses to the DMB, DSB, and ISB System
389      * instructions in the (coproc== 1111 ) encoding space from EL0
390      */
391     val = armv8_SCTLR_EL1_CP15BEN_insert(val, 0x1);
392
393     /* Enable SP alignment checks */
394     val = armv8_SCTLR_EL1_SA0_insert(val, 0x1);
395     val = armv8_SCTLR_EL1_SA_insert(val, 0x1);
396
397     /* enable data cachable */
398     val = armv8_SCTLR_EL1_C_insert(val, 0x1);
399
400     /* enable alignment checks */
401     val = armv8_SCTLR_EL1_A_insert(val, 0x1);
402
403     /* enable mmu */
404     val = armv8_SCTLR_EL1_M_insert(val, 0x1);
405
406     armv8_SCTLR_EL1_wr(NULL, val);
407 }
408
409 static void configure_el3_traps(void)
410 {
411
412     /* If we've started in EL3, that most likely means we're in the
413      * simulator.  We don't use it at all, so just disable all traps to
414      * EL3, and drop to non-secure EL2 (if it exists). */
415
416     armv8_SCR_EL3_t val = 0;
417
418     /* Don't trap secure timer access. */
419     val = armv8_SCR_EL3_ST_insert(val, 0x1);
420
421     /* Next EL is AArch64. */
422     val = armv8_SCR_EL3_RW_insert(val, 0x1);
423
424     /* HVC is enabled. */
425     val = armv8_SCR_EL3_HCE_insert(val, 0x1);
426
427     /* SMC is disabled. */
428     val = armv8_SCR_EL3_SMD_insert(val, 0x1);
429
430     /* External aborts don't trap to EL3. */
431     val = armv8_SCR_EL3_EA_insert(val, 0x1);
432
433     /* FIQs don't trap to EL3. */
434     val = armv8_SCR_EL3_FIQ_insert(val, 0x1);
435
436     /* IRQs don't trap to EL3. */
437     val = armv8_SCR_EL3_IRQ_insert(val, 0x1);
438
439     /* EL0 and EL1 are non-secure. */
440     val = armv8_SCR_EL3_NS_insert(val, 0x1);
441
442     armv8_SCR_EL3_wr(NULL, val);
443
444     /* We don't need to set SCTLR_EL3, as we're not using it. */
445
446     armv8_MDCR_EL3_t mdcr = 0;
447     /* Allow event counting in secure state. */
448     armv8_MDCR_EL3_SPME_insert(mdcr, 0x1);
449     armv8_MDCR_EL3_wr(NULL, mdcr);
450 }
451
452 static void configure_el2_traps(void)
453 {
454     /* check if EL2 is implemented */
455     if (armv8_ID_AA64PFR0_EL1_EL2_rdf(NULL) == armv8_ID_EL_NOT_IMPLEMENTED) {
456         return;
457     }
458
459     /* configure EL2 traps & mmu */
460
461     armv8_HCR_EL2_t val = 0;
462
463     /* For the Non-secure EL1&0 translation regime, for permitted accesses to a
464      * memory location that use a common definition of the Shareability and
465      * Cacheability of the location, there might be a loss of coherency if the
466      * Inner Cacheability attribute for those accesses differs from the Outer
467      * Cacheability attribute.*/
468     val = armv8_HCR_EL2_MIOCNCE_insert(val, 1);
469
470     /* Set the mode to be AARCH64 */
471     val = armv8_HCR_EL2_RW_insert(val, 1);
472
473     /* HVC instructions are UNDEFINED at EL2 and Non-secure EL1. Any resulting
474      * exception is taken to the Exception level at which the HVC instruction
475      * is executed.
476      *
477      * XXX: this will disable Hypervisor calls entirely, revisit for ARRAKIS
478      */
479     val = armv8_HCR_EL2_HCD_insert(val, 1);
480
481     armv8_HCR_EL2_wr(NULL, val);
482
483
484     /* disable traps to EL2 for timer accesses */
485
486     armv8_CNTHCTL_EL2_t cnthctl;
487     cnthctl = armv8_CNTHCTL_EL2_rd(NULL);
488     cnthctl = armv8_CNTHCTL_EL2_EL1PCEN_insert(cnthctl, 0x1);
489     cnthctl = armv8_CNTHCTL_EL2_EL1PCTEN_insert(cnthctl, 0x1);
490     armv8_CNTHCTL_EL2_wr(NULL, cnthctl);
491 }
492
493 static void configure_el1_traps(void)
494 {
495     /* disable traps for FP/SIMD access  */
496     armv8_CPACR_EL1_FPEN_wrf(NULL, armv8_fpen_trap_none);
497 }
498
499 static void drop_to_el2(struct armv8_core_data *pointer)
500 {
501     /* write the stack pointer for EL1 */
502     armv8_SP_EL1_wr(NULL, pointer->cpu_driver_stack + KERNEL_OFFSET);
503
504     /* Set the jump target */
505     armv8_ELR_EL3_wr(NULL, (uint64_t)cpu_driver_entry);
506
507     /* call exception return */
508     eret((lpaddr_t)pointer + KERNEL_OFFSET, 0, 0, 0);
509 }
510
511 static void drop_to_el1(struct armv8_core_data *pointer)
512 {
513     /* write the stack pointer for EL1 */
514     armv8_SP_EL1_wr(NULL, pointer->cpu_driver_stack + KERNEL_OFFSET);
515
516     /* Set the jump target */
517     armv8_ELR_EL2_wr(NULL, (uint64_t)cpu_driver_entry);
518
519     /* call exception return */
520     eret((lpaddr_t)pointer + KERNEL_OFFSET, 0, 0, 0);
521 }
522
523 static void jump_to_cpudriver(struct armv8_core_data *pointer)
524 {
525     // We are in EL1, so call arch_init directly.
526
527     // Re-set the stack pointer
528     sysreg_write_sp(pointer->cpu_driver_stack + KERNEL_OFFSET);
529     cpu_driver_entry((lpaddr_t)pointer + KERNEL_OFFSET);
530 }
531
532
533 /* On entry:
534
535    Execution is starting in LOW addresses
536    Pointers to stack and multiboot are LOW addresses
537    Single core running (not guaranteed to be core 0)
538    CPU is in highest implemented exception level
539    MMU enabled, 4k translation granule, 1:1 mapping of all RAM
540    Little-endian mode
541    Core caches (L1&L2) and TLB enabled
542    Non-architectural caches disabled (e.g. L3)
543    Interrupts enabled
544    Generic timer initialized and enabled
545    >= 128KiB stack
546    ACPI tables available
547    Register x0 contains a pointer to ARMv8 core data
548  */
549 static void boot_generic_init(struct armv8_core_data *core_data) {
550
551     cpu_driver_entry = (void *)core_data->cpu_driver_entry;
552
553     uint8_t el = armv8_CurrentEL_EL_rdf(NULL);
554
555     /* Configure the EL1 translation regime. */
556     configure_tcr();
557
558     /* Configure the kernel page tables for EL1. */
559     configure_ttbr1(core_data->page_table_root);
560
561     /* configure memory attributes */
562     configure_mair();
563
564     /* Enable EL0/1 translation. */
565     configure_sctlr();
566
567     /* configure spsr */
568     configure_spsr(el);
569
570     /* configure EL 1 traps*/
571     configure_el1_traps();
572
573     debug_print_string("Jumping to CPU driver\n");
574     __asm volatile(
575         "_break:\n"
576         "b _break"
577     );
578     switch(el) {
579     case 3:
580         configure_el3_traps();
581         configure_el2_traps();
582         drop_to_el2(core_data);
583         break;
584     case 2:
585         configure_el2_traps();
586         drop_to_el1(core_data);
587         break;
588     case 1:
589         jump_to_cpudriver(core_data);
590         break;
591     default:
592         break;
593     }
594 }
595
596 /**
597  * @brief initializes an application core
598  *
599  * @param state pointer to the armv8_core_data structure
600  *
601  * This function is intended to bring the core to the same state as if it
602  * has been booted by the UEFI boot loader.
603  */
604 void boot_app_init(lpaddr_t pointer)
605 {
606     debug_uart_initialize();
607     debug_print_string("APP BOOTING\n");
608
609     struct armv8_core_data *core_data = (struct armv8_core_data *)pointer;
610
611     uint8_t current_el = armv8_CurrentEL_EL_rdf(NULL);
612
613     if (current_el == 2) {
614         uint64_t zero = 0;
615         __asm volatile("MSR CPTR_EL2, %[zero]" : : [zero] "r" (zero));
616     }
617
618     // /* disable interrupts */
619     armv8_disable_interrupts();
620
621     /* set the ttbr0/1 */
622     armv8_set_ttbr0(current_el, core_data->page_table_root);
623
624     /* set the TCR */
625     armv8_set_tcr(current_el);
626
627     /* enable MMU */
628     armv8_enable_mmu(current_el);
629
630     /* invalidate TLB */
631     armv8_invalidate_tlb(current_el);
632
633     /* invalidate icache */
634     armv8_invalidate_icache();
635     armv8_instruction_synchronization_barrier();
636
637     boot_generic_init(core_data);
638
639     while(1) {
640         __asm volatile("wfi \n");
641     }
642 }
643
644 /* On entry:
645
646    Execution is starting in LOW addresses
647    Pointers to stack and multiboot are LOW addresses
648    Single core running (not guaranteed to be core 0)
649    CPU is in highest implemented exception level
650    MMU enabled, 4k translation granule, 1:1 mapping of all RAM
651    Little-endian mode
652    Core caches (L1&L2) and TLB enabled
653    Non-architectural caches disabled (e.g. L3)
654    Interrupts enabled
655    Generic timer initialized and enabled
656    >= 128KiB stack
657    ACPI tables available
658    Register x0 contains the multiboot magic value
659    Register x1 contains a pointer to ARMv8 core data
660  */
661 void
662 boot_bsp_init(uint32_t magic, lpaddr_t pointer) {
663
664     debug_uart_initialize();
665     debug_print_string("BSP BOOTING\n");
666     debug_print_string("Magic: ");
667     debug_print_hex(magic);
668     debug_print_string(", Pointer: ");
669     debug_print_hex(pointer);
670     debug_print_string("\n");
671
672     /* Boot magic must be set */
673     if (magic != MULTIBOOT2_BOOTLOADER_MAGIC) {
674         debug_print_string("Invalid bootloader magic\n");
675         goto stop;
676     }
677
678     struct armv8_core_data *core_data = (struct armv8_core_data *)pointer;
679
680     debug_print_string("CPU driver entry: ");
681     debug_print_hex(core_data->cpu_driver_entry);
682     debug_print_string("\n");
683
684     /* disable interrupts */
685     armv8_disable_interrupts();
686     
687     boot_generic_init(core_data);
688
689     stop:
690     while(1) {
691         __asm volatile("wfi \n");
692     }
693 }