]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/arm/locore-v6.S
MFV r304732.
[FreeBSD/FreeBSD.git] / sys / arm / arm / locore-v6.S
1 /*-
2  * Copyright 2004-2014 Olivier Houchard <cognet@FreeBSD.org>
3  * Copyright 2012-2014 Ian Lepore <ian@FreeBSD.org>
4  * Copyright 2013-2014 Andrew Turner <andrew@FreeBSD.org>
5  * Copyright 2014 Svatopluk Kraus <onwahe@gmail.com>
6  * Copyright 2014 Michal Meloun <meloun@miracle.cz>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include "assym.s"
32 #include <sys/syscall.h>
33 #include <machine/asm.h>
34 #include <machine/asmacros.h>
35 #include <machine/armreg.h>
36 #include <machine/sysreg.h>
37 #include <machine/cpuconf.h>
38 #include <machine/pte-v6.h>
39
40 __FBSDID("$FreeBSD$");
41
42
43 #if __ARM_ARCH >= 7
44 #if defined(__ARM_ARCH_7VE__) || defined(__clang__)
45 /*
46  * HYP support is in bintuils >= 2.21 and gcc >= 4.9 defines __ARM_ARCH_7VE__
47  * when enabled. llvm >= 3.6 supports it too.
48  */
49 .arch_extension virt
50 #define MSR_ELR_HYP(regnum)     msr     elr_hyp, lr
51 #define ERET    eret
52 #else
53 #define MSR_ELR_HYP(regnum) .word (0xe12ef300 | regnum)
54 #define ERET .word 0xe160006e
55 #endif
56 #endif /* __ARM_ARCH >= 7 */
57
58 /* A small statically-allocated stack used only during initarm() and AP startup. */
59 #define INIT_ARM_STACK_SIZE     2048
60
61         .text
62         .align  2
63
64 #if __ARM_ARCH >= 7
65 #define LEAVE_HYP                                                       \
66         /* Leave HYP mode */                                            ;\
67         mrs     r0, cpsr                                                ;\
68         and     r0, r0, #(PSR_MODE)   /* Mode is in the low 5 bits of CPSR */ ;\
69         teq     r0, #(PSR_HYP32_MODE) /* Hyp Mode? */                   ;\
70         bne     1f                                                      ;\
71         /* Ensure that IRQ, FIQ and Aborts will be disabled after eret */ ;\
72         mrs     r0, cpsr                                                ;\
73         bic     r0, r0, #(PSR_MODE)                                     ;\
74         orr     r0, r0, #(PSR_SVC32_MODE)                               ;\
75         orr     r0, r0, #(PSR_I | PSR_F | PSR_A)                        ;\
76         msr     spsr_cxsf, r0                                           ;\
77         /* Exit hypervisor mode */                                      ;\
78         adr     lr, 1f                                                  ;\
79         MSR_ELR_HYP(14)                                                 ;\
80         ERET                                                            ;\
81 1:
82 #else
83 #define LEAVE_HYP
84 #endif /* __ARM_ARCH >= 7 */
85
86 /*
87  * On entry for FreeBSD boot ABI:
88  *      r0 - metadata pointer or 0 (boothowto on AT91's boot2)
89  *      r1 - if (r0 == 0) then metadata pointer
90  * On entry for Linux boot ABI:
91  *      r0 - 0
92  *      r1 - machine type (passed as arg2 to initarm)
93  *      r2 - Pointer to a tagged list or dtb image (phys addr) (passed as arg1 initarm)
94  *
95  * For both types of boot we gather up the args, put them in a struct arm_boot_params
96  * structure and pass that to initarm.
97  */
98         .globl  btext
99 btext:
100 ASENTRY_NP(_start)
101         STOP_UNWINDING          /* Can't unwind into the bootloader! */
102
103         /* Make sure interrupts are disabled. */
104         cpsid   ifa
105
106         mov     r8, r0          /* 0 or boot mode from boot2 */
107         mov     r9, r1          /* Save Machine type */
108         mov     r10, r2         /* Save meta data */
109         mov     r11, r3         /* Future expansion */
110
111         LEAVE_HYP
112
113         /*
114          * Check whether data cache is enabled.  If it is, then we know
115          * current tags are valid (not power-on garbage values) and there
116          * might be dirty lines that need cleaning.  Disable cache to prevent
117          * new lines being allocated, then call wbinv_poc_all to clean it.
118          */
119         mrc     CP15_SCTLR(r7)
120         tst     r7, #CPU_CONTROL_DC_ENABLE
121         blne    dcache_wbinv_poc_all
122
123         /* ! Do not write to memory between wbinv and disabling cache ! */
124
125         /*
126          * Now there are no dirty lines, but there may still be lines marked
127          * valid.  Disable all caches and the MMU, and invalidate everything
128          * before setting up new page tables and re-enabling the mmu.
129          */
130 1:
131         bic     r7, #CPU_CONTROL_DC_ENABLE
132         bic     r7, #CPU_CONTROL_AFLT_ENABLE
133         bic     r7, #CPU_CONTROL_MMU_ENABLE
134         bic     r7, #CPU_CONTROL_IC_ENABLE
135         bic     r7, #CPU_CONTROL_BPRD_ENABLE
136         bic     r7, #CPU_CONTROL_SW_ENABLE
137         orr     r7, #CPU_CONTROL_UNAL_ENABLE
138         orr     r7, #CPU_CONTROL_VECRELOC
139         mcr     CP15_SCTLR(r7)
140         DSB
141         ISB
142         bl      dcache_inv_poc_all
143         mcr     CP15_ICIALLU
144         DSB
145         ISB
146
147         /*
148          * Build page table from scratch.
149          */
150
151         /* 
152          * Figure out the physical address we're loaded at by assuming this
153          * entry point code is in the first L1 section and so if we clear the
154          * offset bits of the pc that will give us the section-aligned load
155          * address, which remains in r5 throughout all the following code.
156          */
157         ldr     r2, =(L1_S_OFFSET)
158         bic     r5, pc, r2
159
160         /* Find the delta between VA and PA, result stays in r0 throughout. */
161         adr     r0, Lpagetable
162         bl      translate_va_to_pa
163
164         /* 
165          * First map the entire 4GB address space as VA=PA.  It's mapped as
166          * normal (cached) memory because it's for things like accessing the
167          * parameters passed in from the bootloader, which might be at any
168          * physical address, different for every platform.
169          */
170         mov     r1, #0
171         mov     r2, #0
172         mov     r3, #4096
173         bl      build_pagetables
174
175         /* 
176          * Next we do 64MiB starting at the physical load address, mapped to
177          * the VA the kernel is linked for.
178          */
179         mov     r1, r5
180         ldr     r2, =(KERNVIRTADDR)
181         mov     r3, #64
182         bl      build_pagetables
183
184         /* Create a device mapping for early_printf if specified. */
185 #if defined(SOCDEV_PA) && defined(SOCDEV_VA)
186         ldr     r1, =SOCDEV_PA
187         ldr     r2, =SOCDEV_VA
188         mov     r3, #1
189         bl      build_device_pagetables
190 #endif
191         bl      init_mmu
192
193         /* Transition the PC from physical to virtual addressing. */
194         ldr     pc, =1f
195 1:
196
197         /* Setup stack, clear BSS */
198         ldr     r1, =.Lstart
199         ldmia   r1, {r1, r2, sp}        /* Set initial stack and */
200         add     sp, sp, #INIT_ARM_STACK_SIZE
201         sub     r2, r2, r1              /* get zero init data */
202         mov     r3, #0
203 2:
204         str     r3, [r1], #0x0004       /* get zero init data */
205         subs    r2, r2, #4
206         bgt     2b
207
208         mov     r1, #28                 /* loader info size is 28 bytes also second arg */
209         subs    sp, sp, r1              /* allocate arm_boot_params struct on stack */
210         mov     r0, sp                  /* loader info pointer is first arg */
211         bic     sp, sp, #7              /* align stack to 8 bytes */
212         str     r1, [r0]                /* Store length of loader info */
213         str     r8, [r0, #4]            /* Store r0 from boot loader */
214         str     r9, [r0, #8]            /* Store r1 from boot loader */
215         str     r10, [r0, #12]          /* store r2 from boot loader */
216         str     r11, [r0, #16]          /* store r3 from boot loader */
217         str     r5, [r0, #20]           /* store the physical address */
218         adr     r4, Lpagetable          /* load the pagetable address */
219         ldr     r5, [r4, #4]
220         str     r5, [r0, #24]           /* store the pagetable address */
221         mov     fp, #0                  /* trace back starts here */
222         bl      _C_LABEL(initarm)       /* Off we go */
223
224         /* init arm will return the new stack pointer. */
225         mov     sp, r0
226
227         bl      _C_LABEL(mi_startup)    /* call mi_startup()! */
228
229         ldr     r0, =.Lmainreturned
230         b       _C_LABEL(panic)
231         /* NOTREACHED */
232 END(_start)
233
234 #define VA_TO_PA_POINTER(name, table)    \
235 name:                                   ;\
236         .word   .                       ;\
237         .word   table
238
239 /*
240  * Returns the physical address of a magic va to pa pointer.
241  * r0     - The pagetable data pointer. This must be built using the
242  *          VA_TO_PA_POINTER macro.
243  *          e.g.
244  *            VA_TO_PA_POINTER(Lpagetable, pagetable)
245  *            ...
246  *            adr  r0, Lpagetable
247  *            bl   translate_va_to_pa
248  *            r0 will now contain the physical address of pagetable
249  * r1, r2 - Trashed
250  */
251 translate_va_to_pa:
252         ldr     r1, [r0]
253         sub     r2, r1, r0
254         /* At this point: r2 = VA - PA */
255
256         /*
257          * Find the physical address of the table. After these two
258          * instructions:
259          * r1 = va(pagetable)
260          *
261          * r0 = va(pagetable) - (VA - PA)
262          *    = va(pagetable) - VA + PA
263          *    = pa(pagetable)
264          */
265         ldr     r1, [r0, #4]
266         sub     r0, r1, r2
267         mov     pc, lr
268
269 /*
270  * Init MMU
271  * r0 - the table base address
272  */
273
274 ASENTRY_NP(init_mmu)
275
276         /* Setup TLB and MMU registers */
277         mcr     CP15_TTBR0(r0)          /* Set TTB */
278         mov     r0, #0
279         mcr     CP15_CONTEXTIDR(r0)     /* Set ASID to 0 */
280
281         /* Set the Domain Access register */
282         mov     r0, #DOMAIN_CLIENT      /* Only domain #0 is used */
283         mcr     CP15_DACR(r0)
284
285         /*
286          * Set TEX remap registers
287          *  - All is set to uncacheable memory
288          */
289         ldr     r0, =0xAAAAA
290         mcr     CP15_PRRR(r0)
291         mov     r0, #0
292         mcr     CP15_NMRR(r0)
293         mcr     CP15_TLBIALL            /* Flush TLB */
294         DSB
295         ISB
296
297         /* Enable MMU */
298         mrc     CP15_SCTLR(r0)
299         orr     r0, r0, #CPU_CONTROL_MMU_ENABLE
300         orr     r0, r0, #CPU_CONTROL_V6_EXTPAGE
301         orr     r0, r0, #CPU_CONTROL_TR_ENABLE
302         orr     r0, r0, #CPU_CONTROL_AF_ENABLE
303         mcr     CP15_SCTLR(r0)
304         DSB
305         ISB
306         mcr     CP15_TLBIALL            /* Flush TLB */
307         mcr     CP15_BPIALL             /* Flush Branch predictor */
308         DSB
309         ISB
310
311         mov     pc, lr
312 END(init_mmu)
313
314
315 /*
316  * Init SMP coherent mode, enable caching and switch to final MMU table.
317  * Called with disabled caches
318  * r0 - The table base address
319  * r1 - clear bits for aux register
320  * r2 - set bits for aux register
321  */
322 ASENTRY_NP(reinit_mmu)
323         push    {r4-r11, lr}
324         mov     r4, r0
325         mov     r5, r1
326         mov     r6, r2
327
328         /* !! Be very paranoid here !! */
329         /* !! We cannot write single bit here !! */
330
331 #if 0   /* XXX writeback shouldn't be necessary */
332         /* Write back and invalidate all integrated caches */
333         bl      dcache_wbinv_poc_all
334 #else
335         bl      dcache_inv_pou_all
336 #endif
337         mcr     CP15_ICIALLU
338         DSB
339         ISB
340
341         /* Set auxiliary register */
342         mrc     CP15_ACTLR(r7)
343         bic     r8, r7, r5              /* Mask bits */
344         eor     r8, r8, r6              /* Set bits */
345         teq     r7, r8
346         mcrne   CP15_ACTLR(r8)
347         DSB
348         ISB
349
350         /* Enable caches. */
351         mrc     CP15_SCTLR(r7)
352         orr     r7, #CPU_CONTROL_DC_ENABLE
353         orr     r7, #CPU_CONTROL_IC_ENABLE
354         orr     r7, #CPU_CONTROL_BPRD_ENABLE
355         mcr     CP15_SCTLR(r7)
356         DSB
357
358         mcr     CP15_TTBR0(r4)          /* Set new TTB */
359         DSB
360         ISB
361
362         mcr     CP15_TLBIALL            /* Flush TLB */
363         mcr     CP15_BPIALL             /* Flush Branch predictor */
364         DSB
365         ISB
366
367 #if 0 /* XXX writeback shouldn't be necessary */
368         /* Write back and invalidate all integrated caches */
369         bl      dcache_wbinv_poc_all
370 #else
371         bl      dcache_inv_pou_all
372 #endif
373         mcr     CP15_ICIALLU
374         DSB
375         ISB
376
377         pop     {r4-r11, pc}
378 END(reinit_mmu)
379
380
381 /*
382  * Builds the page table
383  * r0 - The table base address
384  * r1 - The physical address (trashed)
385  * r2 - The virtual address (trashed)
386  * r3 - The number of 1MiB sections
387  * r4 - Trashed
388  *
389  * Addresses must be 1MiB aligned
390  */
391 build_device_pagetables:
392         ldr     r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0
393         b       1f
394 build_pagetables:
395         /* Set the required page attributed */
396         ldr     r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0
397 1:
398         orr     r1, r4
399
400         /* Move the virtual address to the correct bit location */
401         lsr     r2, #(PTE1_SHIFT - 2)
402
403         mov     r4, r3
404 2:
405         str     r1, [r0, r2]
406         add     r2, r2, #4
407         add     r1, r1, #(PTE1_SIZE)
408         adds    r4, r4, #-1
409         bhi     2b
410
411         mov     pc, lr
412
413 VA_TO_PA_POINTER(Lpagetable, boot_pt1)
414
415
416 .Lstart:
417         .word   _edata                  /* Note that these three items are */
418         .word   _ebss                   /* loaded with a single ldmia and */
419         .word   svcstk                  /* must remain in order together. */
420
421 .Lmainreturned:
422         .asciz  "main() returned"
423         .align  2
424
425         .bss
426 svcstk:
427         .space  INIT_ARM_STACK_SIZE * MAXCPU
428
429 /*
430  * Memory for the initial pagetable. We are unable to place this in
431  * the bss as this will be cleared after the table is loaded.
432  */
433         .section ".init_pagetable"
434         .align  14 /* 16KiB aligned */
435         .globl  boot_pt1
436 boot_pt1:
437         .space  L1_TABLE_SIZE
438
439         .text
440         .align  2
441
442 .Lcpufuncs:
443         .word   _C_LABEL(cpufuncs)
444
445 #if defined(SMP)
446
447 ASENTRY_NP(mpentry)
448         /* Make sure interrupts are disabled. */
449         cpsid   ifa
450
451         LEAVE_HYP
452
453         /* Setup core, disable all caches. */
454         mrc     CP15_SCTLR(r0)
455         bic     r0, #CPU_CONTROL_MMU_ENABLE
456         bic     r0, #CPU_CONTROL_AFLT_ENABLE
457         bic     r0, #CPU_CONTROL_DC_ENABLE
458         bic     r0, #CPU_CONTROL_IC_ENABLE
459         bic     r0, #CPU_CONTROL_BPRD_ENABLE
460         bic     r0, #CPU_CONTROL_SW_ENABLE
461         orr     r0, #CPU_CONTROL_UNAL_ENABLE
462         orr     r0, #CPU_CONTROL_VECRELOC
463         mcr     CP15_SCTLR(r0)
464         DSB
465         ISB
466
467         /* Invalidate L1 cache I+D cache */
468         bl      dcache_inv_pou_all
469         mcr     CP15_ICIALLU
470         DSB
471         ISB
472
473         /* Find the delta between VA and PA */
474         adr     r0, Lpagetable
475         bl      translate_va_to_pa
476
477         bl      init_mmu
478
479         adr     r1, .Lstart+8           /* Get initstack pointer from */
480         ldr     sp, [r1]                /* startup data. */
481         mrc     CP15_MPIDR(r0)          /* Get processor id number. */
482         and     r0, r0, #0x0f
483         mov     r1, #INIT_ARM_STACK_SIZE
484         mul     r2, r1, r0              /* Point sp to initstack */
485         add     sp, sp, r2              /* area for this processor. */
486
487         /* Switch to virtual addresses. */
488         ldr     pc, =1f
489 1:
490         mov     fp, #0                  /* trace back starts here */
491         bl      _C_LABEL(init_secondary)/* Off we go, cpu id in r0. */
492
493         adr     r0, .Lmpreturned
494         b       _C_LABEL(panic)
495         /* NOTREACHED */
496 END(mpentry)
497
498 .Lmpreturned:
499         .asciz  "init_secondary() returned"
500         .align  2
501 #endif
502
503 ENTRY_NP(cpu_halt)
504
505         /* XXX re-implement !!! */
506         cpsid   ifa
507         bl      dcache_wbinv_poc_all
508
509         ldr     r4, .Lcpu_reset_address
510         ldr     r4, [r4]
511         teq     r4, #0
512         movne   pc, r4
513 1:
514         WFI
515         b       1b
516
517         /*
518          * _cpu_reset_address contains the address to branch to, to complete
519          * the cpu reset after turning the MMU off
520          * This variable is provided by the hardware specific code
521          */
522 .Lcpu_reset_address:
523         .word   _C_LABEL(cpu_reset_address)
524 END(cpu_halt)
525
526
527 /*
528  * setjump + longjmp
529  */
530 ENTRY(setjmp)
531         stmia   r0, {r4-r14}
532         mov     r0, #0x00000000
533         RET
534 END(setjmp)
535
536 ENTRY(longjmp)
537         ldmia   r0, {r4-r14}
538         mov     r0, #0x00000001
539         RET
540 END(longjmp)
541
542         .data
543         .global _C_LABEL(esym)
544 _C_LABEL(esym): .word   _C_LABEL(end)
545
546 ENTRY_NP(abort)
547         b       _C_LABEL(abort)
548 END(abort)
549
550 ENTRY_NP(sigcode)
551         mov     r0, sp
552         add     r0, r0, #SIGF_UC
553
554         /*
555          * Call the sigreturn system call.
556          *
557          * We have to load r7 manually rather than using
558          * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is
559          * correct. Using the alternative places esigcode at the address
560          * of the data rather than the address one past the data.
561          */
562
563         ldr     r7, [pc, #12]   /* Load SYS_sigreturn */
564         swi     SYS_sigreturn
565
566         /* Well if that failed we better exit quick ! */
567
568         ldr     r7, [pc, #8]    /* Load SYS_exit */
569         swi     SYS_exit
570
571         /* Branch back to retry SYS_sigreturn */
572         b       . - 16
573 END(sigcode)
574         .word   SYS_sigreturn
575         .word   SYS_exit
576
577         .align  2
578         .global _C_LABEL(esigcode)
579                 _C_LABEL(esigcode):
580
581         .data
582         .global szsigcode
583 szsigcode:
584         .long esigcode-sigcode
585
586 /* End of locore.S */