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