xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/powermac/bootx_init.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Early boot support code for BootX bootloader
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org)
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/kernel.h>
9*4882a593Smuzhiyun #include <linux/string.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <generated/utsrelease.h>
12*4882a593Smuzhiyun #include <asm/sections.h>
13*4882a593Smuzhiyun #include <asm/prom.h>
14*4882a593Smuzhiyun #include <asm/page.h>
15*4882a593Smuzhiyun #include <asm/bootx.h>
16*4882a593Smuzhiyun #include <asm/btext.h>
17*4882a593Smuzhiyun #include <asm/io.h>
18*4882a593Smuzhiyun #include <asm/setup.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #undef DEBUG
21*4882a593Smuzhiyun #define SET_BOOT_BAT
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #ifdef DEBUG
24*4882a593Smuzhiyun #define DBG(fmt...) do { bootx_printf(fmt); } while(0)
25*4882a593Smuzhiyun #else
26*4882a593Smuzhiyun #define DBG(fmt...) do { } while(0)
27*4882a593Smuzhiyun #endif
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun static unsigned long __initdata bootx_dt_strbase;
32*4882a593Smuzhiyun static unsigned long __initdata bootx_dt_strend;
33*4882a593Smuzhiyun static unsigned long __initdata bootx_node_chosen;
34*4882a593Smuzhiyun static boot_infos_t * __initdata bootx_info;
35*4882a593Smuzhiyun static char __initdata bootx_disp_path[256];
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* Is boot-info compatible ? */
38*4882a593Smuzhiyun #define BOOT_INFO_IS_COMPATIBLE(bi) \
39*4882a593Smuzhiyun 	((bi)->compatible_version <= BOOT_INFO_VERSION)
40*4882a593Smuzhiyun #define BOOT_INFO_IS_V2_COMPATIBLE(bi)	((bi)->version >= 2)
41*4882a593Smuzhiyun #define BOOT_INFO_IS_V4_COMPATIBLE(bi)	((bi)->version >= 4)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
bootx_printf(const char * format,...)44*4882a593Smuzhiyun static void __init bootx_printf(const char *format, ...)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	const char *p, *q, *s;
47*4882a593Smuzhiyun 	va_list args;
48*4882a593Smuzhiyun 	unsigned long v;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	va_start(args, format);
51*4882a593Smuzhiyun 	for (p = format; *p != 0; p = q) {
52*4882a593Smuzhiyun 		for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
53*4882a593Smuzhiyun 			;
54*4882a593Smuzhiyun 		if (q > p)
55*4882a593Smuzhiyun 			btext_drawtext(p, q - p);
56*4882a593Smuzhiyun 		if (*q == 0)
57*4882a593Smuzhiyun 			break;
58*4882a593Smuzhiyun 		if (*q == '\n') {
59*4882a593Smuzhiyun 			++q;
60*4882a593Smuzhiyun 			btext_flushline();
61*4882a593Smuzhiyun 			btext_drawstring("\r\n");
62*4882a593Smuzhiyun 			btext_flushline();
63*4882a593Smuzhiyun 			continue;
64*4882a593Smuzhiyun 		}
65*4882a593Smuzhiyun 		++q;
66*4882a593Smuzhiyun 		if (*q == 0)
67*4882a593Smuzhiyun 			break;
68*4882a593Smuzhiyun 		switch (*q) {
69*4882a593Smuzhiyun 		case 's':
70*4882a593Smuzhiyun 			++q;
71*4882a593Smuzhiyun 			s = va_arg(args, const char *);
72*4882a593Smuzhiyun 			if (s == NULL)
73*4882a593Smuzhiyun 				s = "<NULL>";
74*4882a593Smuzhiyun 			btext_drawstring(s);
75*4882a593Smuzhiyun 			break;
76*4882a593Smuzhiyun 		case 'x':
77*4882a593Smuzhiyun 			++q;
78*4882a593Smuzhiyun 			v = va_arg(args, unsigned long);
79*4882a593Smuzhiyun 			btext_drawhex(v);
80*4882a593Smuzhiyun 			break;
81*4882a593Smuzhiyun 		}
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	va_end(args);
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun #else /* CONFIG_BOOTX_TEXT */
bootx_printf(const char * format,...)86*4882a593Smuzhiyun static void __init bootx_printf(const char *format, ...) {}
87*4882a593Smuzhiyun #endif /* CONFIG_BOOTX_TEXT */
88*4882a593Smuzhiyun 
bootx_early_getprop(unsigned long base,unsigned long node,char * prop)89*4882a593Smuzhiyun static void * __init bootx_early_getprop(unsigned long base,
90*4882a593Smuzhiyun 					 unsigned long node,
91*4882a593Smuzhiyun 					 char *prop)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
94*4882a593Smuzhiyun 	u32 *ppp = &np->properties;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	while(*ppp) {
97*4882a593Smuzhiyun 		struct bootx_dt_prop *pp =
98*4882a593Smuzhiyun 			(struct bootx_dt_prop *)(base + *ppp);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 		if (strcmp((char *)((unsigned long)pp->name + base),
101*4882a593Smuzhiyun 			   prop) == 0) {
102*4882a593Smuzhiyun 			return (void *)((unsigned long)pp->value + base);
103*4882a593Smuzhiyun 		}
104*4882a593Smuzhiyun 		ppp = &pp->next;
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 	return NULL;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun #define dt_push_token(token, mem) \
110*4882a593Smuzhiyun 	do { \
111*4882a593Smuzhiyun 		*(mem) = ALIGN(*(mem),4); \
112*4882a593Smuzhiyun 		*((u32 *)*(mem)) = token; \
113*4882a593Smuzhiyun 		*(mem) += 4; \
114*4882a593Smuzhiyun 	} while(0)
115*4882a593Smuzhiyun 
bootx_dt_find_string(char * str)116*4882a593Smuzhiyun static unsigned long __init bootx_dt_find_string(char *str)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	char *s, *os;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	s = os = (char *)bootx_dt_strbase;
121*4882a593Smuzhiyun 	s += 4;
122*4882a593Smuzhiyun 	while (s <  (char *)bootx_dt_strend) {
123*4882a593Smuzhiyun 		if (strcmp(s, str) == 0)
124*4882a593Smuzhiyun 			return s - os;
125*4882a593Smuzhiyun 		s += strlen(s) + 1;
126*4882a593Smuzhiyun 	}
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
bootx_dt_add_prop(char * name,void * data,int size,unsigned long * mem_end)130*4882a593Smuzhiyun static void __init bootx_dt_add_prop(char *name, void *data, int size,
131*4882a593Smuzhiyun 				  unsigned long *mem_end)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	unsigned long soff = bootx_dt_find_string(name);
134*4882a593Smuzhiyun 	if (data == NULL)
135*4882a593Smuzhiyun 		size = 0;
136*4882a593Smuzhiyun 	if (soff == 0) {
137*4882a593Smuzhiyun 		bootx_printf("WARNING: Can't find string index for <%s>\n",
138*4882a593Smuzhiyun 			     name);
139*4882a593Smuzhiyun 		return;
140*4882a593Smuzhiyun 	}
141*4882a593Smuzhiyun 	if (size > 0x20000) {
142*4882a593Smuzhiyun 		bootx_printf("WARNING: ignoring large property ");
143*4882a593Smuzhiyun 		bootx_printf("%s length 0x%x\n", name, size);
144*4882a593Smuzhiyun 		return;
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun 	dt_push_token(OF_DT_PROP, mem_end);
147*4882a593Smuzhiyun 	dt_push_token(size, mem_end);
148*4882a593Smuzhiyun 	dt_push_token(soff, mem_end);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/* push property content */
151*4882a593Smuzhiyun 	if (size && data) {
152*4882a593Smuzhiyun 		memcpy((void *)*mem_end, data, size);
153*4882a593Smuzhiyun 		*mem_end = ALIGN(*mem_end + size, 4);
154*4882a593Smuzhiyun 	}
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun 
bootx_add_chosen_props(unsigned long base,unsigned long * mem_end)157*4882a593Smuzhiyun static void __init bootx_add_chosen_props(unsigned long base,
158*4882a593Smuzhiyun 					  unsigned long *mem_end)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	u32 val;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx", NULL, 0, mem_end);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	if (bootx_info->kernelParamsOffset) {
165*4882a593Smuzhiyun 		char *args = (char *)((unsigned long)bootx_info) +
166*4882a593Smuzhiyun 			bootx_info->kernelParamsOffset;
167*4882a593Smuzhiyun 		bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end);
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 	if (bootx_info->ramDisk) {
170*4882a593Smuzhiyun 		val = ((unsigned long)bootx_info) + bootx_info->ramDisk;
171*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end);
172*4882a593Smuzhiyun 		val += bootx_info->ramDiskSize;
173*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end);
174*4882a593Smuzhiyun 	}
175*4882a593Smuzhiyun 	if (strlen(bootx_disp_path))
176*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,stdout-path", bootx_disp_path,
177*4882a593Smuzhiyun 				  strlen(bootx_disp_path) + 1, mem_end);
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
bootx_add_display_props(unsigned long base,unsigned long * mem_end,int has_real_node)180*4882a593Smuzhiyun static void __init bootx_add_display_props(unsigned long base,
181*4882a593Smuzhiyun 					   unsigned long *mem_end,
182*4882a593Smuzhiyun 					   int has_real_node)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	boot_infos_t *bi = bootx_info;
185*4882a593Smuzhiyun 	u32 tmp;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	if (has_real_node) {
188*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end);
189*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,opened", NULL, 0, mem_end);
190*4882a593Smuzhiyun 	} else
191*4882a593Smuzhiyun 		bootx_dt_add_prop("linux,bootx-noscreen", NULL, 0, mem_end);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	tmp = bi->dispDeviceDepth;
194*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx-depth", &tmp, 4, mem_end);
195*4882a593Smuzhiyun 	tmp = bi->dispDeviceRect[2] - bi->dispDeviceRect[0];
196*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx-width", &tmp, 4, mem_end);
197*4882a593Smuzhiyun 	tmp = bi->dispDeviceRect[3] - bi->dispDeviceRect[1];
198*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx-height", &tmp, 4, mem_end);
199*4882a593Smuzhiyun 	tmp = bi->dispDeviceRowBytes;
200*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx-linebytes", &tmp, 4, mem_end);
201*4882a593Smuzhiyun 	tmp = (u32)bi->dispDeviceBase;
202*4882a593Smuzhiyun 	if (tmp == 0)
203*4882a593Smuzhiyun 		tmp = (u32)bi->logicalDisplayBase;
204*4882a593Smuzhiyun 	tmp += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes;
205*4882a593Smuzhiyun 	tmp += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8);
206*4882a593Smuzhiyun 	bootx_dt_add_prop("linux,bootx-addr", &tmp, 4, mem_end);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
bootx_dt_add_string(char * s,unsigned long * mem_end)209*4882a593Smuzhiyun static void __init bootx_dt_add_string(char *s, unsigned long *mem_end)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	unsigned int l = strlen(s) + 1;
212*4882a593Smuzhiyun 	memcpy((void *)*mem_end, s, l);
213*4882a593Smuzhiyun 	bootx_dt_strend = *mem_end = *mem_end + l;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
bootx_scan_dt_build_strings(unsigned long base,unsigned long node,unsigned long * mem_end)216*4882a593Smuzhiyun static void __init bootx_scan_dt_build_strings(unsigned long base,
217*4882a593Smuzhiyun 					       unsigned long node,
218*4882a593Smuzhiyun 					       unsigned long *mem_end)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun 	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
221*4882a593Smuzhiyun 	u32 *cpp, *ppp = &np->properties;
222*4882a593Smuzhiyun 	unsigned long soff;
223*4882a593Smuzhiyun 	char *namep;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	/* Keep refs to known nodes */
226*4882a593Smuzhiyun 	namep = np->full_name ? (char *)(base + np->full_name) : NULL;
227*4882a593Smuzhiyun        	if (namep == NULL) {
228*4882a593Smuzhiyun 		bootx_printf("Node without a full name !\n");
229*4882a593Smuzhiyun 		namep = "";
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 	DBG("* strings: %s\n", namep);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	if (!strcmp(namep, "/chosen")) {
234*4882a593Smuzhiyun 		DBG(" detected /chosen ! adding properties names !\n");
235*4882a593Smuzhiyun 		bootx_dt_add_string("linux,bootx", mem_end);
236*4882a593Smuzhiyun 		bootx_dt_add_string("linux,stdout-path", mem_end);
237*4882a593Smuzhiyun 		bootx_dt_add_string("linux,initrd-start", mem_end);
238*4882a593Smuzhiyun 		bootx_dt_add_string("linux,initrd-end", mem_end);
239*4882a593Smuzhiyun 		bootx_dt_add_string("bootargs", mem_end);
240*4882a593Smuzhiyun 		bootx_node_chosen = node;
241*4882a593Smuzhiyun 	}
242*4882a593Smuzhiyun 	if (node == bootx_info->dispDeviceRegEntryOffset) {
243*4882a593Smuzhiyun 		DBG(" detected display ! adding properties names !\n");
244*4882a593Smuzhiyun 		bootx_dt_add_string("linux,boot-display", mem_end);
245*4882a593Smuzhiyun 		bootx_dt_add_string("linux,opened", mem_end);
246*4882a593Smuzhiyun 		strlcpy(bootx_disp_path, namep, sizeof(bootx_disp_path));
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	/* get and store all property names */
250*4882a593Smuzhiyun 	while (*ppp) {
251*4882a593Smuzhiyun 		struct bootx_dt_prop *pp =
252*4882a593Smuzhiyun 			(struct bootx_dt_prop *)(base + *ppp);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 		namep = pp->name ? (char *)(base + pp->name) : NULL;
255*4882a593Smuzhiyun  		if (namep == NULL || strcmp(namep, "name") == 0)
256*4882a593Smuzhiyun  			goto next;
257*4882a593Smuzhiyun 		/* get/create string entry */
258*4882a593Smuzhiyun 		soff = bootx_dt_find_string(namep);
259*4882a593Smuzhiyun 		if (soff == 0)
260*4882a593Smuzhiyun 			bootx_dt_add_string(namep, mem_end);
261*4882a593Smuzhiyun 	next:
262*4882a593Smuzhiyun 		ppp = &pp->next;
263*4882a593Smuzhiyun 	}
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* do all our children */
266*4882a593Smuzhiyun 	cpp = &np->child;
267*4882a593Smuzhiyun 	while(*cpp) {
268*4882a593Smuzhiyun 		np = (struct bootx_dt_node *)(base + *cpp);
269*4882a593Smuzhiyun 		bootx_scan_dt_build_strings(base, *cpp, mem_end);
270*4882a593Smuzhiyun 		cpp = &np->sibling;
271*4882a593Smuzhiyun 	}
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
bootx_scan_dt_build_struct(unsigned long base,unsigned long node,unsigned long * mem_end)274*4882a593Smuzhiyun static void __init bootx_scan_dt_build_struct(unsigned long base,
275*4882a593Smuzhiyun 					      unsigned long node,
276*4882a593Smuzhiyun 					      unsigned long *mem_end)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
279*4882a593Smuzhiyun 	u32 *cpp, *ppp = &np->properties;
280*4882a593Smuzhiyun 	char *namep, *p, *ep, *lp;
281*4882a593Smuzhiyun 	int l;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	dt_push_token(OF_DT_BEGIN_NODE, mem_end);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	/* get the node's full name */
286*4882a593Smuzhiyun 	namep = np->full_name ? (char *)(base + np->full_name) : NULL;
287*4882a593Smuzhiyun 	if (namep == NULL)
288*4882a593Smuzhiyun 		namep = "";
289*4882a593Smuzhiyun 	l = strlen(namep);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	DBG("* struct: %s\n", namep);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	/* Fixup an Apple bug where they have bogus \0 chars in the
294*4882a593Smuzhiyun 	 * middle of the path in some properties, and extract
295*4882a593Smuzhiyun 	 * the unit name (everything after the last '/').
296*4882a593Smuzhiyun 	 */
297*4882a593Smuzhiyun 	memcpy((void *)*mem_end, namep, l + 1);
298*4882a593Smuzhiyun 	namep = (char *)*mem_end;
299*4882a593Smuzhiyun 	for (lp = p = namep, ep = namep + l; p < ep; p++) {
300*4882a593Smuzhiyun 		if (*p == '/')
301*4882a593Smuzhiyun 			lp = namep;
302*4882a593Smuzhiyun 		else if (*p != 0)
303*4882a593Smuzhiyun 			*lp++ = *p;
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun 	*lp = 0;
306*4882a593Smuzhiyun 	*mem_end = ALIGN((unsigned long)lp + 1, 4);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	/* get and store all properties */
309*4882a593Smuzhiyun 	while (*ppp) {
310*4882a593Smuzhiyun 		struct bootx_dt_prop *pp =
311*4882a593Smuzhiyun 			(struct bootx_dt_prop *)(base + *ppp);
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 		namep = pp->name ? (char *)(base + pp->name) : NULL;
314*4882a593Smuzhiyun 		/* Skip "name" */
315*4882a593Smuzhiyun  		if (namep == NULL || !strcmp(namep, "name"))
316*4882a593Smuzhiyun  			goto next;
317*4882a593Smuzhiyun 		/* Skip "bootargs" in /chosen too as we replace it */
318*4882a593Smuzhiyun 		if (node == bootx_node_chosen && !strcmp(namep, "bootargs"))
319*4882a593Smuzhiyun 			goto next;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 		/* push property head */
322*4882a593Smuzhiyun 		bootx_dt_add_prop(namep,
323*4882a593Smuzhiyun 				  pp->value ? (void *)(base + pp->value): NULL,
324*4882a593Smuzhiyun 				  pp->length, mem_end);
325*4882a593Smuzhiyun 	next:
326*4882a593Smuzhiyun 		ppp = &pp->next;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	if (node == bootx_node_chosen) {
330*4882a593Smuzhiyun 		bootx_add_chosen_props(base, mem_end);
331*4882a593Smuzhiyun 		if (bootx_info->dispDeviceRegEntryOffset == 0)
332*4882a593Smuzhiyun 			bootx_add_display_props(base, mem_end, 0);
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 	else if (node == bootx_info->dispDeviceRegEntryOffset)
335*4882a593Smuzhiyun 		bootx_add_display_props(base, mem_end, 1);
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* do all our children */
338*4882a593Smuzhiyun 	cpp = &np->child;
339*4882a593Smuzhiyun 	while(*cpp) {
340*4882a593Smuzhiyun 		np = (struct bootx_dt_node *)(base + *cpp);
341*4882a593Smuzhiyun 		bootx_scan_dt_build_struct(base, *cpp, mem_end);
342*4882a593Smuzhiyun 		cpp = &np->sibling;
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	dt_push_token(OF_DT_END_NODE, mem_end);
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun 
bootx_flatten_dt(unsigned long start)348*4882a593Smuzhiyun static unsigned long __init bootx_flatten_dt(unsigned long start)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	boot_infos_t *bi = bootx_info;
351*4882a593Smuzhiyun 	unsigned long mem_start, mem_end;
352*4882a593Smuzhiyun 	struct boot_param_header *hdr;
353*4882a593Smuzhiyun 	unsigned long base;
354*4882a593Smuzhiyun 	u64 *rsvmap;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	/* Start using memory after the big blob passed by BootX, get
357*4882a593Smuzhiyun 	 * some space for the header
358*4882a593Smuzhiyun 	 */
359*4882a593Smuzhiyun 	mem_start = mem_end = ALIGN(((unsigned long)bi) + start, 4);
360*4882a593Smuzhiyun 	DBG("Boot params header at: %x\n", mem_start);
361*4882a593Smuzhiyun 	hdr = (struct boot_param_header *)mem_start;
362*4882a593Smuzhiyun 	mem_end += sizeof(struct boot_param_header);
363*4882a593Smuzhiyun 	rsvmap = (u64 *)(ALIGN(mem_end, 8));
364*4882a593Smuzhiyun 	hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start;
365*4882a593Smuzhiyun 	mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64);
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	/* Get base of tree */
368*4882a593Smuzhiyun 	base = ((unsigned long)bi) + bi->deviceTreeOffset;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	/* Build string array */
371*4882a593Smuzhiyun 	DBG("Building string array at: %x\n", mem_end);
372*4882a593Smuzhiyun 	DBG("Device Tree Base=%x\n", base);
373*4882a593Smuzhiyun 	bootx_dt_strbase = mem_end;
374*4882a593Smuzhiyun 	mem_end += 4;
375*4882a593Smuzhiyun 	bootx_dt_strend = mem_end;
376*4882a593Smuzhiyun 	bootx_scan_dt_build_strings(base, 4, &mem_end);
377*4882a593Smuzhiyun 	/* Add some strings */
378*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-noscreen", &mem_end);
379*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-depth", &mem_end);
380*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-width", &mem_end);
381*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-height", &mem_end);
382*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-linebytes", &mem_end);
383*4882a593Smuzhiyun 	bootx_dt_add_string("linux,bootx-addr", &mem_end);
384*4882a593Smuzhiyun 	/* Wrap up strings */
385*4882a593Smuzhiyun 	hdr->off_dt_strings = bootx_dt_strbase - mem_start;
386*4882a593Smuzhiyun 	hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	/* Build structure */
389*4882a593Smuzhiyun 	mem_end = ALIGN(mem_end, 16);
390*4882a593Smuzhiyun 	DBG("Building device tree structure at: %x\n", mem_end);
391*4882a593Smuzhiyun 	hdr->off_dt_struct = mem_end - mem_start;
392*4882a593Smuzhiyun 	bootx_scan_dt_build_struct(base, 4, &mem_end);
393*4882a593Smuzhiyun 	dt_push_token(OF_DT_END, &mem_end);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	/* Finish header */
396*4882a593Smuzhiyun 	hdr->boot_cpuid_phys = 0;
397*4882a593Smuzhiyun 	hdr->magic = OF_DT_HEADER;
398*4882a593Smuzhiyun 	hdr->totalsize = mem_end - mem_start;
399*4882a593Smuzhiyun 	hdr->version = OF_DT_VERSION;
400*4882a593Smuzhiyun 	/* Version 16 is not backward compatible */
401*4882a593Smuzhiyun 	hdr->last_comp_version = 0x10;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	/* Reserve the whole thing and copy the reserve map in, we
404*4882a593Smuzhiyun 	 * also bump mem_reserve_cnt to cause further reservations to
405*4882a593Smuzhiyun 	 * fail since it's too late.
406*4882a593Smuzhiyun 	 */
407*4882a593Smuzhiyun 	mem_end = ALIGN(mem_end, PAGE_SIZE);
408*4882a593Smuzhiyun 	DBG("End of boot params: %x\n", mem_end);
409*4882a593Smuzhiyun 	rsvmap[0] = mem_start;
410*4882a593Smuzhiyun 	rsvmap[1] = mem_end;
411*4882a593Smuzhiyun 	if (bootx_info->ramDisk) {
412*4882a593Smuzhiyun 		rsvmap[2] = ((unsigned long)bootx_info) + bootx_info->ramDisk;
413*4882a593Smuzhiyun 		rsvmap[3] = rsvmap[2] + bootx_info->ramDiskSize;
414*4882a593Smuzhiyun 		rsvmap[4] = 0;
415*4882a593Smuzhiyun 		rsvmap[5] = 0;
416*4882a593Smuzhiyun 	} else {
417*4882a593Smuzhiyun 		rsvmap[2] = 0;
418*4882a593Smuzhiyun 		rsvmap[3] = 0;
419*4882a593Smuzhiyun 	}
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	return (unsigned long)hdr;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
btext_welcome(boot_infos_t * bi)426*4882a593Smuzhiyun static void __init btext_welcome(boot_infos_t *bi)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 	unsigned long flags;
429*4882a593Smuzhiyun 	unsigned long pvr;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n");
432*4882a593Smuzhiyun 	bootx_printf("\nlinked at        : 0x%x", KERNELBASE);
433*4882a593Smuzhiyun 	bootx_printf("\nframe buffer at  : 0x%x", bi->dispDeviceBase);
434*4882a593Smuzhiyun 	bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase);
435*4882a593Smuzhiyun 	bootx_printf(" (log)");
436*4882a593Smuzhiyun 	bootx_printf("\nklimit           : 0x%x",(unsigned long)klimit);
437*4882a593Smuzhiyun 	bootx_printf("\nboot_info at     : 0x%x", bi);
438*4882a593Smuzhiyun 	__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
439*4882a593Smuzhiyun 	bootx_printf("\nMSR              : 0x%x", flags);
440*4882a593Smuzhiyun 	__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
441*4882a593Smuzhiyun 	bootx_printf("\nPVR              : 0x%x", pvr);
442*4882a593Smuzhiyun 	pvr >>= 16;
443*4882a593Smuzhiyun 	if (pvr > 1) {
444*4882a593Smuzhiyun 	    __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
445*4882a593Smuzhiyun 	    bootx_printf("\nHID0             : 0x%x", flags);
446*4882a593Smuzhiyun 	}
447*4882a593Smuzhiyun 	if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
448*4882a593Smuzhiyun 	    __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
449*4882a593Smuzhiyun 	    bootx_printf("\nICTC             : 0x%x", flags);
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun #ifdef DEBUG
452*4882a593Smuzhiyun 	bootx_printf("\n\n");
453*4882a593Smuzhiyun 	bootx_printf("bi->deviceTreeOffset   : 0x%x\n",
454*4882a593Smuzhiyun 		     bi->deviceTreeOffset);
455*4882a593Smuzhiyun 	bootx_printf("bi->deviceTreeSize     : 0x%x\n",
456*4882a593Smuzhiyun 		     bi->deviceTreeSize);
457*4882a593Smuzhiyun #endif
458*4882a593Smuzhiyun 	bootx_printf("\n\n");
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun #endif /* CONFIG_BOOTX_TEXT */
461*4882a593Smuzhiyun 
bootx_init(unsigned long r3,unsigned long r4)462*4882a593Smuzhiyun void __init bootx_init(unsigned long r3, unsigned long r4)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun 	boot_infos_t *bi = (boot_infos_t *) r4;
465*4882a593Smuzhiyun 	unsigned long hdr;
466*4882a593Smuzhiyun 	unsigned long space;
467*4882a593Smuzhiyun 	unsigned long ptr;
468*4882a593Smuzhiyun 	char *model;
469*4882a593Smuzhiyun 	unsigned long offset = reloc_offset();
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	reloc_got2(offset);
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	bootx_info = bi;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	/* We haven't cleared any bss at this point, make sure
476*4882a593Smuzhiyun 	 * what we need is initialized
477*4882a593Smuzhiyun 	 */
478*4882a593Smuzhiyun 	bootx_dt_strbase = bootx_dt_strend = 0;
479*4882a593Smuzhiyun 	bootx_node_chosen = 0;
480*4882a593Smuzhiyun 	bootx_disp_path[0] = 0;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
483*4882a593Smuzhiyun 		bi->logicalDisplayBase = bi->dispDeviceBase;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	/* Fixup depth 16 -> 15 as that's what MacOS calls 16bpp */
486*4882a593Smuzhiyun 	if (bi->dispDeviceDepth == 16)
487*4882a593Smuzhiyun 		bi->dispDeviceDepth = 15;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
491*4882a593Smuzhiyun 	ptr = (unsigned long)bi->logicalDisplayBase;
492*4882a593Smuzhiyun 	ptr += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes;
493*4882a593Smuzhiyun 	ptr += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8);
494*4882a593Smuzhiyun 	btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0],
495*4882a593Smuzhiyun 			    bi->dispDeviceRect[3] - bi->dispDeviceRect[1],
496*4882a593Smuzhiyun 			    bi->dispDeviceDepth, bi->dispDeviceRowBytes,
497*4882a593Smuzhiyun 			    (unsigned long)bi->logicalDisplayBase);
498*4882a593Smuzhiyun 	btext_clearscreen();
499*4882a593Smuzhiyun 	btext_flushscreen();
500*4882a593Smuzhiyun #endif /* CONFIG_BOOTX_TEXT */
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/*
503*4882a593Smuzhiyun 	 * Test if boot-info is compatible.  Done only in config
504*4882a593Smuzhiyun 	 * CONFIG_BOOTX_TEXT since there is nothing much we can do
505*4882a593Smuzhiyun 	 * with an incompatible version, except display a message
506*4882a593Smuzhiyun 	 * and eventually hang the processor...
507*4882a593Smuzhiyun 	 *
508*4882a593Smuzhiyun 	 * I'll try to keep enough of boot-info compatible in the
509*4882a593Smuzhiyun 	 * future to always allow display of this message;
510*4882a593Smuzhiyun 	 */
511*4882a593Smuzhiyun 	if (!BOOT_INFO_IS_COMPATIBLE(bi)) {
512*4882a593Smuzhiyun 		bootx_printf(" !!! WARNING - Incompatible version"
513*4882a593Smuzhiyun 			     " of BootX !!!\n\n\n");
514*4882a593Smuzhiyun 		for (;;)
515*4882a593Smuzhiyun 			;
516*4882a593Smuzhiyun 	}
517*4882a593Smuzhiyun 	if (bi->architecture != BOOT_ARCH_PCI) {
518*4882a593Smuzhiyun 		bootx_printf(" !!! WARNING - Unsupported machine"
519*4882a593Smuzhiyun 			     " architecture !\n");
520*4882a593Smuzhiyun 		for (;;)
521*4882a593Smuzhiyun 			;
522*4882a593Smuzhiyun 	}
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
525*4882a593Smuzhiyun 	btext_welcome(bi);
526*4882a593Smuzhiyun #endif
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	/* New BootX enters kernel with MMU off, i/os are not allowed
529*4882a593Smuzhiyun 	 * here. This hack will have been done by the boostrap anyway.
530*4882a593Smuzhiyun 	 */
531*4882a593Smuzhiyun 	if (bi->version < 4) {
532*4882a593Smuzhiyun 		/*
533*4882a593Smuzhiyun 		 * XXX If this is an iMac, turn off the USB controller.
534*4882a593Smuzhiyun 		 */
535*4882a593Smuzhiyun 		model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset,
536*4882a593Smuzhiyun 						     4, "model");
537*4882a593Smuzhiyun 		if (model
538*4882a593Smuzhiyun 		    && (strcmp(model, "iMac,1") == 0
539*4882a593Smuzhiyun 			|| strcmp(model, "PowerMac1,1") == 0)) {
540*4882a593Smuzhiyun 			bootx_printf("iMac,1 detected, shutting down USB\n");
541*4882a593Smuzhiyun 			out_le32((unsigned __iomem *)0x80880008, 1);	/* XXX */
542*4882a593Smuzhiyun 		}
543*4882a593Smuzhiyun 	}
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	/* Get a pointer that points above the device tree, args, ramdisk,
546*4882a593Smuzhiyun 	 * etc... to use for generating the flattened tree
547*4882a593Smuzhiyun 	 */
548*4882a593Smuzhiyun 	if (bi->version < 5) {
549*4882a593Smuzhiyun 		space = bi->deviceTreeOffset + bi->deviceTreeSize;
550*4882a593Smuzhiyun 		if (bi->ramDisk >= space)
551*4882a593Smuzhiyun 			space = bi->ramDisk + bi->ramDiskSize;
552*4882a593Smuzhiyun 	} else
553*4882a593Smuzhiyun 		space = bi->totalParamsSize;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	bootx_printf("Total space used by parameters & ramdisk: 0x%x\n", space);
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	/* New BootX will have flushed all TLBs and enters kernel with
558*4882a593Smuzhiyun 	 * MMU switched OFF, so this should not be useful anymore.
559*4882a593Smuzhiyun 	 */
560*4882a593Smuzhiyun 	if (bi->version < 4) {
561*4882a593Smuzhiyun 		unsigned long x __maybe_unused;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 		bootx_printf("Touching pages...\n");
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 		/*
566*4882a593Smuzhiyun 		 * Touch each page to make sure the PTEs for them
567*4882a593Smuzhiyun 		 * are in the hash table - the aim is to try to avoid
568*4882a593Smuzhiyun 		 * getting DSI exceptions while copying the kernel image.
569*4882a593Smuzhiyun 		 */
570*4882a593Smuzhiyun 		for (ptr = ((unsigned long) &_stext) & PAGE_MASK;
571*4882a593Smuzhiyun 		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
572*4882a593Smuzhiyun 			x = *(volatile unsigned long *)ptr;
573*4882a593Smuzhiyun 	}
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	/* Ok, now we need to generate a flattened device-tree to pass
576*4882a593Smuzhiyun 	 * to the kernel
577*4882a593Smuzhiyun 	 */
578*4882a593Smuzhiyun 	bootx_printf("Preparing boot params...\n");
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	hdr = bootx_flatten_dt(space);
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
583*4882a593Smuzhiyun #ifdef SET_BOOT_BAT
584*4882a593Smuzhiyun 	bootx_printf("Preparing BAT...\n");
585*4882a593Smuzhiyun 	btext_prepare_BAT();
586*4882a593Smuzhiyun #else
587*4882a593Smuzhiyun 	btext_unmap();
588*4882a593Smuzhiyun #endif
589*4882a593Smuzhiyun #endif
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	reloc_got2(-offset);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	__start(hdr, KERNELBASE + offset, 0);
594*4882a593Smuzhiyun }
595