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