1150861Scognet/*-
2150861Scognet * Copyright (c) 2005 Olivier Houchard.  All rights reserved.
3150861Scognet *
4150861Scognet * Redistribution and use in source and binary forms, with or without
5150861Scognet * modification, are permitted provided that the following conditions
6150861Scognet * are met:
7150861Scognet * 1. Redistributions of source code must retain the above copyright
8150861Scognet *    notice, this list of conditions and the following disclaimer.
9150861Scognet * 2. Redistributions in binary form must reproduce the above copyright
10150861Scognet *    notice, this list of conditions and the following disclaimer in the
11150861Scognet *    documentation and/or other materials provided with the distribution.
12150861Scognet *
13150861Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14150861Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15150861Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16150861Scognet * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17150861Scognet * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18150861Scognet * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19150861Scognet * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20150861Scognet * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21150861Scognet * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22150861Scognet * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23150861Scognet */
24150861Scognet
25225990Smarcel/*
26225990Smarcel * Since we are compiled outside of the normal kernel build process, we
27225990Smarcel * need to include opt_global.h manually.
28225990Smarcel */
29225990Smarcel#include "opt_global.h"
30225990Smarcel#include "opt_kernname.h"
31225990Smarcel
32150861Scognet#include <sys/cdefs.h>
33150861Scognet__FBSDID("$FreeBSD$");
34150861Scognet#include <machine/asm.h>
35163871Scognet#include <sys/param.h>
36150861Scognet#include <sys/elf32.h>
37153112Scognet#include <sys/inflate.h>
38150861Scognet#include <machine/elf.h>
39154561Scognet#include <machine/pte.h>
40159084Scognet#include <machine/cpufunc.h>
41159557Scognet#include <machine/armreg.h>
42154561Scognet
43150861Scognetextern char kernel_start[];
44150861Scognetextern char kernel_end[];
45150861Scognet
46159557Scognetextern void *_end;
47159557Scognet
48177508Scognetvoid _start(void);
49152743Scognetvoid __start(void);
50171619Scognetvoid __startC(void);
51152743Scognet
52252996Srpauloextern unsigned int cpufunc_id(void);
53252996Srpauloextern void armv6_idcache_wbinv_all(void);
54253005Srpauloextern void armv7_idcache_wbinv_all(void);
55252996Srpauloextern void do_call(void *, void *, void *, int);
56252996Srpaulo
57153112Scognet#define GZ_HEAD	0xa
58152743Scognet
59266311Sian#if defined(CPU_ARM9)
60159557Scognet#define cpu_idcache_wbinv_all	arm9_idcache_wbinv_all
61252996Srpauloextern void arm9_idcache_wbinv_all(void);
62207611Skevlo#elif defined(CPU_FA526) || defined(CPU_FA626TE)
63201468Srpaulo#define cpu_idcache_wbinv_all	fa526_idcache_wbinv_all
64252996Srpauloextern void fa526_idcache_wbinv_all(void);
65177916Sraj#elif defined(CPU_ARM9E)
66177916Sraj#define cpu_idcache_wbinv_all	armv5_ec_idcache_wbinv_all
67252996Srpauloextern void armv5_ec_idcache_wbinv_all(void);
68159084Scognet#elif defined(CPU_ARM10)
69159084Scognet#define cpu_idcache_wbinv_all	arm10_idcache_wbinv_all
70252996Srpauloextern void arm10_idcache_wbinv_all(void);
71244480Sgonzo#elif defined(CPU_ARM1136) || defined(CPU_ARM1176)
72239702Sgonzo#define cpu_idcache_wbinv_all	armv6_idcache_wbinv_all
73159084Scognet#elif defined(CPU_XSCALE_80200) || defined(CPU_XSCALE_80321) || \
74161592Scognet  defined(CPU_XSCALE_PXA2X0) || defined(CPU_XSCALE_IXP425) ||	\
75161592Scognet  defined(CPU_XSCALE_80219)
76159084Scognet#define cpu_idcache_wbinv_all	xscale_cache_purgeID
77252996Srpauloextern void xscale_cache_purgeID(void);
78171619Scognet#elif defined(CPU_XSCALE_81342)
79171619Scognet#define cpu_idcache_wbinv_all	xscalec3_cache_purgeID
80252996Srpauloextern void xscalec3_cache_purgeID(void);
81239268Sgonzo#elif defined(CPU_MV_PJ4B)
82239268Sgonzo#if !defined(SOC_MV_ARMADAXP)
83239268Sgonzo#define cpu_idcache_wbinv_all	armv6_idcache_wbinv_all
84252996Srpauloextern void armv6_idcache_wbinv_all(void);
85239268Sgonzo#else
86239268Sgonzo#define cpu_idcache_wbinv_all()	armadaxp_idcache_wbinv_all
87159084Scognet#endif
88239268Sgonzo#endif /* CPU_MV_PJ4B */
89171619Scognet#ifdef CPU_XSCALE_81342
90171619Scognet#define cpu_l2cache_wbinv_all	xscalec3_l2cache_purge
91252996Srpauloextern void xscalec3_l2cache_purge(void);
92183840Sraj#elif defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
93186933Sraj#define cpu_l2cache_wbinv_all	sheeva_l2cache_wbinv_all
94252996Srpauloextern void sheeva_l2cache_wbinv_all(void);
95266058Sian#elif defined(CPU_CORTEXA) || defined(CPU_KRAIT)
96239268Sgonzo#define cpu_idcache_wbinv_all	armv7_idcache_wbinv_all
97239268Sgonzo#define cpu_l2cache_wbinv_all()
98171619Scognet#else
99171619Scognet#define cpu_l2cache_wbinv_all()
100171619Scognet#endif
101159557Scognet
102252997Srpaulostatic void armadaxp_idcache_wbinv_all(void);
103252997Srpaulo
104159557Scognetint     arm_picache_size;
105159557Scognetint     arm_picache_line_size;
106159557Scognetint     arm_picache_ways;
107159557Scognet
108159557Scognetint     arm_pdcache_size;       /* and unified */
109159557Scognetint     arm_pdcache_line_size = 32;
110159557Scognetint     arm_pdcache_ways;
111159557Scognet
112159557Scognetint     arm_pcache_type;
113159557Scognetint     arm_pcache_unified;
114159557Scognet
115159557Scognetint     arm_dcache_align;
116159557Scognetint     arm_dcache_align_mask;
117159557Scognet
118239268Sgonzou_int	arm_cache_level;
119239268Sgonzou_int	arm_cache_type[14];
120239268Sgonzou_int	arm_cache_loc;
121239268Sgonzo
122159557Scognet/* Additional cache information local to this file.  Log2 of some of the
123159557Scognet      above numbers.  */
124159557Scognetstatic int      arm_dcache_l2_nsets;
125159557Scognetstatic int      arm_dcache_l2_assoc;
126159557Scognetstatic int      arm_dcache_l2_linesize;
127159557Scognet
128159557Scognet
129159084Scognetint block_userspace_access = 0;
130159557Scognetextern int arm9_dcache_sets_inc;
131159557Scognetextern int arm9_dcache_sets_max;
132159557Scognetextern int arm9_dcache_index_max;
133159557Scognetextern int arm9_dcache_index_inc;
134159084Scognet
135150861Scognetstatic __inline void *
136150861Scognetmemcpy(void *dst, const void *src, int len)
137150861Scognet{
138150861Scognet	const char *s = src;
139150861Scognet    	char *d = dst;
140153549Scognet
141153112Scognet	while (len) {
142166819Scognet		if (0 && len >= 4 && !((vm_offset_t)d & 3) &&
143153549Scognet		    !((vm_offset_t)s & 3)) {
144153112Scognet			*(uint32_t *)d = *(uint32_t *)s;
145153112Scognet			s += 4;
146153112Scognet			d += 4;
147153112Scognet			len -= 4;
148153112Scognet		} else {
149153112Scognet			*d++ = *s++;
150153112Scognet			len--;
151153112Scognet		}
152150861Scognet	}
153150861Scognet	return (dst);
154150861Scognet}
155150861Scognet
156150861Scognetstatic __inline void
157153112Scognetbzero(void *addr, int count)
158150861Scognet{
159153112Scognet	char *tmp = (char *)addr;
160153112Scognet
161150861Scognet	while (count > 0) {
162153112Scognet		if (count >= 4 && !((vm_offset_t)tmp & 3)) {
163153112Scognet			*(uint32_t *)tmp = 0;
164153112Scognet			tmp += 4;
165153112Scognet			count -= 4;
166153112Scognet		} else {
167153112Scognet			*tmp = 0;
168153112Scognet			tmp++;
169153112Scognet			count--;
170153112Scognet		}
171150861Scognet	}
172150861Scognet}
173150861Scognet
174159900Scognetstatic void arm9_setup(void);
175159900Scognet
176153112Scognetvoid
177171619Scognet_startC(void)
178153112Scognet{
179153549Scognet	int tmp1;
180159758Scognet	unsigned int sp = ((unsigned int)&_end & ~3) + 4;
181266194Sian	unsigned int pc, kernphysaddr;
182166819Scognet
183266194Sian	/*
184266194Sian	 * Figure out the physical address the kernel was loaded at.  This
185266194Sian	 * assumes the entry point (this code right here) is in the first page,
186266194Sian	 * which will always be the case for this trampoline code.
187266194Sian	 */
188214648Scognet	__asm __volatile("mov %0, pc\n"
189166819Scognet	    : "=r" (pc));
190266194Sian	kernphysaddr = pc & ~PAGE_MASK;
191266194Sian
192266194Sian#if defined(FLASHADDR) && defined(PHYSADDR) && defined(LOADERRAMADDR)
193166819Scognet	if ((FLASHADDR > LOADERRAMADDR && pc >= FLASHADDR) ||
194166819Scognet	    (FLASHADDR < LOADERRAMADDR && pc < LOADERRAMADDR)) {
195166819Scognet		/*
196166819Scognet		 * We're running from flash, so just copy the whole thing
197166819Scognet		 * from flash to memory.
198166819Scognet		 * This is far from optimal, we could do the relocation or
199166819Scognet		 * the unzipping directly from flash to memory to avoid this
200166819Scognet		 * needless copy, but it would require to know the flash
201166819Scognet		 * physical address.
202166819Scognet		 */
203166819Scognet		unsigned int target_addr;
204166819Scognet		unsigned int tmp_sp;
205214648Scognet		uint32_t src_addr = (uint32_t)&_start - PHYSADDR + FLASHADDR
206214648Scognet		    + (pc - FLASHADDR - ((uint32_t)&_startC - PHYSADDR)) & 0xfffff000;
207166819Scognet
208166819Scognet		target_addr = (unsigned int)&_start - PHYSADDR + LOADERRAMADDR;
209166819Scognet		tmp_sp = target_addr + 0x100000 +
210166819Scognet		    (unsigned int)&_end - (unsigned int)&_start;
211214648Scognet		memcpy((char *)target_addr, (char *)src_addr,
212166819Scognet		    (unsigned int)&_end - (unsigned int)&_start);
213166819Scognet		/* Temporary set the sp and jump to the new location. */
214166819Scognet		__asm __volatile(
215166819Scognet		    "mov sp, %1\n"
216166819Scognet		    "mov pc, %0\n"
217166819Scognet		    : : "r" (target_addr), "r" (tmp_sp));
218166819Scognet
219166819Scognet	}
220166819Scognet#endif
221159557Scognet#ifdef KZIP
222159557Scognet	sp += KERNSIZE + 0x100;
223159557Scognet	sp &= ~(L1_TABLE_SIZE - 1);
224159557Scognet	sp += 2 * L1_TABLE_SIZE;
225159557Scognet#endif
226159557Scognet	sp += 1024 * 1024; /* Should be enough for a stack */
227153549Scognet
228153549Scognet	__asm __volatile("adr %0, 2f\n"
229153549Scognet	    		 "bic %0, %0, #0xff000000\n"
230153549Scognet			 "and %1, %1, #0xff000000\n"
231153549Scognet			 "orr %0, %0, %1\n"
232153549Scognet			 "mrc p15, 0, %1, c1, c0, 0\n"
233153549Scognet			 "bic %1, %1, #1\n" /* Disable MMU */
234236991Simp			 "orr %1, %1, #(4 | 8)\n" /* Add DC enable,
235153549Scognet						     WBUF enable */
236153549Scognet			 "orr %1, %1, #0x1000\n" /* Add IC enable */
237153549Scognet			 "orr %1, %1, #(0x800)\n" /* BPRD enable */
238153549Scognet
239153549Scognet			 "mcr p15, 0, %1, c1, c0, 0\n"
240153549Scognet			 "nop\n"
241153549Scognet			 "nop\n"
242153549Scognet			 "nop\n"
243153549Scognet			 "mov pc, %0\n"
244153549Scognet			 "2: nop\n"
245159557Scognet			 "mov sp, %2\n"
246266194Sian			 : "=r" (tmp1), "+r" (kernphysaddr), "+r" (sp));
247159758Scognet#ifndef KZIP
248159758Scognet#ifdef CPU_ARM9
249159758Scognet	/* So that idcache_wbinv works; */
250159758Scognet	if ((cpufunc_id() & 0x0000f000) == 0x00009000)
251159758Scognet		arm9_setup();
252159758Scognet#endif
253159758Scognet#endif
254153112Scognet	__start();
255153112Scognet}
256153112Scognet
257159557Scognetstatic void
258159557Scognetget_cachetype_cp15()
259159557Scognet{
260239268Sgonzo	u_int ctype, isize, dsize, cpuid;
261239268Sgonzo	u_int clevel, csize, i, sel;
262159557Scognet	u_int multiplier;
263239268Sgonzo	u_char type;
264159557Scognet
265159557Scognet	__asm __volatile("mrc p15, 0, %0, c0, c0, 1"
266239268Sgonzo		: "=r" (ctype));
267239268Sgonzo
268239268Sgonzo	cpuid = cpufunc_id();
269159557Scognet	/*
270159557Scognet	 * ...and thus spake the ARM ARM:
271159557Scognet	 *
272239268Sgonzo	 * If an <opcode2> value corresponding to an unimplemented or
273159557Scognet	 * reserved ID register is encountered, the System Control
274159557Scognet	 * processor returns the value of the main ID register.
275159557Scognet	 */
276239268Sgonzo	if (ctype == cpuid)
277159557Scognet		goto out;
278159557Scognet
279239268Sgonzo	if (CPU_CT_FORMAT(ctype) == CPU_CT_ARMV7) {
280239268Sgonzo		__asm __volatile("mrc p15, 1, %0, c0, c0, 1"
281239268Sgonzo		    : "=r" (clevel));
282239268Sgonzo		arm_cache_level = clevel;
283239268Sgonzo		arm_cache_loc = CPU_CLIDR_LOC(arm_cache_level) + 1;
284239268Sgonzo		i = 0;
285239268Sgonzo		while ((type = (clevel & 0x7)) && i < 7) {
286239268Sgonzo			if (type == CACHE_DCACHE || type == CACHE_UNI_CACHE ||
287239268Sgonzo			    type == CACHE_SEP_CACHE) {
288239268Sgonzo				sel = i << 1;
289239268Sgonzo				__asm __volatile("mcr p15, 2, %0, c0, c0, 0"
290239268Sgonzo				    : : "r" (sel));
291239268Sgonzo				__asm __volatile("mrc p15, 1, %0, c0, c0, 0"
292239268Sgonzo				    : "=r" (csize));
293239268Sgonzo				arm_cache_type[sel] = csize;
294239268Sgonzo			}
295239268Sgonzo			if (type == CACHE_ICACHE || type == CACHE_SEP_CACHE) {
296239268Sgonzo				sel = (i << 1) | 1;
297239268Sgonzo				__asm __volatile("mcr p15, 2, %0, c0, c0, 0"
298239268Sgonzo				    : : "r" (sel));
299239268Sgonzo				__asm __volatile("mrc p15, 1, %0, c0, c0, 0"
300239268Sgonzo				    : "=r" (csize));
301239268Sgonzo				arm_cache_type[sel] = csize;
302239268Sgonzo			}
303239268Sgonzo			i++;
304239268Sgonzo			clevel >>= 3;
305239268Sgonzo		}
306239268Sgonzo	} else {
307239268Sgonzo		if ((ctype & CPU_CT_S) == 0)
308239268Sgonzo			arm_pcache_unified = 1;
309239268Sgonzo
310239268Sgonzo		/*
311239268Sgonzo		 * If you want to know how this code works, go read the ARM ARM.
312239268Sgonzo		 */
313239268Sgonzo
314239268Sgonzo		arm_pcache_type = CPU_CT_CTYPE(ctype);
315239268Sgonzo
316239268Sgonzo		if (arm_pcache_unified == 0) {
317239268Sgonzo			isize = CPU_CT_ISIZE(ctype);
318239268Sgonzo			multiplier = (isize & CPU_CT_xSIZE_M) ? 3 : 2;
319239268Sgonzo			arm_picache_line_size = 1U << (CPU_CT_xSIZE_LEN(isize) + 3);
320239268Sgonzo			if (CPU_CT_xSIZE_ASSOC(isize) == 0) {
321239268Sgonzo				if (isize & CPU_CT_xSIZE_M)
322239268Sgonzo					arm_picache_line_size = 0; /* not present */
323239268Sgonzo				else
324239268Sgonzo					arm_picache_ways = 1;
325239268Sgonzo			} else {
326239268Sgonzo				arm_picache_ways = multiplier <<
327239268Sgonzo				    (CPU_CT_xSIZE_ASSOC(isize) - 1);
328239268Sgonzo			}
329239268Sgonzo			arm_picache_size = multiplier << (CPU_CT_xSIZE_SIZE(isize) + 8);
330239268Sgonzo		}
331239268Sgonzo
332239268Sgonzo		dsize = CPU_CT_DSIZE(ctype);
333239268Sgonzo		multiplier = (dsize & CPU_CT_xSIZE_M) ? 3 : 2;
334239268Sgonzo		arm_pdcache_line_size = 1U << (CPU_CT_xSIZE_LEN(dsize) + 3);
335239268Sgonzo		if (CPU_CT_xSIZE_ASSOC(dsize) == 0) {
336239268Sgonzo			if (dsize & CPU_CT_xSIZE_M)
337239268Sgonzo				arm_pdcache_line_size = 0; /* not present */
338159557Scognet			else
339239268Sgonzo				arm_pdcache_ways = 1;
340159557Scognet		} else {
341239268Sgonzo			arm_pdcache_ways = multiplier <<
342239268Sgonzo			    (CPU_CT_xSIZE_ASSOC(dsize) - 1);
343159557Scognet		}
344239268Sgonzo		arm_pdcache_size = multiplier << (CPU_CT_xSIZE_SIZE(dsize) + 8);
345239268Sgonzo
346239268Sgonzo		arm_dcache_align = arm_pdcache_line_size;
347239268Sgonzo
348239268Sgonzo		arm_dcache_l2_assoc = CPU_CT_xSIZE_ASSOC(dsize) + multiplier - 2;
349239268Sgonzo		arm_dcache_l2_linesize = CPU_CT_xSIZE_LEN(dsize) + 3;
350239268Sgonzo		arm_dcache_l2_nsets = 6 + CPU_CT_xSIZE_SIZE(dsize) -
351239268Sgonzo		    CPU_CT_xSIZE_ASSOC(dsize) - CPU_CT_xSIZE_LEN(dsize);
352239268Sgonzo
353239268Sgonzo	out:
354239268Sgonzo		arm_dcache_align_mask = arm_dcache_align - 1;
355159557Scognet	}
356159557Scognet}
357159557Scognet
358159557Scognetstatic void
359159557Scognetarm9_setup(void)
360159557Scognet{
361159557Scognet
362159557Scognet	get_cachetype_cp15();
363159557Scognet	arm9_dcache_sets_inc = 1U << arm_dcache_l2_linesize;
364159557Scognet	arm9_dcache_sets_max = (1U << (arm_dcache_l2_linesize +
365159557Scognet	    arm_dcache_l2_nsets)) - arm9_dcache_sets_inc;
366159557Scognet	arm9_dcache_index_inc = 1U << (32 - arm_dcache_l2_assoc);
367159557Scognet	arm9_dcache_index_max = 0U - arm9_dcache_index_inc;
368159557Scognet}
369159557Scognet
370239268Sgonzostatic void
371239268Sgonzoarmadaxp_idcache_wbinv_all(void)
372239268Sgonzo{
373239268Sgonzo	uint32_t feat;
374159557Scognet
375239268Sgonzo	__asm __volatile("mrc p15, 0, %0, c0, c1, 0" : "=r" (feat));
376239268Sgonzo	if (feat & ARM_PFR0_THUMBEE_MASK)
377239268Sgonzo		armv7_idcache_wbinv_all();
378239268Sgonzo	else
379239268Sgonzo		armv6_idcache_wbinv_all();
380239268Sgonzo
381239268Sgonzo}
382159868Scognet#ifdef KZIP
383153112Scognetstatic  unsigned char *orig_input, *i_input, *i_output;
384153112Scognet
385153112Scognet
386153112Scognetstatic u_int memcnt;		/* Memory allocated: blocks */
387153112Scognetstatic size_t memtot;		/* Memory allocated: bytes */
388153112Scognet/*
389153112Scognet * Library functions required by inflate().
390153112Scognet */
391153112Scognet
392153112Scognet#define MEMSIZ 0x8000
393153112Scognet
394153112Scognet/*
395153112Scognet * Allocate memory block.
396153112Scognet */
397153112Scognetunsigned char *
398153112Scognetkzipmalloc(int size)
399153112Scognet{
400153112Scognet	void *ptr;
401153112Scognet	static u_char mem[MEMSIZ];
402153112Scognet
403153112Scognet	if (memtot + size > MEMSIZ)
404153112Scognet		return NULL;
405153112Scognet	ptr = mem + memtot;
406153112Scognet	memtot += size;
407153112Scognet	memcnt++;
408153112Scognet	return ptr;
409153112Scognet}
410153112Scognet
411153112Scognet/*
412153112Scognet * Free allocated memory block.
413153112Scognet */
414153112Scognetvoid
415153112Scognetkzipfree(void *ptr)
416153112Scognet{
417153112Scognet	memcnt--;
418153112Scognet	if (!memcnt)
419153112Scognet		memtot = 0;
420153112Scognet}
421153112Scognet
422153112Scognetvoid
423153112Scognetputstr(char *dummy)
424153112Scognet{
425153112Scognet}
426153112Scognet
427153112Scognetstatic int
428153112Scognetinput(void *dummy)
429153112Scognet{
430159084Scognet	if ((size_t)(i_input - orig_input) >= KERNCOMPSIZE) {
431153112Scognet		return (GZ_EOF);
432153112Scognet	}
433153112Scognet	return *i_input++;
434153112Scognet}
435153112Scognet
436153112Scognetstatic int
437153112Scognetoutput(void *dummy, unsigned char *ptr, unsigned long len)
438153112Scognet{
439153112Scognet
440159758Scognet
441153112Scognet	memcpy(i_output, ptr, len);
442153112Scognet	i_output += len;
443153112Scognet	return (0);
444153112Scognet}
445153112Scognet
446153112Scognetstatic void *
447153112Scognetinflate_kernel(void *kernel, void *startaddr)
448153112Scognet{
449153112Scognet	struct inflate infl;
450252996Srpaulo	unsigned char slide[GZ_WSIZE];
451153112Scognet
452153112Scognet	orig_input = kernel;
453153549Scognet	memcnt = memtot = 0;
454252996Srpaulo	i_input = (unsigned char *)kernel + GZ_HEAD;
455153112Scognet	if (((char *)kernel)[3] & 0x18) {
456153112Scognet		while (*i_input)
457153112Scognet			i_input++;
458153112Scognet		i_input++;
459153112Scognet	}
460153112Scognet	i_output = startaddr;
461153112Scognet	bzero(&infl, sizeof(infl));
462153112Scognet	infl.gz_input = input;
463153112Scognet	infl.gz_output = output;
464153112Scognet	infl.gz_slide = slide;
465153112Scognet	inflate(&infl);
466153112Scognet	return ((char *)(((vm_offset_t)i_output & ~3) + 4));
467153112Scognet}
468153112Scognet
469153112Scognet#endif
470153112Scognet
471150861Scognetvoid *
472236991Simpload_kernel(unsigned int kstart, unsigned int curaddr,unsigned int func_end,
473153112Scognet    int d)
474150861Scognet{
475150861Scognet	Elf32_Ehdr *eh;
476159901Scognet	Elf32_Phdr phdr[64] /* XXX */, *php;
477159901Scognet	Elf32_Shdr shdr[64] /* XXX */;
478150861Scognet	int i,j;
479150861Scognet	void *entry_point;
480153112Scognet	int symtabindex = -1;
481153112Scognet	int symstrindex = -1;
482150861Scognet	vm_offset_t lastaddr = 0;
483188019Scognet	Elf_Addr ssym = 0;
484150861Scognet	Elf_Dyn *dp;
485150861Scognet
486150861Scognet	eh = (Elf32_Ehdr *)kstart;
487188019Scognet	ssym = 0;
488150861Scognet	entry_point = (void*)eh->e_entry;
489150861Scognet	memcpy(phdr, (void *)(kstart + eh->e_phoff ),
490150861Scognet	    eh->e_phnum * sizeof(phdr[0]));
491153549Scognet
492150861Scognet	/* Determine lastaddr. */
493150861Scognet	for (i = 0; i < eh->e_phnum; i++) {
494150861Scognet		if (lastaddr < (phdr[i].p_vaddr - KERNVIRTADDR + curaddr
495150861Scognet		    + phdr[i].p_memsz))
496150861Scognet			lastaddr = phdr[i].p_vaddr - KERNVIRTADDR +
497150861Scognet			    curaddr + phdr[i].p_memsz;
498150861Scognet	}
499150861Scognet
500153112Scognet	/* Save the symbol tables, as there're about to be scratched. */
501153549Scognet	memcpy(shdr, (void *)(kstart + eh->e_shoff),
502153549Scognet	    sizeof(*shdr) * eh->e_shnum);
503150861Scognet	if (eh->e_shnum * eh->e_shentsize != 0 &&
504150861Scognet	    eh->e_shoff != 0) {
505150861Scognet		for (i = 0; i < eh->e_shnum; i++) {
506150861Scognet			if (shdr[i].sh_type == SHT_SYMTAB) {
507150861Scognet				for (j = 0; j < eh->e_phnum; j++) {
508150861Scognet					if (phdr[j].p_type == PT_LOAD &&
509150861Scognet					    shdr[i].sh_offset >=
510150861Scognet					    phdr[j].p_offset &&
511236991Simp					    (shdr[i].sh_offset +
512150861Scognet					     shdr[i].sh_size <=
513150861Scognet					     phdr[j].p_offset +
514150861Scognet					     phdr[j].p_filesz)) {
515150861Scognet						shdr[i].sh_offset = 0;
516150861Scognet						shdr[i].sh_size = 0;
517150861Scognet						j = eh->e_phnum;
518150861Scognet					}
519150861Scognet				}
520236991Simp				if (shdr[i].sh_offset != 0 &&
521150861Scognet				    shdr[i].sh_size != 0) {
522150861Scognet					symtabindex = i;
523150861Scognet					symstrindex = shdr[i].sh_link;
524150861Scognet				}
525150861Scognet			}
526150861Scognet		}
527150861Scognet		func_end = roundup(func_end, sizeof(long));
528150861Scognet		if (symtabindex >= 0 && symstrindex >= 0) {
529150861Scognet			ssym = lastaddr;
530150861Scognet			if (d) {
531150861Scognet				memcpy((void *)func_end, (void *)(
532236991Simp				    shdr[symtabindex].sh_offset + kstart),
533150861Scognet				    shdr[symtabindex].sh_size);
534150861Scognet				memcpy((void *)(func_end +
535150861Scognet				    shdr[symtabindex].sh_size),
536150861Scognet				    (void *)(shdr[symstrindex].sh_offset +
537150861Scognet				    kstart), shdr[symstrindex].sh_size);
538153112Scognet			} else {
539153112Scognet				lastaddr += shdr[symtabindex].sh_size;
540153112Scognet				lastaddr = roundup(lastaddr,
541153112Scognet				    sizeof(shdr[symtabindex].sh_size));
542153112Scognet				lastaddr += sizeof(shdr[symstrindex].sh_size);
543153112Scognet				lastaddr += shdr[symstrindex].sh_size;
544236991Simp				lastaddr = roundup(lastaddr,
545153112Scognet				    sizeof(shdr[symstrindex].sh_size));
546150861Scognet			}
547150861Scognet
548150861Scognet		}
549150861Scognet	}
550150861Scognet	if (!d)
551150861Scognet		return ((void *)lastaddr);
552150861Scognet
553150861Scognet	j = eh->e_phnum;
554150861Scognet	for (i = 0; i < j; i++) {
555150861Scognet		volatile char c;
556153112Scognet
557153112Scognet		if (phdr[i].p_type != PT_LOAD)
558150861Scognet			continue;
559150861Scognet		memcpy((void *)(phdr[i].p_vaddr - KERNVIRTADDR + curaddr),
560150861Scognet		    (void*)(kstart + phdr[i].p_offset), phdr[i].p_filesz);
561150861Scognet		/* Clean space from oversized segments, eg: bss. */
562150861Scognet		if (phdr[i].p_filesz < phdr[i].p_memsz)
563236991Simp			bzero((void *)(phdr[i].p_vaddr - KERNVIRTADDR +
564150861Scognet			    curaddr + phdr[i].p_filesz), phdr[i].p_memsz -
565150861Scognet			    phdr[i].p_filesz);
566150861Scognet	}
567150861Scognet	/* Now grab the symbol tables. */
568153112Scognet	if (symtabindex >= 0 && symstrindex >= 0) {
569236991Simp		*(Elf_Size *)lastaddr =
570153112Scognet		    shdr[symtabindex].sh_size;
571153112Scognet		lastaddr += sizeof(shdr[symtabindex].sh_size);
572153112Scognet		memcpy((void*)lastaddr,
573153112Scognet		    (void *)func_end,
574153112Scognet		    shdr[symtabindex].sh_size);
575153112Scognet		lastaddr += shdr[symtabindex].sh_size;
576153112Scognet		lastaddr = roundup(lastaddr,
577153112Scognet		    sizeof(shdr[symtabindex].sh_size));
578153112Scognet		*(Elf_Size *)lastaddr =
579153112Scognet		    shdr[symstrindex].sh_size;
580153112Scognet		lastaddr += sizeof(shdr[symstrindex].sh_size);
581153112Scognet		memcpy((void*)lastaddr,
582153112Scognet		    (void*)(func_end +
583153112Scognet			    shdr[symtabindex].sh_size),
584153112Scognet		    shdr[symstrindex].sh_size);
585153112Scognet		lastaddr += shdr[symstrindex].sh_size;
586236991Simp		lastaddr = roundup(lastaddr,
587153112Scognet   		    sizeof(shdr[symstrindex].sh_size));
588153112Scognet		*(Elf_Addr *)curaddr = MAGIC_TRAMP_NUMBER;
589153112Scognet		*((Elf_Addr *)curaddr + 1) = ssym - curaddr + KERNVIRTADDR;
590153112Scognet		*((Elf_Addr *)curaddr + 2) = lastaddr - curaddr + KERNVIRTADDR;
591153112Scognet	} else
592153112Scognet		*(Elf_Addr *)curaddr = 0;
593153549Scognet	/* Invalidate the instruction cache. */
594153549Scognet	__asm __volatile("mcr p15, 0, %0, c7, c5, 0\n"
595153549Scognet	    		 "mcr p15, 0, %0, c7, c10, 4\n"
596153549Scognet			 : : "r" (curaddr));
597159758Scognet	__asm __volatile("mrc p15, 0, %0, c1, c0, 0\n"
598159758Scognet	    "bic %0, %0, #1\n" /* MMU_ENABLE */
599159758Scognet	    "mcr p15, 0, %0, c1, c0, 0\n"
600159758Scognet	    : "=r" (ssym));
601150861Scognet	/* Jump to the entry point. */
602150861Scognet	((void(*)(void))(entry_point - KERNVIRTADDR + curaddr))();
603150861Scognet	__asm __volatile(".globl func_end\n"
604150861Scognet	    "func_end:");
605150861Scognet
606252996Srpaulo	/* NOTREACHED */
607252996Srpaulo	return NULL;
608150861Scognet}
609150861Scognet
610150861Scognetextern char func_end[];
611150861Scognet
612154561Scognet
613171619Scognet#define PMAP_DOMAIN_KERNEL	0 /*
614154561Scognet				    * Just define it instead of including the
615154561Scognet				    * whole VM headers set.
616154561Scognet				    */
617154561Scognetint __hack;
618154561Scognetstatic __inline void
619159758Scognetsetup_pagetables(unsigned int pt_addr, vm_paddr_t physstart, vm_paddr_t physend,
620159758Scognet    int write_back)
621150861Scognet{
622154561Scognet	unsigned int *pd = (unsigned int *)pt_addr;
623154561Scognet	vm_paddr_t addr;
624154561Scognet	int domain = (DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)) | DOMAIN_CLIENT;
625154561Scognet	int tmp;
626154561Scognet
627154561Scognet	bzero(pd, L1_TABLE_SIZE);
628159758Scognet	for (addr = physstart; addr < physend; addr += L1_S_SIZE) {
629154561Scognet		pd[addr >> L1_S_SHIFT] = L1_TYPE_S|L1_S_C|L1_S_AP(AP_KRW)|
630154561Scognet		    L1_S_DOM(PMAP_DOMAIN_KERNEL) | addr;
631194609Scognet		if (write_back && 0)
632159758Scognet			pd[addr >> L1_S_SHIFT] |= L1_S_B;
633159758Scognet	}
634154561Scognet	/* XXX: See below */
635154561Scognet	if (0xfff00000 < physstart || 0xfff00000 > physend)
636154561Scognet		pd[0xfff00000 >> L1_S_SHIFT] = L1_TYPE_S|L1_S_AP(AP_KRW)|
637154561Scognet		    L1_S_DOM(PMAP_DOMAIN_KERNEL)|physstart;
638154561Scognet	__asm __volatile("mcr p15, 0, %1, c2, c0, 0\n" /* set TTB */
639154561Scognet	    		 "mcr p15, 0, %1, c8, c7, 0\n" /* Flush TTB */
640154561Scognet			 "mcr p15, 0, %2, c3, c0, 0\n" /* Set DAR */
641154561Scognet			 "mrc p15, 0, %0, c1, c0, 0\n"
642154561Scognet			 "orr %0, %0, #1\n" /* MMU_ENABLE */
643154561Scognet			 "mcr p15, 0, %0, c1, c0, 0\n"
644154561Scognet			 "mrc p15, 0, %0, c2, c0, 0\n" /* CPWAIT */
645154561Scognet			 "mov r0, r0\n"
646154561Scognet			 "sub pc, pc, #4\n" :
647154561Scognet			 "=r" (tmp) : "r" (pd), "r" (domain));
648154561Scognet
649236991Simp	/*
650154561Scognet	 * XXX: This is the most stupid workaround I've ever wrote.
651154561Scognet	 * For some reason, the KB9202 won't boot the kernel unless
652236991Simp	 * we access an address which is not in the
653154561Scognet	 * 0x20000000 - 0x20ffffff range. I hope I'll understand
654154561Scognet	 * what's going on later.
655154561Scognet	 */
656154561Scognet	__hack = *(volatile int *)0xfffff21c;
657154561Scognet}
658154561Scognet
659154561Scognetvoid
660154561Scognet__start(void)
661154561Scognet{
662150861Scognet	void *curaddr;
663159084Scognet	void *dst, *altdst;
664153112Scognet	char *kernel = (char *)&kernel_start;
665159557Scognet	int sp;
666159758Scognet	int pt_addr;
667150861Scognet
668150861Scognet	__asm __volatile("mov %0, pc"  :
669150861Scognet	    "=r" (curaddr));
670150861Scognet	curaddr = (void*)((unsigned int)curaddr & 0xfff00000);
671153112Scognet#ifdef KZIP
672153112Scognet	if (*kernel == 0x1f && kernel[1] == 0x8b) {
673236991Simp		pt_addr = (((int)&_end + KERNSIZE + 0x100) &
674154561Scognet		    ~(L1_TABLE_SIZE - 1)) + L1_TABLE_SIZE;
675159084Scognet
676159557Scognet#ifdef CPU_ARM9
677159557Scognet		/* So that idcache_wbinv works; */
678159557Scognet		if ((cpufunc_id() & 0x0000f000) == 0x00009000)
679159557Scognet			arm9_setup();
680159557Scognet#endif
681154561Scognet		setup_pagetables(pt_addr, (vm_paddr_t)curaddr,
682159758Scognet		    (vm_paddr_t)curaddr + 0x10000000, 1);
683153112Scognet		/* Gzipped kernel */
684153112Scognet		dst = inflate_kernel(kernel, &_end);
685153112Scognet		kernel = (char *)&_end;
686236991Simp		altdst = 4 + load_kernel((unsigned int)kernel,
687159084Scognet		    (unsigned int)curaddr,
688171619Scognet		    (unsigned int)&func_end + 800 , 0);
689159084Scognet		if (altdst > dst)
690159084Scognet			dst = altdst;
691234688Sstas
692234688Sstas		/*
693234688Sstas		 * Disable MMU.  Otherwise, setup_pagetables call below
694234688Sstas		 * might overwrite the L1 table we are currently using.
695234688Sstas		 */
696234688Sstas		cpu_idcache_wbinv_all();
697234688Sstas		cpu_l2cache_wbinv_all();
698234688Sstas		__asm __volatile("mrc p15, 0, %0, c1, c0, 0\n"
699234688Sstas		  "bic %0, %0, #1\n" /* MMU_DISABLE */
700234688Sstas		  "mcr p15, 0, %0, c1, c0, 0\n"
701234688Sstas		  :"=r" (pt_addr));
702153112Scognet	} else
703153112Scognet#endif
704236991Simp		dst = 4 + load_kernel((unsigned int)&kernel_start,
705236991Simp	    (unsigned int)curaddr,
706150861Scognet	    (unsigned int)&func_end, 0);
707159758Scognet	dst = (void *)(((vm_offset_t)dst & ~3));
708159758Scognet	pt_addr = ((unsigned int)dst &~(L1_TABLE_SIZE - 1)) + L1_TABLE_SIZE;
709159758Scognet	setup_pagetables(pt_addr, (vm_paddr_t)curaddr,
710159758Scognet	    (vm_paddr_t)curaddr + 0x10000000, 0);
711159758Scognet	sp = pt_addr + L1_TABLE_SIZE + 8192;
712159758Scognet	sp = sp &~3;
713159758Scognet	dst = (void *)(sp + 4);
714236991Simp	memcpy((void *)dst, (void *)&load_kernel, (unsigned int)&func_end -
715171619Scognet	    (unsigned int)&load_kernel + 800);
716236991Simp	do_call(dst, kernel, dst + (unsigned int)(&func_end) -
717171619Scognet	    (unsigned int)(&load_kernel) + 800, sp);
718150861Scognet}
719248364Sandrew
720248364Sandrew#ifdef __ARM_EABI__
721248364Sandrew/* We need to provide these functions but never call them */
722248364Sandrewvoid __aeabi_unwind_cpp_pr0(void);
723248364Sandrewvoid __aeabi_unwind_cpp_pr1(void);
724248364Sandrewvoid __aeabi_unwind_cpp_pr2(void);
725248364Sandrew
726248364Sandrew__strong_reference(__aeabi_unwind_cpp_pr0, __aeabi_unwind_cpp_pr1);
727248364Sandrew__strong_reference(__aeabi_unwind_cpp_pr0, __aeabi_unwind_cpp_pr2);
728248364Sandrewvoid
729248364Sandrew__aeabi_unwind_cpp_pr0(void)
730248364Sandrew{
731248364Sandrew}
732248364Sandrew#endif
733248364Sandrew
734