xref: /freebsd/sys/arm/arm/locore.S (revision 38d120bc13ac1de5b739b67b87016b9122149374)
1/*	$NetBSD: locore.S,v 1.14 2003/04/20 16:21:40 thorpej Exp $	*/
2
3/*-
4 * Copyright 2011 Semihalf
5 * Copyright (C) 1994-1997 Mark Brinicombe
6 * Copyright (C) 1994 Brini
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 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by Brini.
20 * 4. The name of Brini may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL BRINI BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 */
35
36#include "assym.s"
37#include <sys/syscall.h>
38#include <machine/asm.h>
39#include <machine/armreg.h>
40#include <machine/cpuconf.h>
41#include <machine/pte.h>
42
43__FBSDID("$FreeBSD$");
44
45/*
46 * Sanity check the configuration.
47 * FLASHADDR and LOADERRAMADDR depend on PHYSADDR in some cases.
48 * ARMv4 and ARMv5 make assumptions on where they are loaded.
49 *
50 * TODO: Fix the ARMv4/v5 case.
51 */
52#if (defined(FLASHADDR) || defined(LOADERRAMADDR) || !defined(_ARM_ARCH_6)) && \
53    !defined(PHYSADDR)
54#error PHYSADDR must be defined for this configuration
55#endif
56
57/* What size should this really be ? It is only used by initarm() */
58#define INIT_ARM_STACK_SIZE	(2048 * 4)
59
60#define	CPWAIT_BRANCH							 \
61	sub	pc, pc, #4
62
63#define	CPWAIT(tmp)							 \
64	mrc	p15, 0, tmp, c2, c0, 0	/* arbitrary read of CP15 */	;\
65	mov	tmp, tmp		/* wait for it to complete */	;\
66	CPWAIT_BRANCH			/* branch to next insn */
67
68/*
69 * This is for libkvm, and should be the address of the beginning
70 * of the kernel text segment (not necessarily the same as kernbase).
71 *
72 * These are being phased out. Newer copies of libkvm don't need these
73 * values as the information is added to the core file by inspecting
74 * the running kernel.
75 */
76	.text
77	.align	0
78#ifdef PHYSADDR
79.globl kernbase
80.set kernbase,KERNBASE
81.globl physaddr
82.set physaddr,PHYSADDR
83#endif
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
98btext:
99ASENTRY_NP(_start)
100	STOP_UNWINDING		/* Can't unwind into the bootloader! */
101
102	mov	r9, r0		/* 0 or boot mode from boot2 */
103	mov	r8, r1		/* Save Machine type */
104	mov	ip, r2		/* Save meta data */
105	mov	fp, r3		/* Future expansion */
106
107	/* Make sure interrupts are disabled. */
108	mrs	r7, cpsr
109	orr	r7, r7, #(PSR_I | PSR_F)
110	msr	cpsr_c, r7
111
112#if defined (FLASHADDR) && defined(LOADERRAMADDR)
113	/* Check if we're running from flash. */
114	ldr	r7, =FLASHADDR
115	/*
116	 * If we're running with MMU disabled, test against the
117	 * physical address instead.
118	 */
119	mrc     p15, 0, r2, c1, c0, 0
120	ands	r2, r2, #CPU_CONTROL_MMU_ENABLE
121	ldreq	r6, =PHYSADDR
122	ldrne	r6, =LOADERRAMADDR
123	cmp	r7, r6
124	bls 	flash_lower
125	cmp	r7, pc
126	bhi	from_ram
127	b	do_copy
128
129flash_lower:
130	cmp	r6, pc
131	bls	from_ram
132do_copy:
133	ldr	r7, =KERNBASE
134	adr	r1, _start
135	ldr	r0, Lreal_start
136	ldr	r2, Lend
137	sub	r2, r2, r0
138	sub	r0, r0, r7
139	add	r0, r0, r6
140	mov	r4, r0
141	bl	memcpy
142	ldr	r0, Lram_offset
143	add	pc, r4, r0
144Lram_offset:	.word from_ram-_C_LABEL(_start)
145from_ram:
146	nop
147#endif
148
149disable_mmu:
150	/* Disable MMU for a while */
151	mrc     p15, 0, r2, c1, c0, 0
152	bic	r2, r2, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
153	    CPU_CONTROL_WBUF_ENABLE)
154	bic	r2, r2, #(CPU_CONTROL_IC_ENABLE)
155	bic	r2, r2, #(CPU_CONTROL_BPRD_ENABLE)
156	mcr     p15, 0, r2, c1, c0, 0
157
158	nop
159	nop
160	nop
161	CPWAIT(r0)
162
163Lunmapped:
164	/*
165	 * Build page table from scratch.
166	 */
167
168	/* Find the delta between VA and PA */
169	adr	r0, Lpagetable
170	bl	translate_va_to_pa
171
172#ifndef _ARM_ARCH_6
173	/*
174	 * Some of the older ports (the various XScale, mostly) assume
175	 * that the memory before the kernel is mapped, and use it for
176	 * the various stacks, page tables, etc. For those CPUs, map the
177	 * 64 first MB of RAM, as it used to be.
178	 */
179	/*
180	 * Map PA == VA
181	 */
182	ldr     r5, =PHYSADDR
183	mov     r1, r5
184	mov     r2, r5
185	/* Map 64MiB, preserved over calls to build_pagetables */
186	mov     r3, #64
187	bl      build_pagetables
188
189	/* Create the kernel map to jump to */
190	mov     r1, r5
191	ldr     r2, =(KERNBASE)
192	bl      build_pagetables
193	ldr	r5, =(KERNPHYSADDR)
194#else
195	/*
196	 * Map PA == VA
197	 */
198	/* Find the start kernels load address */
199	adr	r5, _start
200	ldr	r2, =(L1_S_OFFSET)
201	bic	r5, r2
202	mov	r1, r5
203	mov	r2, r5
204	/* Map 64MiB, preserved over calls to build_pagetables */
205	mov	r3, #64
206	bl	build_pagetables
207
208	/* Create the kernel map to jump to */
209	mov	r1, r5
210	ldr	r2, =(KERNVIRTADDR)
211	bl	build_pagetables
212#endif
213
214#if defined(SOCDEV_PA) && defined(SOCDEV_VA)
215	/* Create the custom map */
216	ldr	r1, =SOCDEV_PA
217	ldr	r2, =SOCDEV_VA
218	bl	build_pagetables
219#endif
220
221#if defined(SMP)
222	orr 	r0, r0, #2		/* Set TTB shared memory flag */
223#endif
224	mcr	p15, 0, r0, c2, c0, 0	/* Set TTB */
225	mcr	p15, 0, r0, c8, c7, 0	/* Flush TLB */
226
227#if defined(CPU_ARM1136) || defined(CPU_ARM1176) || defined(CPU_CORTEXA) || defined(CPU_MV_PJ4B) || defined(CPU_KRAIT)
228	mov	r0, #0
229	mcr	p15, 0, r0, c13, c0, 1	/* Set ASID to 0 */
230#endif
231
232	/* Set the Domain Access register.  Very important! */
233	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
234	mcr	p15, 0, r0, c3, c0, 0
235	/*
236	 * Enable MMU.
237	 * On armv6 enable extended page tables, and set alignment checking
238	 * to modulo-4 (CPU_CONTROL_UNAL_ENABLE) for the ldrd/strd
239	 * instructions emitted by clang.
240	 */
241	mrc	p15, 0, r0, c1, c0, 0
242#ifdef _ARM_ARCH_6
243	orr	r0, r0, #(CPU_CONTROL_V6_EXTPAGE | CPU_CONTROL_UNAL_ENABLE)
244	orr	r0, r0, #(CPU_CONTROL_AFLT_ENABLE)
245	orr	r0, r0, #(CPU_CONTROL_AF_ENABLE)
246#endif
247	orr	r0, r0, #(CPU_CONTROL_MMU_ENABLE)
248	mcr	p15, 0, r0, c1, c0, 0
249	nop
250	nop
251	nop
252	CPWAIT(r0)
253
254mmu_done:
255	nop
256	adr	r1, .Lstart
257	ldmia	r1, {r1, r2, sp}	/* Set initial stack and */
258	sub	r2, r2, r1		/* get zero init data */
259	mov	r3, #0
260.L1:
261	str	r3, [r1], #0x0004	/* get zero init data */
262	subs	r2, r2, #4
263	bgt	.L1
264	ldr	pc, .Lvirt_done
265
266virt_done:
267	mov	r1, #28			/* loader info size is 28 bytes also second arg */
268	subs	sp, sp, r1		/* allocate arm_boot_params struct on stack */
269	mov	r0, sp			/* loader info pointer is first arg */
270	bic	sp, sp, #7		/* align stack to 8 bytes */
271	str	r1, [r0]		/* Store length of loader info */
272	str	r9, [r0, #4]		/* Store r0 from boot loader */
273	str	r8, [r0, #8]		/* Store r1 from boot loader */
274	str	ip, [r0, #12]		/* store r2 from boot loader */
275	str	fp, [r0, #16]		/* store r3 from boot loader */
276	str	r5, [r0, #20]		/* store the physical address */
277	adr	r4, Lpagetable		/* load the pagetable address */
278	ldr	r5, [r4, #4]
279	str	r5, [r0, #24]		/* store the pagetable address */
280	mov	fp, #0			/* trace back starts here */
281	bl	_C_LABEL(initarm)	/* Off we go */
282
283	/* init arm will return the new stack pointer. */
284	mov	sp, r0
285
286	bl	_C_LABEL(mi_startup)		/* call mi_startup()! */
287
288	adr	r0, .Lmainreturned
289	b	_C_LABEL(panic)
290	/* NOTREACHED */
291END(_start)
292
293#define VA_TO_PA_POINTER(name, table)	 \
294name:					;\
295	.word	.			;\
296	.word	table
297
298/*
299 * Returns the physical address of a magic va to pa pointer.
300 * r0     - The pagetable data pointer. This must be built using the
301 *          VA_TO_PA_POINTER macro.
302 *          e.g.
303 *            VA_TO_PA_POINTER(Lpagetable, pagetable)
304 *            ...
305 *            adr  r0, Lpagetable
306 *            bl   translate_va_to_pa
307 *            r0 will now contain the physical address of pagetable
308 * r1, r2 - Trashed
309 */
310translate_va_to_pa:
311	ldr	r1, [r0]
312	sub	r2, r1, r0
313	/* At this point: r2 = VA - PA */
314
315	/*
316	 * Find the physical address of the table. After these two
317	 * instructions:
318	 * r1 = va(pagetable)
319	 *
320	 * r0 = va(pagetable) - (VA - PA)
321	 *    = va(pagetable) - VA + PA
322	 *    = pa(pagetable)
323	 */
324	ldr	r1, [r0, #4]
325	sub	r0, r1, r2
326	RET
327
328/*
329 * Builds the page table
330 * r0 - The table base address
331 * r1 - The physical address (trashed)
332 * r2 - The virtual address (trashed)
333 * r3 - The number of 1MiB sections
334 * r4 - Trashed
335 *
336 * Addresses must be 1MiB aligned
337 */
338build_pagetables:
339	/* Set the required page attributed */
340	ldr	r4, =(L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW))
341#if defined(SMP)
342	orr	r4, #(L1_SHARED)
343#endif
344	orr	r1, r4
345
346	/* Move the virtual address to the correct bit location */
347	lsr	r2, #(L1_S_SHIFT - 2)
348
349	mov	r4, r3
3501:
351	str	r1, [r0, r2]
352	add	r2, r2, #4
353	add	r1, r1, #(L1_S_SIZE)
354	adds	r4, r4, #-1
355	bhi	1b
356
357	RET
358
359VA_TO_PA_POINTER(Lpagetable, pagetable)
360
361Lreal_start:
362	.word	_start
363Lend:
364	.word	_edata
365
366.Lstart:
367	.word	_edata
368	.word	_ebss
369	.word	svcstk + INIT_ARM_STACK_SIZE
370
371.Lvirt_done:
372	.word	virt_done
373
374.Lmainreturned:
375	.asciz	"main() returned"
376	.align	0
377
378	.bss
379svcstk:
380	.space	INIT_ARM_STACK_SIZE
381
382/*
383 * Memory for the initial pagetable. We are unable to place this in
384 * the bss as this will be cleared after the table is loaded.
385 */
386	.section ".init_pagetable"
387	.align	14 /* 16KiB aligned */
388pagetable:
389	.space	L1_TABLE_SIZE
390
391	.text
392	.align	0
393
394.Lcpufuncs:
395	.word	_C_LABEL(cpufuncs)
396
397#if defined(SMP)
398
399.Lmpvirt_done:
400	.word	mpvirt_done
401VA_TO_PA_POINTER(Lstartup_pagetable_secondary, temp_pagetable)
402
403ASENTRY_NP(mpentry)
404
405	/* Make sure interrupts are disabled. */
406	mrs	r7, cpsr
407	orr	r7, r7, #(PSR_I | PSR_F)
408	msr	cpsr_c, r7
409
410	/* Disable MMU.  It should be disabled already, but make sure. */
411	mrc	p15, 0, r2, c1, c0, 0
412	bic	r2, r2, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
413	    CPU_CONTROL_WBUF_ENABLE)
414	bic	r2, r2, #(CPU_CONTROL_IC_ENABLE)
415	bic	r2, r2, #(CPU_CONTROL_BPRD_ENABLE)
416	mcr	p15, 0, r2, c1, c0, 0
417	nop
418	nop
419	nop
420	CPWAIT(r0)
421
422#if ARM_MMU_V6
423	bl	armv6_idcache_inv_all	/* Modifies r0 only */
424#elif ARM_MMU_V7
425	bl	armv7_idcache_inv_all	/* Modifies r0-r3, ip */
426#endif
427
428	/* Load the page table physical address */
429	adr	r0, Lstartup_pagetable_secondary
430	bl	translate_va_to_pa
431	/* Load the address the secondary page table */
432	ldr	r0, [r0]
433
434	orr 	r0, r0, #2		/* Set TTB shared memory flag */
435	mcr	p15, 0, r0, c2, c0, 0	/* Set TTB */
436	mcr	p15, 0, r0, c8, c7, 0	/* Flush TLB */
437
438	mov	r0, #0
439	mcr	p15, 0, r0, c13, c0, 1	/* Set ASID to 0 */
440
441	/* Set the Domain Access register.  Very important! */
442	mov	r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
443	mcr	p15, 0, r0, c3, c0, 0
444	/* Enable MMU */
445	mrc	p15, 0, r0, c1, c0, 0
446	orr	r0, r0, #CPU_CONTROL_V6_EXTPAGE
447	orr	r0, r0, #CPU_CONTROL_AF_ENABLE
448	orr	r0, r0, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
449	    CPU_CONTROL_WBUF_ENABLE)
450	orr	r0, r0, #(CPU_CONTROL_IC_ENABLE)
451	orr	r0, r0, #(CPU_CONTROL_BPRD_ENABLE)
452	mcr	p15, 0, r0, c1, c0, 0
453	nop
454	nop
455	nop
456	CPWAIT(r0)
457
458	adr	r1, .Lstart
459	ldmia	r1, {r1, r2, sp}	/* Set initial stack and */
460	mrc	p15, 0, r0, c0, c0, 5
461	and	r0, r0, #15
462	mov	r1, #2048
463	mul	r2, r1, r0
464	sub	sp, sp, r2
465	str	r1, [sp]
466	ldr	pc, .Lmpvirt_done
467
468mpvirt_done:
469
470	mov	fp, #0			/* trace back starts here */
471	bl	_C_LABEL(init_secondary)	/* Off we go */
472
473	adr	r0, .Lmpreturned
474	b	_C_LABEL(panic)
475	/* NOTREACHED */
476
477.Lmpreturned:
478	.asciz	"init_secondary() returned"
479	.align	0
480END(mpentry)
481#endif
482
483ENTRY_NP(cpu_halt)
484	mrs     r2, cpsr
485	bic	r2, r2, #(PSR_MODE)
486	orr     r2, r2, #(PSR_SVC32_MODE)
487	orr	r2, r2, #(PSR_I | PSR_F)
488	msr     cpsr_fsxc, r2
489
490	ldr	r4, .Lcpu_reset_address
491	ldr	r4, [r4]
492
493	ldr	r0, .Lcpufuncs
494	mov	lr, pc
495	ldr	pc, [r0, #CF_IDCACHE_WBINV_ALL]
496	mov	lr, pc
497	ldr	pc, [r0, #CF_L2CACHE_WBINV_ALL]
498
499	/*
500	 * Load the cpu_reset_needs_v4_MMU_disable flag to determine if it's
501	 * necessary.
502	 */
503
504	ldr	r1, .Lcpu_reset_needs_v4_MMU_disable
505	ldr	r1, [r1]
506	cmp	r1, #0
507	mov	r2, #0
508
509	/*
510	 * MMU & IDC off, 32 bit program & data space
511	 * Hurl ourselves into the ROM
512	 */
513	mov	r0, #(CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE)
514	mcr     p15, 0, r0, c1, c0, 0
515	mcrne   p15, 0, r2, c8, c7, 0 	/* nail I+D TLB on ARMv4 and greater */
516	mov     pc, r4
517
518	/*
519	 * _cpu_reset_address contains the address to branch to, to complete
520	 * the cpu reset after turning the MMU off
521	 * This variable is provided by the hardware specific code
522	 */
523.Lcpu_reset_address:
524	.word	_C_LABEL(cpu_reset_address)
525
526	/*
527	 * cpu_reset_needs_v4_MMU_disable contains a flag that signals if the
528	 * v4 MMU disable instruction needs executing... it is an illegal instruction
529	 * on f.e. ARM6/7 that locks up the computer in an endless illegal
530	 * instruction / data-abort / reset loop.
531	 */
532.Lcpu_reset_needs_v4_MMU_disable:
533	.word	_C_LABEL(cpu_reset_needs_v4_MMU_disable)
534END(cpu_halt)
535
536
537/*
538 * setjump + longjmp
539 */
540ENTRY(setjmp)
541	stmia	r0, {r4-r14}
542	mov	r0, #0x00000000
543	RET
544END(setjmp)
545
546ENTRY(longjmp)
547	ldmia	r0, {r4-r14}
548	mov	r0, #0x00000001
549	RET
550END(longjmp)
551
552	.data
553	.global _C_LABEL(esym)
554_C_LABEL(esym):	.word	_C_LABEL(end)
555
556ENTRY_NP(abort)
557	b	_C_LABEL(abort)
558END(abort)
559
560ENTRY_NP(sigcode)
561	mov	r0, sp
562	add	r0, r0, #SIGF_UC
563
564	/*
565	 * Call the sigreturn system call.
566	 *
567	 * We have to load r7 manually rather than using
568	 * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is
569	 * correct. Using the alternative places esigcode at the address
570	 * of the data rather than the address one past the data.
571	 */
572
573	ldr	r7, [pc, #12]	/* Load SYS_sigreturn */
574	swi	SYS_sigreturn
575
576	/* Well if that failed we better exit quick ! */
577
578	ldr	r7, [pc, #8]	/* Load SYS_exit */
579	swi	SYS_exit
580
581	/* Branch back to retry SYS_sigreturn */
582	b	. - 16
583END(sigcode)
584	.word	SYS_sigreturn
585	.word	SYS_exit
586
587	.align	0
588	.global _C_LABEL(esigcode)
589		_C_LABEL(esigcode):
590
591	.data
592	.global szsigcode
593szsigcode:
594	.long esigcode-sigcode
595
596/* End of locore.S */
597