elf_trampoline.c revision 183840
1194676Sthompsa/*-
2194676Sthompsa * Copyright (c) 2005 Olivier Houchard.  All rights reserved.
3194676Sthompsa *
4194676Sthompsa * Redistribution and use in source and binary forms, with or without
5194676Sthompsa * modification, are permitted provided that the following conditions
6194676Sthompsa * are met:
7194676Sthompsa * 1. Redistributions of source code must retain the above copyright
8194676Sthompsa *    notice, this list of conditions and the following disclaimer.
9194676Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
10194676Sthompsa *    notice, this list of conditions and the following disclaimer in the
11194676Sthompsa *    documentation and/or other materials provided with the distribution.
12194676Sthompsa *
13194676Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14194676Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15194676Sthompsa * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16194676Sthompsa * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17194676Sthompsa * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18194676Sthompsa * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19194676Sthompsa * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20194676Sthompsa * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21194676Sthompsa * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22194676Sthompsa * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23194676Sthompsa */
24194676Sthompsa
25194676Sthompsa#include <sys/cdefs.h>
26194676Sthompsa__FBSDID("$FreeBSD: head/sys/arm/arm/elf_trampoline.c 183840 2008-10-13 20:07:13Z raj $");
27203815Swkoszek#include <machine/asm.h>
28203815Swkoszek#include <sys/param.h>
29203815Swkoszek#include <sys/elf32.h>
30194676Sthompsa#include <sys/inflate.h>
31194676Sthompsa#include <machine/elf.h>
32203815Swkoszek#include <machine/pte.h>
33203815Swkoszek#include <machine/cpufunc.h>
34194676Sthompsa#include <machine/armreg.h>
35203815Swkoszek
36194676Sthompsa/*
37194676Sthompsa * Since we are compiled outside of the normal kernel build process, we
38194676Sthompsa * need to include opt_global.h manually.
39194676Sthompsa */
40194676Sthompsa#include "opt_global.h"
41194676Sthompsa#include "opt_kernname.h"
42194676Sthompsa
43195957Salfredextern char kernel_start[];
44195957Salfredextern char kernel_end[];
45195957Salfred
46195560Sthompsaextern void *_end;
47195560Sthompsa
48195957Salfredvoid _start(void);
49195560Sthompsavoid __start(void);
50195957Salfredvoid __startC(void);
51195957Salfred
52195957Salfred#define GZ_HEAD	0xa
53195957Salfred
54195957Salfred#ifdef CPU_ARM7TDMI
55195957Salfred#define cpu_idcache_wbinv_all	arm7tdmi_cache_flushID
56195957Salfred#elif defined(CPU_ARM8)
57195560Sthompsa#define cpu_idcache_wbinv_all	arm8_cache_purgeID
58195560Sthompsa#elif defined(CPU_ARM9)
59195560Sthompsa#define cpu_idcache_wbinv_all	arm9_idcache_wbinv_all
60195957Salfred#elif defined(CPU_ARM9E)
61195957Salfred#define cpu_idcache_wbinv_all	armv5_ec_idcache_wbinv_all
62195957Salfred#elif defined(CPU_ARM10)
63195560Sthompsa#define cpu_idcache_wbinv_all	arm10_idcache_wbinv_all
64195560Sthompsa#elif defined(CPU_SA110) || defined(CPU_SA1110) || defined(CPU_SA1100) || \
65195560Sthompsa    defined(CPU_IXP12X0)
66195560Sthompsa#define cpu_idcache_wbinv_all	sa1_cache_purgeID
67195560Sthompsa#elif defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \
68195560Sthompsa  defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) ||	\
69195957Salfred  defined(CPU_XSCALE_80219)
70195560Sthompsa#define cpu_idcache_wbinv_all	xscale_cache_purgeID
71195957Salfred#elif defined(CPU_XSCALE_81342)
72195957Salfred#define cpu_idcache_wbinv_all	xscalec3_cache_purgeID
73195560Sthompsa#endif
74195957Salfred#ifdef CPU_XSCALE_81342
75195957Salfred#define cpu_l2cache_wbinv_all	xscalec3_l2cache_purge
76195560Sthompsa#elif defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
77195957Salfred#define cpu_l2cache_wbinv_all	feroceon_l2cache_wbinv_all
78195957Salfred#else
79195957Salfred#define cpu_l2cache_wbinv_all()
80195957Salfred#endif
81195560Sthompsa
82195560Sthompsa
83195957Salfredint     arm_picache_size;
84195560Sthompsaint     arm_picache_line_size;
85195560Sthompsaint     arm_picache_ways;
86195957Salfred
87195560Sthompsaint     arm_pdcache_size;       /* and unified */
88195957Salfredint     arm_pdcache_line_size = 32;
89195957Salfredint     arm_pdcache_ways;
90195560Sthompsa
91195957Salfredint     arm_pcache_type;
92195957Salfredint     arm_pcache_unified;
93195957Salfred
94195957Salfredint     arm_dcache_align;
95195957Salfredint     arm_dcache_align_mask;
96194676Sthompsa
97194676Sthompsa/* Additional cache information local to this file.  Log2 of some of the
98194676Sthompsa      above numbers.  */
99194676Sthompsastatic int      arm_dcache_l2_nsets;
100195957Salfredstatic int      arm_dcache_l2_assoc;
101194676Sthompsastatic int      arm_dcache_l2_linesize;
102195957Salfred
103195957Salfred
104194676Sthompsaint block_userspace_access = 0;
105195957Salfredextern int arm9_dcache_sets_inc;
106195957Salfredextern int arm9_dcache_sets_max;
107195957Salfredextern int arm9_dcache_index_max;
108194676Sthompsaextern int arm9_dcache_index_inc;
109195560Sthompsa
110194676Sthompsastatic __inline void *
111194676Sthompsamemcpy(void *dst, const void *src, int len)
112194676Sthompsa{
113195957Salfred	const char *s = src;
114195957Salfred    	char *d = dst;
115195957Salfred
116195957Salfred	while (len) {
117195957Salfred		if (0 && len >= 4 && !((vm_offset_t)d & 3) &&
118195957Salfred		    !((vm_offset_t)s & 3)) {
119195957Salfred			*(uint32_t *)d = *(uint32_t *)s;
120195957Salfred			s += 4;
121195957Salfred			d += 4;
122195957Salfred			len -= 4;
123195957Salfred		} else {
124194676Sthompsa			*d++ = *s++;
125194676Sthompsa			len--;
126194676Sthompsa		}
127195957Salfred	}
128195957Salfred	return (dst);
129195957Salfred}
130195957Salfred
131194676Sthompsastatic __inline void
132195957Salfredbzero(void *addr, int count)
133195957Salfred{
134195957Salfred	char *tmp = (char *)addr;
135194676Sthompsa
136195957Salfred	while (count > 0) {
137195957Salfred		if (count >= 4 && !((vm_offset_t)tmp & 3)) {
138195957Salfred			*(uint32_t *)tmp = 0;
139195957Salfred			tmp += 4;
140194676Sthompsa			count -= 4;
141195957Salfred		} else {
142195957Salfred			*tmp = 0;
143195957Salfred			tmp++;
144195957Salfred			count--;
145195957Salfred		}
146195957Salfred	}
147195957Salfred}
148194676Sthompsa
149195957Salfredstatic void arm9_setup(void);
150194676Sthompsa
151195957Salfredvoid
152195957Salfred_startC(void)
153195957Salfred{
154194676Sthompsa	int physaddr = KERNPHYSADDR;
155199055Sthompsa	int tmp1;
156199055Sthompsa	unsigned int sp = ((unsigned int)&_end & ~3) + 4;
157199055Sthompsa#if defined(FLASHADDR) && defined(LOADERRAMADDR)
158199055Sthompsa	unsigned int pc;
159199055Sthompsa
160195957Salfred	__asm __volatile("adr %0, _start\n"
161195957Salfred	    : "=r" (pc));
162195957Salfred	if ((FLASHADDR > LOADERRAMADDR && pc >= FLASHADDR) ||
163199055Sthompsa	    (FLASHADDR < LOADERRAMADDR && pc < LOADERRAMADDR)) {
164199055Sthompsa		/*
165195957Salfred		 * We're running from flash, so just copy the whole thing
166195957Salfred		 * from flash to memory.
167195957Salfred		 * This is far from optimal, we could do the relocation or
168195957Salfred		 * the unzipping directly from flash to memory to avoid this
169195957Salfred		 * needless copy, but it would require to know the flash
170194676Sthompsa		 * physical address.
171195957Salfred		 */
172195957Salfred		unsigned int target_addr;
173194676Sthompsa		unsigned int tmp_sp;
174195957Salfred
175195957Salfred		target_addr = (unsigned int)&_start - PHYSADDR + LOADERRAMADDR;
176195957Salfred		tmp_sp = target_addr + 0x100000 +
177195957Salfred		    (unsigned int)&_end - (unsigned int)&_start;
178194676Sthompsa		memcpy((char *)target_addr, (char *)pc,
179195957Salfred		    (unsigned int)&_end - (unsigned int)&_start);
180194676Sthompsa		/* Temporary set the sp and jump to the new location. */
181195957Salfred		__asm __volatile(
182194676Sthompsa		    "mov sp, %1\n"
183195957Salfred		    "mov pc, %0\n"
184194676Sthompsa		    : : "r" (target_addr), "r" (tmp_sp));
185195957Salfred
186194676Sthompsa	}
187195957Salfred#endif
188195957Salfred#ifdef KZIP
189195957Salfred	sp += KERNSIZE + 0x100;
190194676Sthompsa	sp &= ~(L1_TABLE_SIZE - 1);
191195957Salfred	sp += 2 * L1_TABLE_SIZE;
192195957Salfred#endif
193195957Salfred	sp += 1024 * 1024; /* Should be enough for a stack */
194195957Salfred
195195957Salfred	__asm __volatile("adr %0, 2f\n"
196195957Salfred	    		 "bic %0, %0, #0xff000000\n"
197195957Salfred			 "and %1, %1, #0xff000000\n"
198195957Salfred			 "orr %0, %0, %1\n"
199195957Salfred			 "mrc p15, 0, %1, c1, c0, 0\n"
200195957Salfred			 "bic %1, %1, #1\n" /* Disable MMU */
201195957Salfred			 "orr %1, %1, #(4 | 8)\n" /* Add DC enable,
202195957Salfred						     WBUF enable */
203195957Salfred			 "orr %1, %1, #0x1000\n" /* Add IC enable */
204195957Salfred			 "orr %1, %1, #(0x800)\n" /* BPRD enable */
205195957Salfred
206195957Salfred			 "mcr p15, 0, %1, c1, c0, 0\n"
207195957Salfred			 "nop\n"
208195957Salfred			 "nop\n"
209195957Salfred			 "nop\n"
210195957Salfred			 "mov pc, %0\n"
211194676Sthompsa			 "2: nop\n"
212194676Sthompsa			 "mov sp, %2\n"
213195957Salfred			 : "=r" (tmp1), "+r" (physaddr), "+r" (sp));
214195957Salfred#ifndef KZIP
215194676Sthompsa#ifdef CPU_ARM9
216195957Salfred	/* So that idcache_wbinv works; */
217194676Sthompsa	if ((cpufunc_id() & 0x0000f000) == 0x00009000)
218194676Sthompsa		arm9_setup();
219194676Sthompsa#endif
220194676Sthompsa	cpu_idcache_wbinv_all();
221194676Sthompsa	cpu_l2cache_wbinv_all();
222195957Salfred#endif
223194676Sthompsa	__start();
224195957Salfred}
225194676Sthompsa
226195957Salfredstatic void
227195957Salfredget_cachetype_cp15()
228194676Sthompsa{
229194676Sthompsa	u_int ctype, isize, dsize;
230195957Salfred	u_int multiplier;
231195957Salfred
232194676Sthompsa	__asm __volatile("mrc p15, 0, %0, c0, c0, 1"
233194676Sthompsa	    : "=r" (ctype));
234195957Salfred
235195957Salfred	/*
236195957Salfred	 * ...and thus spake the ARM ARM:
237195957Salfred	 *
238195957Salfred 	 * If an <opcode2> value corresponding to an unimplemented or
239195957Salfred	 * reserved ID register is encountered, the System Control
240195957Salfred	 * processor returns the value of the main ID register.
241194676Sthompsa	 */
242194676Sthompsa	if (ctype == cpufunc_id())
243194676Sthompsa		goto out;
244195957Salfred
245194676Sthompsa	if ((ctype & CPU_CT_S) == 0)
246195957Salfred		arm_pcache_unified = 1;
247195957Salfred
248195957Salfred	/*
249195957Salfred	 * If you want to know how this code works, go read the ARM ARM.
250194676Sthompsa	 */
251194676Sthompsa
252194676Sthompsa	arm_pcache_type = CPU_CT_CTYPE(ctype);
253195957Salfred        if (arm_pcache_unified == 0) {
254194676Sthompsa		isize = CPU_CT_ISIZE(ctype);
255195957Salfred	    	multiplier = (isize & CPU_CT_xSIZE_M) ? 3 : 2;
256195957Salfred		arm_picache_line_size = 1U << (CPU_CT_xSIZE_LEN(isize) + 3);
257195957Salfred		if (CPU_CT_xSIZE_ASSOC(isize) == 0) {
258195957Salfred			if (isize & CPU_CT_xSIZE_M)
259195957Salfred				arm_picache_line_size = 0; /* not present */
260195957Salfred			else
261194676Sthompsa				arm_picache_ways = 1;
262194676Sthompsa		} else {
263194676Sthompsa			arm_picache_ways = multiplier <<
264195957Salfred			    (CPU_CT_xSIZE_ASSOC(isize) - 1);
265194676Sthompsa		}
266195957Salfred		arm_picache_size = multiplier << (CPU_CT_xSIZE_SIZE(isize) + 8);
267195957Salfred	}
268194676Sthompsa
269194676Sthompsa	dsize = CPU_CT_DSIZE(ctype);
270194676Sthompsa	multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2;
271195957Salfred	arm_pdcache_line_size = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3);
272194676Sthompsa	if (CPU_CT_xSIZE_ASSOC(dsize) == 0) {
273195957Salfred		if (dsize & CPU_CT_xSIZE_M)
274195957Salfred			arm_pdcache_line_size = 0; /* not present */
275194676Sthompsa		else
276194676Sthompsa			arm_pdcache_ways = 1;
277194676Sthompsa	} else {
278195957Salfred		arm_pdcache_ways = multiplier <<
279194676Sthompsa		    (CPU_CT_xSIZE_ASSOC(dsize) - 1);
280195957Salfred	}
281195957Salfred	arm_pdcache_size = multiplier << (CPU_CT_xSIZE_SIZE(dsize) + 8);
282194676Sthompsa
283194676Sthompsa	arm_dcache_align = arm_pdcache_line_size;
284194676Sthompsa
285195957Salfred	arm_dcache_l2_assoc = CPU_CT_xSIZE_ASSOC(dsize) + multiplier - 2;
286194676Sthompsa	arm_dcache_l2_linesize = CPU_CT_xSIZE_LEN(dsize) + 3;
287195957Salfred	arm_dcache_l2_nsets = 6 + CPU_CT_xSIZE_SIZE(dsize) -
288195957Salfred	    CPU_CT_xSIZE_ASSOC(dsize) - CPU_CT_xSIZE_LEN(dsize);
289194676Sthompsa out:
290194676Sthompsa	arm_dcache_align_mask = arm_dcache_align - 1;
291194676Sthompsa}
292195957Salfred
293194676Sthompsastatic void
294194676Sthompsaarm9_setup(void)
295195957Salfred{
296194676Sthompsa
297195957Salfred	get_cachetype_cp15();
298195560Sthompsa	arm9_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
299194676Sthompsa	arm9_dcache_sets_max = (1U << (arm_dcache_l2_linesize +
300194676Sthompsa	    arm_dcache_l2_nsets)) - arm9_dcache_sets_inc;
301195957Salfred	arm9_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
302195957Salfred	arm9_dcache_index_max = 0U - arm9_dcache_index_inc;
303194676Sthompsa}
304194676Sthompsa
305195957Salfred
306195957Salfred#ifdef KZIP
307194676Sthompsastatic  unsigned char *orig_input, *i_input, *i_output;
308194676Sthompsa
309194676Sthompsa
310194676Sthompsastatic u_int memcnt;		/* Memory allocated: blocks */
311195957Salfredstatic size_t memtot;		/* Memory allocated: bytes */
312194676Sthompsa/*
313194676Sthompsa * Library functions required by inflate().
314194676Sthompsa */
315195957Salfred
316195957Salfred#define MEMSIZ 0x8000
317194676Sthompsa
318195957Salfred/*
319194676Sthompsa * Allocate memory block.
320194676Sthompsa */
321194676Sthompsaunsigned char *
322194676Sthompsakzipmalloc(int size)
323194676Sthompsa{
324194676Sthompsa	void *ptr;
325195957Salfred	static u_char mem[MEMSIZ];
326194676Sthompsa
327195957Salfred	if (memtot + size > MEMSIZ)
328195957Salfred		return NULL;
329195957Salfred	ptr = mem + memtot;
330195957Salfred	memtot += size;
331195560Sthompsa	memcnt++;
332194676Sthompsa	return ptr;
333195957Salfred}
334194676Sthompsa
335195957Salfred/*
336194676Sthompsa * Free allocated memory block.
337195957Salfred */
338194676Sthompsavoid
339195560Sthompsakzipfree(void *ptr)
340195957Salfred{
341195957Salfred	memcnt--;
342194676Sthompsa	if (!memcnt)
343194676Sthompsa		memtot = 0;
344194676Sthompsa}
345195957Salfred
346194676Sthompsavoid
347195957Salfredputstr(char *dummy)
348194676Sthompsa{
349194676Sthompsa}
350194676Sthompsa
351195957Salfredstatic int
352194676Sthompsainput(void *dummy)
353195957Salfred{
354194676Sthompsa	if ((size_t)(i_input - orig_input) >= KERNCOMPSIZE) {
355195957Salfred		return (GZ_EOF);
356194676Sthompsa	}
357195957Salfred	return *i_input++;
358195957Salfred}
359195957Salfred
360195957Salfredstatic int
361195957Salfredoutput(void *dummy, unsigned char *ptr, unsigned long len)
362194676Sthompsa{
363195957Salfred
364194676Sthompsa
365194676Sthompsa	memcpy(i_output, ptr, len);
366194676Sthompsa	i_output += len;
367195957Salfred	return (0);
368194676Sthompsa}
369195957Salfred
370195957Salfredstatic void *
371195957Salfredinflate_kernel(void *kernel, void *startaddr)
372194676Sthompsa{
373194676Sthompsa	struct inflate infl;
374194676Sthompsa	char slide[GZ_WSIZE];
375195957Salfred
376194676Sthompsa	orig_input = kernel;
377194676Sthompsa	memcnt = memtot = 0;
378194676Sthompsa	i_input = (char *)kernel + GZ_HEAD;
379195957Salfred	if (((char *)kernel)[3] & 0x18) {
380194676Sthompsa		while (*i_input)
381194676Sthompsa			i_input++;
382194676Sthompsa		i_input++;
383194676Sthompsa	}
384194676Sthompsa	i_output = startaddr;
385194676Sthompsa	bzero(&infl, sizeof(infl));
386194676Sthompsa	infl.gz_input = input;
387195957Salfred	infl.gz_output = output;
388194676Sthompsa	infl.gz_slide = slide;
389195957Salfred	inflate(&infl);
390194676Sthompsa	return ((char *)(((vm_offset_t)i_output & ~3) + 4));
391194676Sthompsa}
392194676Sthompsa
393195957Salfred#endif
394194676Sthompsa
395195957Salfredvoid *
396195957Salfredload_kernel(unsigned int kstart, unsigned int curaddr,unsigned int func_end,
397194676Sthompsa    int d)
398195957Salfred{
399195957Salfred	Elf32_Ehdr *eh;
400194676Sthompsa	Elf32_Phdr phdr[64] /* XXX */, *php;
401195957Salfred	Elf32_Shdr shdr[64] /* XXX */;
402195957Salfred	int i,j;
403195957Salfred	void *entry_point;
404194676Sthompsa	int symtabindex = -1;
405194676Sthompsa	int symstrindex = -1;
406195957Salfred	vm_offset_t lastaddr = 0;
407195957Salfred	Elf_Addr ssym = 0, esym = 0;
408194676Sthompsa	Elf_Dyn *dp;
409194676Sthompsa
410195957Salfred	eh = (Elf32_Ehdr *)kstart;
411195957Salfred	ssym = esym = 0;
412194676Sthompsa	entry_point = (void*)eh->e_entry;
413194676Sthompsa	memcpy(phdr, (void *)(kstart + eh->e_phoff ),
414194676Sthompsa	    eh->e_phnum * sizeof(phdr[0]));
415194676Sthompsa
416194676Sthompsa	/* Determine lastaddr. */
417194676Sthompsa	for (i = 0; i < eh->e_phnum; i++) {
418194676Sthompsa		if (lastaddr < (phdr[i].p_vaddr - KERNVIRTADDR + curaddr
419195957Salfred		    + phdr[i].p_memsz))
420194676Sthompsa			lastaddr = phdr[i].p_vaddr - KERNVIRTADDR +
421195957Salfred			    curaddr + phdr[i].p_memsz;
422194676Sthompsa	}
423195957Salfred
424195957Salfred	/* Save the symbol tables, as there're about to be scratched. */
425195957Salfred	memcpy(shdr, (void *)(kstart + eh->e_shoff),
426194676Sthompsa	    sizeof(*shdr) * eh->e_shnum);
427195957Salfred	if (eh->e_shnum * eh->e_shentsize != 0 &&
428195957Salfred	    eh->e_shoff != 0) {
429194676Sthompsa		for (i = 0; i < eh->e_shnum; i++) {
430195957Salfred			if (shdr[i].sh_type == SHT_SYMTAB) {
431195957Salfred				for (j = 0; j < eh->e_phnum; j++) {
432194676Sthompsa					if (phdr[j].p_type == PT_LOAD &&
433195957Salfred					    shdr[i].sh_offset >=
434194676Sthompsa					    phdr[j].p_offset &&
435195957Salfred					    (shdr[i].sh_offset +
436195957Salfred					     shdr[i].sh_size <=
437195957Salfred					     phdr[j].p_offset +
438195957Salfred					     phdr[j].p_filesz)) {
439195957Salfred						shdr[i].sh_offset = 0;
440194676Sthompsa						shdr[i].sh_size = 0;
441195957Salfred						j = eh->e_phnum;
442195957Salfred					}
443194676Sthompsa				}
444195957Salfred				if (shdr[i].sh_offset != 0 &&
445195957Salfred				    shdr[i].sh_size != 0) {
446195957Salfred					symtabindex = i;
447195957Salfred					symstrindex = shdr[i].sh_link;
448195957Salfred				}
449195957Salfred			}
450194676Sthompsa		}
451195957Salfred		func_end = roundup(func_end, sizeof(long));
452195957Salfred		if (symtabindex >= 0 && symstrindex >= 0) {
453194676Sthompsa			ssym = lastaddr;
454195957Salfred			if (d) {
455195957Salfred				memcpy((void *)func_end, (void *)(
456195957Salfred				    shdr[symtabindex].sh_offset + kstart),
457195957Salfred				    shdr[symtabindex].sh_size);
458195957Salfred				memcpy((void *)(func_end +
459194676Sthompsa				    shdr[symtabindex].sh_size),
460195957Salfred				    (void *)(shdr[symstrindex].sh_offset +
461194676Sthompsa				    kstart), shdr[symstrindex].sh_size);
462195957Salfred			} else {
463194676Sthompsa				lastaddr += shdr[symtabindex].sh_size;
464195957Salfred				lastaddr = roundup(lastaddr,
465195957Salfred				    sizeof(shdr[symtabindex].sh_size));
466194676Sthompsa				lastaddr += sizeof(shdr[symstrindex].sh_size);
467194676Sthompsa				lastaddr += shdr[symstrindex].sh_size;
468195957Salfred				lastaddr = roundup(lastaddr,
469195957Salfred				    sizeof(shdr[symstrindex].sh_size));
470195957Salfred			}
471195957Salfred
472194676Sthompsa		}
473195957Salfred	}
474195957Salfred	if (!d)
475194676Sthompsa		return ((void *)lastaddr);
476194676Sthompsa
477195957Salfred	j = eh->e_phnum;
478194676Sthompsa	for (i = 0; i < j; i++) {
479195957Salfred		volatile char c;
480194676Sthompsa
481194676Sthompsa		if (phdr[i].p_type != PT_LOAD)
482195957Salfred			continue;
483195957Salfred		memcpy((void *)(phdr[i].p_vaddr - KERNVIRTADDR + curaddr),
484194676Sthompsa		    (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz);
485195957Salfred		/* Clean space from oversized segments, eg: bss. */
486195957Salfred		if (phdr[i].p_filesz < phdr[i].p_memsz)
487195957Salfred			bzero((void *)(phdr[i].p_vaddr - KERNVIRTADDR +
488194676Sthompsa			    curaddr + phdr[i].p_filesz), phdr[i].p_memsz -
489194676Sthompsa			    phdr[i].p_filesz);
490194676Sthompsa	}
491194676Sthompsa	/* Now grab the symbol tables. */
492195957Salfred	if (symtabindex >= 0 && symstrindex >= 0) {
493194676Sthompsa		*(Elf_Size *)lastaddr =
494194676Sthompsa		    shdr[symtabindex].sh_size;
495194676Sthompsa		lastaddr += sizeof(shdr[symtabindex].sh_size);
496194676Sthompsa		memcpy((void*)lastaddr,
497194676Sthompsa		    (void *)func_end,
498194676Sthompsa		    shdr[symtabindex].sh_size);
499194676Sthompsa		lastaddr += shdr[symtabindex].sh_size;
500195957Salfred		lastaddr = roundup(lastaddr,
501195957Salfred		    sizeof(shdr[symtabindex].sh_size));
502194676Sthompsa		*(Elf_Size *)lastaddr =
503194676Sthompsa		    shdr[symstrindex].sh_size;
504194676Sthompsa		lastaddr += sizeof(shdr[symstrindex].sh_size);
505194676Sthompsa		memcpy((void*)lastaddr,
506194676Sthompsa		    (void*)(func_end +
507194676Sthompsa			    shdr[symtabindex].sh_size),
508194676Sthompsa		    shdr[symstrindex].sh_size);
509194676Sthompsa		lastaddr += shdr[symstrindex].sh_size;
510194676Sthompsa		lastaddr = roundup(lastaddr,
511195957Salfred   		    sizeof(shdr[symstrindex].sh_size));
512194676Sthompsa		*(Elf_Addr *)curaddr = MAGIC_TRAMP_NUMBER;
513194676Sthompsa		*((Elf_Addr *)curaddr + 1) = ssym - curaddr + KERNVIRTADDR;
514194676Sthompsa		*((Elf_Addr *)curaddr + 2) = lastaddr - curaddr + KERNVIRTADDR;
515194676Sthompsa	} else
516195957Salfred		*(Elf_Addr *)curaddr = 0;
517194676Sthompsa	/* Invalidate the instruction cache. */
518194676Sthompsa	__asm __volatile("mcr p15, 0, %0, c7, c5, 0\n"
519195957Salfred	    		 "mcr p15, 0, %0, c7, c10, 4\n"
520194676Sthompsa			 : : "r" (curaddr));
521194676Sthompsa	__asm __volatile("mrc p15, 0, %0, c1, c0, 0\n"
522195957Salfred	    "bic %0, %0, #1\n" /* MMU_ENABLE */
523195957Salfred	    "mcr p15, 0, %0, c1, c0, 0\n"
524194676Sthompsa	    : "=r" (ssym));
525195957Salfred	/* Jump to the entry point. */
526195957Salfred	((void(*)(void))(entry_point - KERNVIRTADDR + curaddr))();
527194676Sthompsa	__asm __volatile(".globl func_end\n"
528195957Salfred	    "func_end:");
529195957Salfred
530194676Sthompsa}
531195957Salfred
532194676Sthompsaextern char func_end[];
533194676Sthompsa
534194676Sthompsa
535195957Salfred#define PMAP_DOMAIN_KERNEL	0 /*
536194676Sthompsa				    * Just define it instead of including the
537194676Sthompsa				    * whole VM headers set.
538194676Sthompsa				    */
539194676Sthompsaint __hack;
540194676Sthompsastatic __inline void
541194676Sthompsasetup_pagetables(unsigned int pt_addr, vm_paddr_t physstart, vm_paddr_t physend,
542194676Sthompsa    int write_back)
543195957Salfred{
544195957Salfred	unsigned int *pd = (unsigned int *)pt_addr;
545194676Sthompsa	vm_paddr_t addr;
546194676Sthompsa	int domain = (DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)) | DOMAIN_CLIENT;
547194676Sthompsa	int tmp;
548194676Sthompsa
549195957Salfred	bzero(pd, L1_TABLE_SIZE);
550195957Salfred	for (addr = physstart; addr < physend; addr += L1_S_SIZE) {
551195560Sthompsa		pd[addr >> L1_S_SHIFT] = L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW)|
552194676Sthompsa		    L1_S_DOM(PMAP_DOMAIN_KERNEL) | addr;
553195957Salfred		if (write_back)
554194676Sthompsa			pd[addr >> L1_S_SHIFT] |= L1_S_B;
555194676Sthompsa	}
556195560Sthompsa	/* XXX: See below */
557194676Sthompsa	if (0xfff00000 < physstart || 0xfff00000 > physend)
558194676Sthompsa		pd[0xfff00000 >> L1_S_SHIFT] = L1_TYPE_S|L1_S_AP(AP_KRW)|
559194676Sthompsa		    L1_S_DOM(PMAP_DOMAIN_KERNEL)|physstart;
560194676Sthompsa	__asm __volatile("mcr p15, 0, %1, c2, c0, 0\n" /* set TTB */
561195957Salfred	    		 "mcr p15, 0, %1, c8, c7, 0\n" /* Flush TTB */
562195957Salfred			 "mcr p15, 0, %2, c3, c0, 0\n" /* Set DAR */
563194676Sthompsa			 "mrc p15, 0, %0, c1, c0, 0\n"
564194676Sthompsa			 "orr %0, %0, #1\n" /* MMU_ENABLE */
565194676Sthompsa			 "mcr p15, 0, %0, c1, c0, 0\n"
566194676Sthompsa			 "mrc p15, 0, %0, c2, c0, 0\n" /* CPWAIT */
567194676Sthompsa			 "mov r0, r0\n"
568195957Salfred			 "sub pc, pc, #4\n" :
569195560Sthompsa			 "=r" (tmp) : "r" (pd), "r" (domain));
570194676Sthompsa
571195957Salfred	/*
572194676Sthompsa	 * XXX: This is the most stupid workaround I've ever wrote.
573194676Sthompsa	 * For some reason, the KB9202 won't boot the kernel unless
574195560Sthompsa	 * we access an address which is not in the
575194676Sthompsa	 * 0x20000000 - 0x20ffffff range. I hope I'll understand
576194676Sthompsa	 * what's going on later.
577199055Sthompsa	 */
578199055Sthompsa	__hack = *(volatile int *)0xfffff21c;
579199055Sthompsa}
580199055Sthompsa
581199055Sthompsavoid
582199055Sthompsa__start(void)
583199055Sthompsa{
584199055Sthompsa	void *curaddr;
585199055Sthompsa	void *dst, *altdst;
586199055Sthompsa	char *kernel = (char *)&kernel_start;
587199055Sthompsa	int sp;
588199055Sthompsa	int pt_addr;
589199055Sthompsa
590199055Sthompsa	__asm __volatile("mov %0, pc"  :
591199055Sthompsa	    "=r" (curaddr));
592199055Sthompsa	curaddr = (void*)((unsigned int)curaddr & 0xfff00000);
593199055Sthompsa#ifdef KZIP
594199055Sthompsa	if (*kernel == 0x1f && kernel[1] == 0x8b) {
595199055Sthompsa		pt_addr = (((int)&_end + KERNSIZE + 0x100) &
596199055Sthompsa		    ~(L1_TABLE_SIZE - 1)) + L1_TABLE_SIZE;
597199055Sthompsa
598199055Sthompsa#ifdef CPU_ARM9
599199055Sthompsa		/* So that idcache_wbinv works; */
600199055Sthompsa		if ((cpufunc_id() & 0x0000f000) == 0x00009000)
601199055Sthompsa			arm9_setup();
602199055Sthompsa#endif
603199055Sthompsa		setup_pagetables(pt_addr, (vm_paddr_t)curaddr,
604199055Sthompsa		    (vm_paddr_t)curaddr + 0x10000000, 1);
605199055Sthompsa		/* Gzipped kernel */
606199055Sthompsa		dst = inflate_kernel(kernel, &_end);
607199055Sthompsa		kernel = (char *)&_end;
608199055Sthompsa		altdst = 4 + load_kernel((unsigned int)kernel,
609199055Sthompsa		    (unsigned int)curaddr,
610199055Sthompsa		    (unsigned int)&func_end + 800 , 0);
611199055Sthompsa		if (altdst > dst)
612199055Sthompsa			dst = altdst;
613199055Sthompsa		cpu_idcache_wbinv_all();
614199055Sthompsa		cpu_l2cache_wbinv_all();
615199055Sthompsa		__asm __volatile("mrc p15, 0, %0, c1, c0, 0\n"
616199055Sthompsa		    "bic %0, %0, #1\n" /* MMU_ENABLE */
617199055Sthompsa		    "mcr p15, 0, %0, c1, c0, 0\n"
618199055Sthompsa		    : "=r" (pt_addr));
619199055Sthompsa	} else
620199055Sthompsa#endif
621199055Sthompsa		dst = 4 + load_kernel((unsigned int)&kernel_start,
622199055Sthompsa	    (unsigned int)curaddr,
623199055Sthompsa	    (unsigned int)&func_end, 0);
624199055Sthompsa	dst = (void *)(((vm_offset_t)dst & ~3));
625199055Sthompsa	pt_addr = ((unsigned int)dst &~(L1_TABLE_SIZE - 1)) + L1_TABLE_SIZE;
626199055Sthompsa	setup_pagetables(pt_addr, (vm_paddr_t)curaddr,
627199055Sthompsa	    (vm_paddr_t)curaddr + 0x10000000, 0);
628199055Sthompsa	sp = pt_addr + L1_TABLE_SIZE + 8192;
629199055Sthompsa	sp = sp &~3;
630199055Sthompsa	dst = (void *)(sp + 4);
631199055Sthompsa	memcpy((void *)dst, (void *)&load_kernel, (unsigned int)&func_end -
632199055Sthompsa	    (unsigned int)&load_kernel + 800);
633199055Sthompsa	do_call(dst, kernel, dst + (unsigned int)(&func_end) -
634199055Sthompsa	    (unsigned int)(&load_kernel) + 800, sp);
635199055Sthompsa}
636199055Sthompsa