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