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