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/* What size should this really be ? It is only used by initarm() */
46#define INIT_ARM_STACK_SIZE	(2048 * 4)
47
48#define	CPWAIT_BRANCH							 \
49	sub	pc, pc, #4
50
51#define	CPWAIT(tmp)							 \
52	mrc	p15, 0, tmp, c2, c0, 0	/* arbitrary read of CP15 */	;\
53	mov	tmp, tmp		/* wait for it to complete */	;\
54	CPWAIT_BRANCH			/* branch to next insn */
55
56/*
57 * This is for kvm_mkdb, and should be the address of the beginning
58 * of the kernel text segment (not necessarily the same as kernbase).
59 */
60	.text
61	.align	0
62.globl kernbase
63.set kernbase,KERNBASE
64.globl physaddr
65.set physaddr,PHYSADDR
66
67/*
68 * On entry for FreeBSD boot ABI:
69 *	r0 - metadata pointer or 0 (boothowto on AT91's boot2)
70 *	r1 - if (r0 == 0) then metadata pointer
71 * On entry for Linux boot ABI:
72 *	r0 - 0
73 *	r1 - machine type (passed as arg2 to initarm)
74 *	r2 - Pointer to a tagged list or dtb image (phys addr) (passed as arg1 initarm)
75 *
76 * For both types of boot we gather up the args, put them in a struct arm_boot_params
77 * structure and pass that to initarm.
78 */
79	.globl	btext
80btext:
81ASENTRY_NP(_start)
82	STOP_UNWINDING		/* Can't unwind into the bootloader! */
83
84	mov	r9, r0		/* 0 or boot mode from boot2 */
85	mov	r8, r1		/* Save Machine type */
86	mov	ip, r2		/* Save meta data */
87	mov	fp, r3		/* Future expantion */
88
89	/* Make sure interrupts are disabled. */
90	mrs	r7, cpsr
91	orr	r7, r7, #(I32_bit|F32_bit)
92	msr	cpsr_c, r7
93
94#if defined (FLASHADDR) && defined(LOADERRAMADDR)
95	/* Check if we're running from flash. */
96	ldr	r7, =FLASHADDR
97	/*
98	 * If we're running with MMU disabled, test against the
99	 * physical address instead.
100	 */
101	mrc     p15, 0, r2, c1, c0, 0
102	ands	r2, r2, #CPU_CONTROL_MMU_ENABLE
103	ldreq	r6, =PHYSADDR
104	ldrne	r6, =LOADERRAMADDR
105	cmp	r7, r6
106	bls 	flash_lower
107	cmp	r7, pc
108	bhi	from_ram
109	b	do_copy
110
111flash_lower:
112	cmp	r6, pc
113	bls	from_ram
114do_copy:
115	ldr	r7, =KERNBASE
116	adr	r1, _start
117	ldr	r0, Lreal_start
118	ldr	r2, Lend
119	sub	r2, r2, r0
120	sub	r0, r0, r7
121	add	r0, r0, r6
122	mov	r4, r0
123	bl	memcpy
124	ldr	r0, Lram_offset
125	add	pc, r4, r0
126Lram_offset:	.word from_ram-_C_LABEL(_start)
127from_ram:
128	nop
129#endif
130	adr	r7, Lunmapped
131	bic     r7, r7, #0xf0000000
132	orr     r7, r7, #PHYSADDR
133
134
135disable_mmu:
136	/* Disable MMU for a while */
137	mrc     p15, 0, r2, c1, c0, 0
138	bic	r2, r2, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
139	    CPU_CONTROL_WBUF_ENABLE)
140	bic	r2, r2, #(CPU_CONTROL_IC_ENABLE)
141	bic	r2, r2, #(CPU_CONTROL_BPRD_ENABLE)
142	mcr     p15, 0, r2, c1, c0, 0
143
144	nop
145	nop
146	nop
147	mov	pc, r7
148Lunmapped:
149	/*
150	 * Build page table from scratch.
151	 */
152
153	/* Find the delta between VA and PA */
154	adr	r0, Lpagetable
155	ldr	r1, [r0]
156	sub	r2, r1, r0
157	/* At this point: r2 = VA - PA */
158
159	/*
160	 * Find the physical address of the table. After these two
161	 * instructions:
162	 * r1 = va(pagetable)
163	 *
164	 * r0 = va(pagetable) - (VA - PA)
165	 *    = va(pagetable) - VA + PA
166	 *    = pa(pagetable)
167	 */
168	ldr	r1, [r0, #4]
169	sub	r0, r1, r2
170
171	/*
172	 * Map PA == VA
173	 */
174	/* Find the start kernels load address */
175	adr	r5, _start
176	ldr	r2, =(L1_S_OFFSET)
177	bic	r5, r2
178	mov	r1, r5
179	mov	r2, r5
180	/* Map 64MiB, preserved over calls to build_pagetables */
181	mov	r3, #64
182	bl	build_pagetables
183
184	/* Create the kernel map to jump to */
185	mov	r1, r5
186	ldr	r2, =(KERNVIRTADDR)
187	bl	build_pagetables
188
189#if defined(SOCDEV_PA) && defined(SOCDEV_VA)
190	/* Create the custom map */
191	ldr	r1, =SOCDEV_PA
192	ldr	r2, =SOCDEV_VA
193	bl	build_pagetables
194#endif
195
196#if defined(SMP)
197	orr 	r0, r0, #2		/* Set TTB shared memory flag */
198#endif
199	mcr	p15, 0, r0, c2, c0, 0	/* Set TTB */
200	mcr	p15, 0, r0, c8, c7, 0	/* Flush TLB */
201
202#if defined(CPU_ARM1136) || defined(CPU_ARM1176) || defined(CPU_CORTEXA) || defined(CPU_MV_PJ4B) || defined(CPU_KRAIT)
203	mov	r0, #0
204	mcr	p15, 0, r0, c13, c0, 1	/* Set ASID to 0 */
205#endif
206
207	/* Set the Domain Access register.  Very important! */
208	mov     r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
209	mcr	p15, 0, r0, c3, c0, 0
210	/*
211	 * Enable MMU.
212	 * On armv6 enable extended page tables, and set alignment checking
213	 * to modulo-4 (CPU_CONTROL_UNAL_ENABLE) for the ldrd/strd
214	 * instructions emitted by clang.
215	 */
216	mrc	p15, 0, r0, c1, c0, 0
217#ifdef _ARM_ARCH_6
218	orr	r0, r0, #(CPU_CONTROL_V6_EXTPAGE | CPU_CONTROL_UNAL_ENABLE)
219	orr	r0, r0, #(CPU_CONTROL_AFLT_ENABLE)
220	orr	r0, r0, #(CPU_CONTROL_AF_ENABLE)
221#endif
222	orr	r0, r0, #(CPU_CONTROL_MMU_ENABLE)
223	mcr	p15, 0, r0, c1, c0, 0
224	nop
225	nop
226	nop
227	CPWAIT(r0)
228
229mmu_done:
230	nop
231	adr	r1, .Lstart
232	ldmia	r1, {r1, r2, sp}	/* Set initial stack and */
233	sub	r2, r2, r1		/* get zero init data */
234	mov	r3, #0
235.L1:
236	str	r3, [r1], #0x0004	/* get zero init data */
237	subs	r2, r2, #4
238	bgt	.L1
239	ldr	pc, .Lvirt_done
240
241virt_done:
242	mov	r1, #28			/* loader info size is 28 bytes also second arg */
243	subs	sp, sp, r1		/* allocate arm_boot_params struct on stack */
244	mov	r0, sp			/* loader info pointer is first arg */
245	bic	sp, sp, #7		/* align stack to 8 bytes */
246	str	r1, [r0]		/* Store length of loader info */
247	str	r9, [r0, #4]		/* Store r0 from boot loader */
248	str	r8, [r0, #8]		/* Store r1 from boot loader */
249	str	ip, [r0, #12]		/* store r2 from boot loader */
250	str	fp, [r0, #16]		/* store r3 from boot loader */
251	str	r5, [r0, #20]		/* store the physical address */
252	adr	r4, Lpagetable		/* load the pagetable address */
253	ldr	r5, [r4, #4]
254	str	r5, [r0, #24]		/* store the pagetable address */
255	mov	fp, #0			/* trace back starts here */
256	bl	_C_LABEL(initarm)	/* Off we go */
257
258	/* init arm will return the new stack pointer. */
259	mov	sp, r0
260
261	bl	_C_LABEL(mi_startup)		/* call mi_startup()! */
262
263	adr	r0, .Lmainreturned
264	b	_C_LABEL(panic)
265	/* NOTREACHED */
266END(_start)
267
268/*
269 * Builds the page table
270 * r0 - The table base address
271 * r1 - The physical address (trashed)
272 * r2 - The virtual address (trashed)
273 * r3 - The number of 1MiB sections
274 * r4 - Trashed
275 *
276 * Addresses must be 1MiB aligned
277 */
278build_pagetables:
279	/* Set the required page attributed */
280	ldr	r4, =(L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW))
281#if defined(SMP)
282	orr	r4, #(L1_SHARED)
283#endif
284	orr	r1, r4
285
286	/* Move the virtual address to the correct bit location */
287	lsr	r2, #(L1_S_SHIFT - 2)
288
289	mov	r4, r3
2901:
291	str	r1, [r0, r2]
292	add	r2, r2, #4
293	add	r1, r1, #(L1_S_SIZE)
294	adds	r4, r4, #-1
295	bhi	1b
296
297	RET
298
299Lpagetable:
300	.word	.
301	.word	pagetable
302
303Lvirtaddr:
304	.word	KERNVIRTADDR
305Lphysaddr:
306	.word	KERNPHYSADDR
307Lreal_start:
308	.word	_start
309Lend:
310	.word	_edata
311
312.Lstart:
313	.word	_edata
314	.word	_ebss
315	.word	svcstk + INIT_ARM_STACK_SIZE
316
317.Lvirt_done:
318	.word	virt_done
319
320.Lmainreturned:
321	.asciz	"main() returned"
322	.align	0
323
324	.bss
325svcstk:
326	.space	INIT_ARM_STACK_SIZE
327
328/*
329 * Memory for the initial pagetable. We are unable to place this in
330 * the bss as this will be cleared after the table is loaded.
331 */
332	.section ".init_pagetable"
333	.align	14 /* 16KiB aligned */
334pagetable:
335	.space	L1_TABLE_SIZE
336
337	.text
338	.align	0
339
340.Lcpufuncs:
341	.word	_C_LABEL(cpufuncs)
342
343#if defined(SMP)
344
345.Lmpvirt_done:
346	.word	mpvirt_done
347Lstartup_pagetable_secondary:
348	.word	temp_pagetable
349
350ASENTRY_NP(mpentry)
351
352	/* Make sure interrupts are disabled. */
353	mrs	r7, cpsr
354	orr	r7, r7, #(I32_bit|F32_bit)
355	msr	cpsr_c, r7
356
357	/* Disable MMU.  It should be disabled already, but make sure. */
358	mrc	p15, 0, r2, c1, c0, 0
359	bic	r2, r2, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
360	    CPU_CONTROL_WBUF_ENABLE)
361	bic	r2, r2, #(CPU_CONTROL_IC_ENABLE)
362	bic	r2, r2, #(CPU_CONTROL_BPRD_ENABLE)
363	mcr	p15, 0, r2, c1, c0, 0
364	nop
365	nop
366	nop
367	CPWAIT(r0)
368
369#if ARM_MMU_V6
370	bl	armv6_idcache_inv_all	/* Modifies r0 only */
371#elif ARM_MMU_V7
372	bl	armv7_idcache_inv_all	/* Modifies r0-r3, ip */
373#endif
374
375	ldr	r0, Lstartup_pagetable_secondary
376	bic	r0, r0, #0xf0000000
377	orr	r0, r0, #PHYSADDR
378	ldr	r0, [r0]
379	orr 	r0, r0, #2		/* Set TTB shared memory flag */
380	mcr	p15, 0, r0, c2, c0, 0	/* Set TTB */
381	mcr	p15, 0, r0, c8, c7, 0	/* Flush TLB */
382
383	mov	r0, #0
384	mcr	p15, 0, r0, c13, c0, 1	/* Set ASID to 0 */
385
386	/* Set the Domain Access register.  Very important! */
387	mov	r0, #((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT)
388	mcr	p15, 0, r0, c3, c0, 0
389	/* Enable MMU */
390	mrc	p15, 0, r0, c1, c0, 0
391	orr	r0, r0, #CPU_CONTROL_V6_EXTPAGE
392	orr	r0, r0, #CPU_CONTROL_AF_ENABLE
393	orr	r0, r0, #(CPU_CONTROL_MMU_ENABLE | CPU_CONTROL_DC_ENABLE |\
394	    CPU_CONTROL_WBUF_ENABLE)
395	orr	r0, r0, #(CPU_CONTROL_IC_ENABLE)
396	orr	r0, r0, #(CPU_CONTROL_BPRD_ENABLE)
397	mcr	p15, 0, r0, c1, c0, 0
398	nop
399	nop
400	nop
401	CPWAIT(r0)
402
403	adr	r1, .Lstart
404	ldmia	r1, {r1, r2, sp}	/* Set initial stack and */
405	mrc	p15, 0, r0, c0, c0, 5
406	and	r0, r0, #15
407	mov	r1, #2048
408	mul	r2, r1, r0
409	sub	sp, sp, r2
410	str	r1, [sp]
411	ldr	pc, .Lmpvirt_done
412
413mpvirt_done:
414
415	mov	fp, #0			/* trace back starts here */
416	bl	_C_LABEL(init_secondary)	/* Off we go */
417
418	adr	r0, .Lmpreturned
419	b	_C_LABEL(panic)
420	/* NOTREACHED */
421
422.Lmpreturned:
423	.asciz	"init_secondary() returned"
424	.align	0
425END(mpentry)
426#endif
427
428ENTRY_NP(cpu_halt)
429	mrs     r2, cpsr
430	bic	r2, r2, #(PSR_MODE)
431	orr     r2, r2, #(PSR_SVC32_MODE)
432	orr	r2, r2, #(I32_bit | F32_bit)
433	msr     cpsr_fsxc, r2
434
435	ldr	r4, .Lcpu_reset_address
436	ldr	r4, [r4]
437
438	ldr	r0, .Lcpufuncs
439	mov	lr, pc
440	ldr	pc, [r0, #CF_IDCACHE_WBINV_ALL]
441	mov	lr, pc
442	ldr	pc, [r0, #CF_L2CACHE_WBINV_ALL]
443
444	/*
445	 * Load the cpu_reset_needs_v4_MMU_disable flag to determine if it's
446	 * necessary.
447	 */
448
449	ldr	r1, .Lcpu_reset_needs_v4_MMU_disable
450	ldr	r1, [r1]
451	cmp	r1, #0
452	mov	r2, #0
453
454	/*
455	 * MMU & IDC off, 32 bit program & data space
456	 * Hurl ourselves into the ROM
457	 */
458	mov	r0, #(CPU_CONTROL_32BP_ENABLE | CPU_CONTROL_32BD_ENABLE)
459	mcr     15, 0, r0, c1, c0, 0
460	mcrne   15, 0, r2, c8, c7, 0 	/* nail I+D TLB on ARMv4 and greater */
461	mov     pc, r4
462
463	/*
464	 * _cpu_reset_address contains the address to branch to, to complete
465	 * the cpu reset after turning the MMU off
466	 * This variable is provided by the hardware specific code
467	 */
468.Lcpu_reset_address:
469	.word	_C_LABEL(cpu_reset_address)
470
471	/*
472	 * cpu_reset_needs_v4_MMU_disable contains a flag that signals if the
473	 * v4 MMU disable instruction needs executing... it is an illegal instruction
474	 * on f.e. ARM6/7 that locks up the computer in an endless illegal
475	 * instruction / data-abort / reset loop.
476	 */
477.Lcpu_reset_needs_v4_MMU_disable:
478	.word	_C_LABEL(cpu_reset_needs_v4_MMU_disable)
479END(cpu_halt)
480
481
482/*
483 * setjump + longjmp
484 */
485ENTRY(setjmp)
486	stmia	r0, {r4-r14}
487	mov	r0, #0x00000000
488	RET
489END(setjmp)
490
491ENTRY(longjmp)
492	ldmia	r0, {r4-r14}
493	mov	r0, #0x00000001
494	RET
495END(longjmp)
496
497	.data
498	.global _C_LABEL(esym)
499_C_LABEL(esym):	.word	_C_LABEL(end)
500
501ENTRY_NP(abort)
502	b	_C_LABEL(abort)
503END(abort)
504
505ENTRY_NP(sigcode)
506	mov	r0, sp
507	add	r0, r0, #SIGF_UC
508
509	/*
510	 * Call the sigreturn system call.
511	 *
512	 * We have to load r7 manually rather than using
513	 * "ldr r7, =SYS_sigreturn" to ensure the value of szsigcode is
514	 * correct. Using the alternative places esigcode at the address
515	 * of the data rather than the address one past the data.
516	 */
517
518	ldr	r7, [pc, #12]	/* Load SYS_sigreturn */
519	swi	SYS_sigreturn
520
521	/* Well if that failed we better exit quick ! */
522
523	ldr	r7, [pc, #8]	/* Load SYS_exit */
524	swi	SYS_exit
525
526	/* Branch back to retry SYS_sigreturn */
527	b	. - 16
528END(sigcode)
529	.word	SYS_sigreturn
530	.word	SYS_exit
531
532	.align	0
533	.global _C_LABEL(esigcode)
534		_C_LABEL(esigcode):
535
536	.data
537	.global szsigcode
538szsigcode:
539	.long esigcode-sigcode
540
541/* End of locore.S */
542