Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 1 | /* |
| 2 | * Early boot support code for BootX bootloader |
| 3 | * |
| 4 | * Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org) |
| 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 | |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 12 | #include <linux/kernel.h> |
| 13 | #include <linux/string.h> |
| 14 | #include <linux/init.h> |
Sam Ravnborg | 63104ee | 2006-07-03 23:30:54 +0200 | [diff] [blame] | 15 | #include <linux/utsrelease.h> |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 16 | #include <asm/sections.h> |
| 17 | #include <asm/prom.h> |
| 18 | #include <asm/page.h> |
| 19 | #include <asm/bootx.h> |
| 20 | #include <asm/bootinfo.h> |
| 21 | #include <asm/btext.h> |
| 22 | #include <asm/io.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; |
| 64 | btext_flushline(); |
| 65 | btext_drawstring("\r\n"); |
| 66 | btext_flushline(); |
| 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>"; |
| 78 | btext_drawstring(s); |
| 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 | { |
Benjamin Herrenschmidt | e822250 | 2006-03-28 23:15:54 +1100 | [diff] [blame] | 163 | u32 val; |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 164 | |
Benjamin Herrenschmidt | 0ebfff1 | 2006-07-03 21:36:01 +1000 | [diff] [blame] | 165 | bootx_dt_add_prop("linux,bootx", NULL, 0, mem_end); |
| 166 | |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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, |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 184 | unsigned long *mem_end, |
| 185 | int has_real_node) |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 186 | { |
Benjamin Herrenschmidt | ab13446 | 2006-07-03 17:19:48 +1000 | [diff] [blame] | 187 | boot_infos_t *bi = bootx_info; |
| 188 | u32 tmp; |
| 189 | |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 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 | |
Benjamin Herrenschmidt | ab13446 | 2006-07-03 17:19:48 +1000 | [diff] [blame] | 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); |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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"); |
Benjamin Herrenschmidt | 0ebfff1 | 2006-07-03 21:36:01 +1000 | [diff] [blame] | 238 | bootx_dt_add_string("linux,bootx", mem_end); |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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 | |
| 286 | dt_push_token(OF_DT_BEGIN_NODE, mem_end); |
| 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 | |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 332 | if (node == bootx_node_chosen) { |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 333 | bootx_add_chosen_props(base, mem_end); |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 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); |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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); |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 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 */ |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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; |
Benjamin Herrenschmidt | 6cdd2bd | 2006-08-23 11:45:12 +1000 | [diff] [blame] | 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 | } |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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 | |
| 485 | if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) |
| 486 | bi->logicalDisplayBase = bi->dispDeviceBase; |
| 487 | |
Benjamin Herrenschmidt | ab13446 | 2006-07-03 17:19:48 +1000 | [diff] [blame] | 488 | /* Fixup depth 16 -> 15 as that's what MacOS calls 16bpp */ |
| 489 | if (bi->dispDeviceDepth == 16) |
| 490 | bi->dispDeviceDepth = 15; |
| 491 | |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 492 | |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 493 | #ifdef CONFIG_BOOTX_TEXT |
Benjamin Herrenschmidt | ab13446 | 2006-07-03 17:19:48 +1000 | [diff] [blame] | 494 | ptr = (unsigned long)bi->logicalDisplayBase; |
| 495 | ptr += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes; |
| 496 | ptr += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8); |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 497 | btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0], |
| 498 | bi->dispDeviceRect[3] - bi->dispDeviceRect[1], |
| 499 | bi->dispDeviceDepth, bi->dispDeviceRowBytes, |
| 500 | (unsigned long)bi->logicalDisplayBase); |
| 501 | btext_clearscreen(); |
| 502 | btext_flushscreen(); |
| 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 |
Benjamin Herrenschmidt | 98c8247 | 2006-07-04 17:07:18 +1000 | [diff] [blame] | 530 | |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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"); |
Stephen Rothwell | af30837 | 2006-03-23 17:38:10 +1100 | [diff] [blame] | 544 | out_le32((unsigned __iomem *)0x80880008, 1); /* XXX */ |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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; |
Benjamin Herrenschmidt | 6cdd2bd | 2006-08-23 11:45:12 +1000 | [diff] [blame] | 553 | if (bi->ramDisk >= space) |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 554 | space = bi->ramDisk + bi->ramDiskSize; |
| 555 | } else |
| 556 | space = bi->totalParamsSize; |
| 557 | |
Benjamin Herrenschmidt | 6cdd2bd | 2006-08-23 11:45:12 +1000 | [diff] [blame] | 558 | bootx_printf("Total space used by parameters & ramdisk: 0x%x \n", space); |
Benjamin Herrenschmidt | d7f3945 | 2005-11-23 17:58:13 +1100 | [diff] [blame] | 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 | } |