18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Early boot support code for BootX bootloader 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Ben. Herrenschmidt (benh@kernel.crashing.org) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/string.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <generated/utsrelease.h> 128c2ecf20Sopenharmony_ci#include <asm/sections.h> 138c2ecf20Sopenharmony_ci#include <asm/prom.h> 148c2ecf20Sopenharmony_ci#include <asm/page.h> 158c2ecf20Sopenharmony_ci#include <asm/bootx.h> 168c2ecf20Sopenharmony_ci#include <asm/btext.h> 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci#include <asm/setup.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#undef DEBUG 218c2ecf20Sopenharmony_ci#define SET_BOOT_BAT 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#ifdef DEBUG 248c2ecf20Sopenharmony_ci#define DBG(fmt...) do { bootx_printf(fmt); } while(0) 258c2ecf20Sopenharmony_ci#else 268c2ecf20Sopenharmony_ci#define DBG(fmt...) do { } while(0) 278c2ecf20Sopenharmony_ci#endif 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciextern void __start(unsigned long r3, unsigned long r4, unsigned long r5); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic unsigned long __initdata bootx_dt_strbase; 328c2ecf20Sopenharmony_cistatic unsigned long __initdata bootx_dt_strend; 338c2ecf20Sopenharmony_cistatic unsigned long __initdata bootx_node_chosen; 348c2ecf20Sopenharmony_cistatic boot_infos_t * __initdata bootx_info; 358c2ecf20Sopenharmony_cistatic char __initdata bootx_disp_path[256]; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Is boot-info compatible ? */ 388c2ecf20Sopenharmony_ci#define BOOT_INFO_IS_COMPATIBLE(bi) \ 398c2ecf20Sopenharmony_ci ((bi)->compatible_version <= BOOT_INFO_VERSION) 408c2ecf20Sopenharmony_ci#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) 418c2ecf20Sopenharmony_ci#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 448c2ecf20Sopenharmony_cistatic void __init bootx_printf(const char *format, ...) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci const char *p, *q, *s; 478c2ecf20Sopenharmony_ci va_list args; 488c2ecf20Sopenharmony_ci unsigned long v; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci va_start(args, format); 518c2ecf20Sopenharmony_ci for (p = format; *p != 0; p = q) { 528c2ecf20Sopenharmony_ci for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) 538c2ecf20Sopenharmony_ci ; 548c2ecf20Sopenharmony_ci if (q > p) 558c2ecf20Sopenharmony_ci btext_drawtext(p, q - p); 568c2ecf20Sopenharmony_ci if (*q == 0) 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci if (*q == '\n') { 598c2ecf20Sopenharmony_ci ++q; 608c2ecf20Sopenharmony_ci btext_flushline(); 618c2ecf20Sopenharmony_ci btext_drawstring("\r\n"); 628c2ecf20Sopenharmony_ci btext_flushline(); 638c2ecf20Sopenharmony_ci continue; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci ++q; 668c2ecf20Sopenharmony_ci if (*q == 0) 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci switch (*q) { 698c2ecf20Sopenharmony_ci case 's': 708c2ecf20Sopenharmony_ci ++q; 718c2ecf20Sopenharmony_ci s = va_arg(args, const char *); 728c2ecf20Sopenharmony_ci if (s == NULL) 738c2ecf20Sopenharmony_ci s = "<NULL>"; 748c2ecf20Sopenharmony_ci btext_drawstring(s); 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci case 'x': 778c2ecf20Sopenharmony_ci ++q; 788c2ecf20Sopenharmony_ci v = va_arg(args, unsigned long); 798c2ecf20Sopenharmony_ci btext_drawhex(v); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci va_end(args); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci#else /* CONFIG_BOOTX_TEXT */ 868c2ecf20Sopenharmony_cistatic void __init bootx_printf(const char *format, ...) {} 878c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void * __init bootx_early_getprop(unsigned long base, 908c2ecf20Sopenharmony_ci unsigned long node, 918c2ecf20Sopenharmony_ci char *prop) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); 948c2ecf20Sopenharmony_ci u32 *ppp = &np->properties; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci while(*ppp) { 978c2ecf20Sopenharmony_ci struct bootx_dt_prop *pp = 988c2ecf20Sopenharmony_ci (struct bootx_dt_prop *)(base + *ppp); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (strcmp((char *)((unsigned long)pp->name + base), 1018c2ecf20Sopenharmony_ci prop) == 0) { 1028c2ecf20Sopenharmony_ci return (void *)((unsigned long)pp->value + base); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci ppp = &pp->next; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define dt_push_token(token, mem) \ 1108c2ecf20Sopenharmony_ci do { \ 1118c2ecf20Sopenharmony_ci *(mem) = ALIGN(*(mem),4); \ 1128c2ecf20Sopenharmony_ci *((u32 *)*(mem)) = token; \ 1138c2ecf20Sopenharmony_ci *(mem) += 4; \ 1148c2ecf20Sopenharmony_ci } while(0) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic unsigned long __init bootx_dt_find_string(char *str) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci char *s, *os; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci s = os = (char *)bootx_dt_strbase; 1218c2ecf20Sopenharmony_ci s += 4; 1228c2ecf20Sopenharmony_ci while (s < (char *)bootx_dt_strend) { 1238c2ecf20Sopenharmony_ci if (strcmp(s, str) == 0) 1248c2ecf20Sopenharmony_ci return s - os; 1258c2ecf20Sopenharmony_ci s += strlen(s) + 1; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void __init bootx_dt_add_prop(char *name, void *data, int size, 1318c2ecf20Sopenharmony_ci unsigned long *mem_end) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned long soff = bootx_dt_find_string(name); 1348c2ecf20Sopenharmony_ci if (data == NULL) 1358c2ecf20Sopenharmony_ci size = 0; 1368c2ecf20Sopenharmony_ci if (soff == 0) { 1378c2ecf20Sopenharmony_ci bootx_printf("WARNING: Can't find string index for <%s>\n", 1388c2ecf20Sopenharmony_ci name); 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci if (size > 0x20000) { 1428c2ecf20Sopenharmony_ci bootx_printf("WARNING: ignoring large property "); 1438c2ecf20Sopenharmony_ci bootx_printf("%s length 0x%x\n", name, size); 1448c2ecf20Sopenharmony_ci return; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci dt_push_token(OF_DT_PROP, mem_end); 1478c2ecf20Sopenharmony_ci dt_push_token(size, mem_end); 1488c2ecf20Sopenharmony_ci dt_push_token(soff, mem_end); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* push property content */ 1518c2ecf20Sopenharmony_ci if (size && data) { 1528c2ecf20Sopenharmony_ci memcpy((void *)*mem_end, data, size); 1538c2ecf20Sopenharmony_ci *mem_end = ALIGN(*mem_end + size, 4); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void __init bootx_add_chosen_props(unsigned long base, 1588c2ecf20Sopenharmony_ci unsigned long *mem_end) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u32 val; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx", NULL, 0, mem_end); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (bootx_info->kernelParamsOffset) { 1658c2ecf20Sopenharmony_ci char *args = (char *)((unsigned long)bootx_info) + 1668c2ecf20Sopenharmony_ci bootx_info->kernelParamsOffset; 1678c2ecf20Sopenharmony_ci bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci if (bootx_info->ramDisk) { 1708c2ecf20Sopenharmony_ci val = ((unsigned long)bootx_info) + bootx_info->ramDisk; 1718c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end); 1728c2ecf20Sopenharmony_ci val += bootx_info->ramDiskSize; 1738c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci if (strlen(bootx_disp_path)) 1768c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,stdout-path", bootx_disp_path, 1778c2ecf20Sopenharmony_ci strlen(bootx_disp_path) + 1, mem_end); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void __init bootx_add_display_props(unsigned long base, 1818c2ecf20Sopenharmony_ci unsigned long *mem_end, 1828c2ecf20Sopenharmony_ci int has_real_node) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci boot_infos_t *bi = bootx_info; 1858c2ecf20Sopenharmony_ci u32 tmp; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (has_real_node) { 1888c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end); 1898c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,opened", NULL, 0, mem_end); 1908c2ecf20Sopenharmony_ci } else 1918c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-noscreen", NULL, 0, mem_end); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci tmp = bi->dispDeviceDepth; 1948c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-depth", &tmp, 4, mem_end); 1958c2ecf20Sopenharmony_ci tmp = bi->dispDeviceRect[2] - bi->dispDeviceRect[0]; 1968c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-width", &tmp, 4, mem_end); 1978c2ecf20Sopenharmony_ci tmp = bi->dispDeviceRect[3] - bi->dispDeviceRect[1]; 1988c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-height", &tmp, 4, mem_end); 1998c2ecf20Sopenharmony_ci tmp = bi->dispDeviceRowBytes; 2008c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-linebytes", &tmp, 4, mem_end); 2018c2ecf20Sopenharmony_ci tmp = (u32)bi->dispDeviceBase; 2028c2ecf20Sopenharmony_ci if (tmp == 0) 2038c2ecf20Sopenharmony_ci tmp = (u32)bi->logicalDisplayBase; 2048c2ecf20Sopenharmony_ci tmp += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes; 2058c2ecf20Sopenharmony_ci tmp += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8); 2068c2ecf20Sopenharmony_ci bootx_dt_add_prop("linux,bootx-addr", &tmp, 4, mem_end); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void __init bootx_dt_add_string(char *s, unsigned long *mem_end) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci unsigned int l = strlen(s) + 1; 2128c2ecf20Sopenharmony_ci memcpy((void *)*mem_end, s, l); 2138c2ecf20Sopenharmony_ci bootx_dt_strend = *mem_end = *mem_end + l; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void __init bootx_scan_dt_build_strings(unsigned long base, 2178c2ecf20Sopenharmony_ci unsigned long node, 2188c2ecf20Sopenharmony_ci unsigned long *mem_end) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); 2218c2ecf20Sopenharmony_ci u32 *cpp, *ppp = &np->properties; 2228c2ecf20Sopenharmony_ci unsigned long soff; 2238c2ecf20Sopenharmony_ci char *namep; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Keep refs to known nodes */ 2268c2ecf20Sopenharmony_ci namep = np->full_name ? (char *)(base + np->full_name) : NULL; 2278c2ecf20Sopenharmony_ci if (namep == NULL) { 2288c2ecf20Sopenharmony_ci bootx_printf("Node without a full name !\n"); 2298c2ecf20Sopenharmony_ci namep = ""; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci DBG("* strings: %s\n", namep); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!strcmp(namep, "/chosen")) { 2348c2ecf20Sopenharmony_ci DBG(" detected /chosen ! adding properties names !\n"); 2358c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx", mem_end); 2368c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,stdout-path", mem_end); 2378c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,initrd-start", mem_end); 2388c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,initrd-end", mem_end); 2398c2ecf20Sopenharmony_ci bootx_dt_add_string("bootargs", mem_end); 2408c2ecf20Sopenharmony_ci bootx_node_chosen = node; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci if (node == bootx_info->dispDeviceRegEntryOffset) { 2438c2ecf20Sopenharmony_ci DBG(" detected display ! adding properties names !\n"); 2448c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,boot-display", mem_end); 2458c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,opened", mem_end); 2468c2ecf20Sopenharmony_ci strlcpy(bootx_disp_path, namep, sizeof(bootx_disp_path)); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* get and store all property names */ 2508c2ecf20Sopenharmony_ci while (*ppp) { 2518c2ecf20Sopenharmony_ci struct bootx_dt_prop *pp = 2528c2ecf20Sopenharmony_ci (struct bootx_dt_prop *)(base + *ppp); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci namep = pp->name ? (char *)(base + pp->name) : NULL; 2558c2ecf20Sopenharmony_ci if (namep == NULL || strcmp(namep, "name") == 0) 2568c2ecf20Sopenharmony_ci goto next; 2578c2ecf20Sopenharmony_ci /* get/create string entry */ 2588c2ecf20Sopenharmony_ci soff = bootx_dt_find_string(namep); 2598c2ecf20Sopenharmony_ci if (soff == 0) 2608c2ecf20Sopenharmony_ci bootx_dt_add_string(namep, mem_end); 2618c2ecf20Sopenharmony_ci next: 2628c2ecf20Sopenharmony_ci ppp = &pp->next; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* do all our children */ 2668c2ecf20Sopenharmony_ci cpp = &np->child; 2678c2ecf20Sopenharmony_ci while(*cpp) { 2688c2ecf20Sopenharmony_ci np = (struct bootx_dt_node *)(base + *cpp); 2698c2ecf20Sopenharmony_ci bootx_scan_dt_build_strings(base, *cpp, mem_end); 2708c2ecf20Sopenharmony_ci cpp = &np->sibling; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void __init bootx_scan_dt_build_struct(unsigned long base, 2758c2ecf20Sopenharmony_ci unsigned long node, 2768c2ecf20Sopenharmony_ci unsigned long *mem_end) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node); 2798c2ecf20Sopenharmony_ci u32 *cpp, *ppp = &np->properties; 2808c2ecf20Sopenharmony_ci char *namep, *p, *ep, *lp; 2818c2ecf20Sopenharmony_ci int l; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci dt_push_token(OF_DT_BEGIN_NODE, mem_end); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* get the node's full name */ 2868c2ecf20Sopenharmony_ci namep = np->full_name ? (char *)(base + np->full_name) : NULL; 2878c2ecf20Sopenharmony_ci if (namep == NULL) 2888c2ecf20Sopenharmony_ci namep = ""; 2898c2ecf20Sopenharmony_ci l = strlen(namep); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci DBG("* struct: %s\n", namep); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Fixup an Apple bug where they have bogus \0 chars in the 2948c2ecf20Sopenharmony_ci * middle of the path in some properties, and extract 2958c2ecf20Sopenharmony_ci * the unit name (everything after the last '/'). 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci memcpy((void *)*mem_end, namep, l + 1); 2988c2ecf20Sopenharmony_ci namep = (char *)*mem_end; 2998c2ecf20Sopenharmony_ci for (lp = p = namep, ep = namep + l; p < ep; p++) { 3008c2ecf20Sopenharmony_ci if (*p == '/') 3018c2ecf20Sopenharmony_ci lp = namep; 3028c2ecf20Sopenharmony_ci else if (*p != 0) 3038c2ecf20Sopenharmony_ci *lp++ = *p; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci *lp = 0; 3068c2ecf20Sopenharmony_ci *mem_end = ALIGN((unsigned long)lp + 1, 4); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* get and store all properties */ 3098c2ecf20Sopenharmony_ci while (*ppp) { 3108c2ecf20Sopenharmony_ci struct bootx_dt_prop *pp = 3118c2ecf20Sopenharmony_ci (struct bootx_dt_prop *)(base + *ppp); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci namep = pp->name ? (char *)(base + pp->name) : NULL; 3148c2ecf20Sopenharmony_ci /* Skip "name" */ 3158c2ecf20Sopenharmony_ci if (namep == NULL || !strcmp(namep, "name")) 3168c2ecf20Sopenharmony_ci goto next; 3178c2ecf20Sopenharmony_ci /* Skip "bootargs" in /chosen too as we replace it */ 3188c2ecf20Sopenharmony_ci if (node == bootx_node_chosen && !strcmp(namep, "bootargs")) 3198c2ecf20Sopenharmony_ci goto next; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* push property head */ 3228c2ecf20Sopenharmony_ci bootx_dt_add_prop(namep, 3238c2ecf20Sopenharmony_ci pp->value ? (void *)(base + pp->value): NULL, 3248c2ecf20Sopenharmony_ci pp->length, mem_end); 3258c2ecf20Sopenharmony_ci next: 3268c2ecf20Sopenharmony_ci ppp = &pp->next; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (node == bootx_node_chosen) { 3308c2ecf20Sopenharmony_ci bootx_add_chosen_props(base, mem_end); 3318c2ecf20Sopenharmony_ci if (bootx_info->dispDeviceRegEntryOffset == 0) 3328c2ecf20Sopenharmony_ci bootx_add_display_props(base, mem_end, 0); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci else if (node == bootx_info->dispDeviceRegEntryOffset) 3358c2ecf20Sopenharmony_ci bootx_add_display_props(base, mem_end, 1); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* do all our children */ 3388c2ecf20Sopenharmony_ci cpp = &np->child; 3398c2ecf20Sopenharmony_ci while(*cpp) { 3408c2ecf20Sopenharmony_ci np = (struct bootx_dt_node *)(base + *cpp); 3418c2ecf20Sopenharmony_ci bootx_scan_dt_build_struct(base, *cpp, mem_end); 3428c2ecf20Sopenharmony_ci cpp = &np->sibling; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dt_push_token(OF_DT_END_NODE, mem_end); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic unsigned long __init bootx_flatten_dt(unsigned long start) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci boot_infos_t *bi = bootx_info; 3518c2ecf20Sopenharmony_ci unsigned long mem_start, mem_end; 3528c2ecf20Sopenharmony_ci struct boot_param_header *hdr; 3538c2ecf20Sopenharmony_ci unsigned long base; 3548c2ecf20Sopenharmony_ci u64 *rsvmap; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Start using memory after the big blob passed by BootX, get 3578c2ecf20Sopenharmony_ci * some space for the header 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci mem_start = mem_end = ALIGN(((unsigned long)bi) + start, 4); 3608c2ecf20Sopenharmony_ci DBG("Boot params header at: %x\n", mem_start); 3618c2ecf20Sopenharmony_ci hdr = (struct boot_param_header *)mem_start; 3628c2ecf20Sopenharmony_ci mem_end += sizeof(struct boot_param_header); 3638c2ecf20Sopenharmony_ci rsvmap = (u64 *)(ALIGN(mem_end, 8)); 3648c2ecf20Sopenharmony_ci hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start; 3658c2ecf20Sopenharmony_ci mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Get base of tree */ 3688c2ecf20Sopenharmony_ci base = ((unsigned long)bi) + bi->deviceTreeOffset; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Build string array */ 3718c2ecf20Sopenharmony_ci DBG("Building string array at: %x\n", mem_end); 3728c2ecf20Sopenharmony_ci DBG("Device Tree Base=%x\n", base); 3738c2ecf20Sopenharmony_ci bootx_dt_strbase = mem_end; 3748c2ecf20Sopenharmony_ci mem_end += 4; 3758c2ecf20Sopenharmony_ci bootx_dt_strend = mem_end; 3768c2ecf20Sopenharmony_ci bootx_scan_dt_build_strings(base, 4, &mem_end); 3778c2ecf20Sopenharmony_ci /* Add some strings */ 3788c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-noscreen", &mem_end); 3798c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-depth", &mem_end); 3808c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-width", &mem_end); 3818c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-height", &mem_end); 3828c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-linebytes", &mem_end); 3838c2ecf20Sopenharmony_ci bootx_dt_add_string("linux,bootx-addr", &mem_end); 3848c2ecf20Sopenharmony_ci /* Wrap up strings */ 3858c2ecf20Sopenharmony_ci hdr->off_dt_strings = bootx_dt_strbase - mem_start; 3868c2ecf20Sopenharmony_ci hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Build structure */ 3898c2ecf20Sopenharmony_ci mem_end = ALIGN(mem_end, 16); 3908c2ecf20Sopenharmony_ci DBG("Building device tree structure at: %x\n", mem_end); 3918c2ecf20Sopenharmony_ci hdr->off_dt_struct = mem_end - mem_start; 3928c2ecf20Sopenharmony_ci bootx_scan_dt_build_struct(base, 4, &mem_end); 3938c2ecf20Sopenharmony_ci dt_push_token(OF_DT_END, &mem_end); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Finish header */ 3968c2ecf20Sopenharmony_ci hdr->boot_cpuid_phys = 0; 3978c2ecf20Sopenharmony_ci hdr->magic = OF_DT_HEADER; 3988c2ecf20Sopenharmony_ci hdr->totalsize = mem_end - mem_start; 3998c2ecf20Sopenharmony_ci hdr->version = OF_DT_VERSION; 4008c2ecf20Sopenharmony_ci /* Version 16 is not backward compatible */ 4018c2ecf20Sopenharmony_ci hdr->last_comp_version = 0x10; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Reserve the whole thing and copy the reserve map in, we 4048c2ecf20Sopenharmony_ci * also bump mem_reserve_cnt to cause further reservations to 4058c2ecf20Sopenharmony_ci * fail since it's too late. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci mem_end = ALIGN(mem_end, PAGE_SIZE); 4088c2ecf20Sopenharmony_ci DBG("End of boot params: %x\n", mem_end); 4098c2ecf20Sopenharmony_ci rsvmap[0] = mem_start; 4108c2ecf20Sopenharmony_ci rsvmap[1] = mem_end; 4118c2ecf20Sopenharmony_ci if (bootx_info->ramDisk) { 4128c2ecf20Sopenharmony_ci rsvmap[2] = ((unsigned long)bootx_info) + bootx_info->ramDisk; 4138c2ecf20Sopenharmony_ci rsvmap[3] = rsvmap[2] + bootx_info->ramDiskSize; 4148c2ecf20Sopenharmony_ci rsvmap[4] = 0; 4158c2ecf20Sopenharmony_ci rsvmap[5] = 0; 4168c2ecf20Sopenharmony_ci } else { 4178c2ecf20Sopenharmony_ci rsvmap[2] = 0; 4188c2ecf20Sopenharmony_ci rsvmap[3] = 0; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return (unsigned long)hdr; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 4268c2ecf20Sopenharmony_cistatic void __init btext_welcome(boot_infos_t *bi) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci unsigned long flags; 4298c2ecf20Sopenharmony_ci unsigned long pvr; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n"); 4328c2ecf20Sopenharmony_ci bootx_printf("\nlinked at : 0x%x", KERNELBASE); 4338c2ecf20Sopenharmony_ci bootx_printf("\nframe buffer at : 0x%x", bi->dispDeviceBase); 4348c2ecf20Sopenharmony_ci bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase); 4358c2ecf20Sopenharmony_ci bootx_printf(" (log)"); 4368c2ecf20Sopenharmony_ci bootx_printf("\nklimit : 0x%x",(unsigned long)klimit); 4378c2ecf20Sopenharmony_ci bootx_printf("\nboot_info at : 0x%x", bi); 4388c2ecf20Sopenharmony_ci __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); 4398c2ecf20Sopenharmony_ci bootx_printf("\nMSR : 0x%x", flags); 4408c2ecf20Sopenharmony_ci __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); 4418c2ecf20Sopenharmony_ci bootx_printf("\nPVR : 0x%x", pvr); 4428c2ecf20Sopenharmony_ci pvr >>= 16; 4438c2ecf20Sopenharmony_ci if (pvr > 1) { 4448c2ecf20Sopenharmony_ci __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); 4458c2ecf20Sopenharmony_ci bootx_printf("\nHID0 : 0x%x", flags); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci if (pvr == 8 || pvr == 12 || pvr == 0x800c) { 4488c2ecf20Sopenharmony_ci __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); 4498c2ecf20Sopenharmony_ci bootx_printf("\nICTC : 0x%x", flags); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci#ifdef DEBUG 4528c2ecf20Sopenharmony_ci bootx_printf("\n\n"); 4538c2ecf20Sopenharmony_ci bootx_printf("bi->deviceTreeOffset : 0x%x\n", 4548c2ecf20Sopenharmony_ci bi->deviceTreeOffset); 4558c2ecf20Sopenharmony_ci bootx_printf("bi->deviceTreeSize : 0x%x\n", 4568c2ecf20Sopenharmony_ci bi->deviceTreeSize); 4578c2ecf20Sopenharmony_ci#endif 4588c2ecf20Sopenharmony_ci bootx_printf("\n\n"); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_civoid __init bootx_init(unsigned long r3, unsigned long r4) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci boot_infos_t *bi = (boot_infos_t *) r4; 4658c2ecf20Sopenharmony_ci unsigned long hdr; 4668c2ecf20Sopenharmony_ci unsigned long space; 4678c2ecf20Sopenharmony_ci unsigned long ptr; 4688c2ecf20Sopenharmony_ci char *model; 4698c2ecf20Sopenharmony_ci unsigned long offset = reloc_offset(); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci reloc_got2(offset); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci bootx_info = bi; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* We haven't cleared any bss at this point, make sure 4768c2ecf20Sopenharmony_ci * what we need is initialized 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci bootx_dt_strbase = bootx_dt_strend = 0; 4798c2ecf20Sopenharmony_ci bootx_node_chosen = 0; 4808c2ecf20Sopenharmony_ci bootx_disp_path[0] = 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) 4838c2ecf20Sopenharmony_ci bi->logicalDisplayBase = bi->dispDeviceBase; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Fixup depth 16 -> 15 as that's what MacOS calls 16bpp */ 4868c2ecf20Sopenharmony_ci if (bi->dispDeviceDepth == 16) 4878c2ecf20Sopenharmony_ci bi->dispDeviceDepth = 15; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 4918c2ecf20Sopenharmony_ci ptr = (unsigned long)bi->logicalDisplayBase; 4928c2ecf20Sopenharmony_ci ptr += bi->dispDeviceRect[1] * bi->dispDeviceRowBytes; 4938c2ecf20Sopenharmony_ci ptr += bi->dispDeviceRect[0] * ((bi->dispDeviceDepth + 7) / 8); 4948c2ecf20Sopenharmony_ci btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0], 4958c2ecf20Sopenharmony_ci bi->dispDeviceRect[3] - bi->dispDeviceRect[1], 4968c2ecf20Sopenharmony_ci bi->dispDeviceDepth, bi->dispDeviceRowBytes, 4978c2ecf20Sopenharmony_ci (unsigned long)bi->logicalDisplayBase); 4988c2ecf20Sopenharmony_ci btext_clearscreen(); 4998c2ecf20Sopenharmony_ci btext_flushscreen(); 5008c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * Test if boot-info is compatible. Done only in config 5048c2ecf20Sopenharmony_ci * CONFIG_BOOTX_TEXT since there is nothing much we can do 5058c2ecf20Sopenharmony_ci * with an incompatible version, except display a message 5068c2ecf20Sopenharmony_ci * and eventually hang the processor... 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * I'll try to keep enough of boot-info compatible in the 5098c2ecf20Sopenharmony_ci * future to always allow display of this message; 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci if (!BOOT_INFO_IS_COMPATIBLE(bi)) { 5128c2ecf20Sopenharmony_ci bootx_printf(" !!! WARNING - Incompatible version" 5138c2ecf20Sopenharmony_ci " of BootX !!!\n\n\n"); 5148c2ecf20Sopenharmony_ci for (;;) 5158c2ecf20Sopenharmony_ci ; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci if (bi->architecture != BOOT_ARCH_PCI) { 5188c2ecf20Sopenharmony_ci bootx_printf(" !!! WARNING - Unsupported machine" 5198c2ecf20Sopenharmony_ci " architecture !\n"); 5208c2ecf20Sopenharmony_ci for (;;) 5218c2ecf20Sopenharmony_ci ; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 5258c2ecf20Sopenharmony_ci btext_welcome(bi); 5268c2ecf20Sopenharmony_ci#endif 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* New BootX enters kernel with MMU off, i/os are not allowed 5298c2ecf20Sopenharmony_ci * here. This hack will have been done by the boostrap anyway. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci if (bi->version < 4) { 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * XXX If this is an iMac, turn off the USB controller. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset, 5368c2ecf20Sopenharmony_ci 4, "model"); 5378c2ecf20Sopenharmony_ci if (model 5388c2ecf20Sopenharmony_ci && (strcmp(model, "iMac,1") == 0 5398c2ecf20Sopenharmony_ci || strcmp(model, "PowerMac1,1") == 0)) { 5408c2ecf20Sopenharmony_ci bootx_printf("iMac,1 detected, shutting down USB\n"); 5418c2ecf20Sopenharmony_ci out_le32((unsigned __iomem *)0x80880008, 1); /* XXX */ 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Get a pointer that points above the device tree, args, ramdisk, 5468c2ecf20Sopenharmony_ci * etc... to use for generating the flattened tree 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (bi->version < 5) { 5498c2ecf20Sopenharmony_ci space = bi->deviceTreeOffset + bi->deviceTreeSize; 5508c2ecf20Sopenharmony_ci if (bi->ramDisk >= space) 5518c2ecf20Sopenharmony_ci space = bi->ramDisk + bi->ramDiskSize; 5528c2ecf20Sopenharmony_ci } else 5538c2ecf20Sopenharmony_ci space = bi->totalParamsSize; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci bootx_printf("Total space used by parameters & ramdisk: 0x%x\n", space); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* New BootX will have flushed all TLBs and enters kernel with 5588c2ecf20Sopenharmony_ci * MMU switched OFF, so this should not be useful anymore. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci if (bi->version < 4) { 5618c2ecf20Sopenharmony_ci unsigned long x __maybe_unused; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci bootx_printf("Touching pages...\n"); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* 5668c2ecf20Sopenharmony_ci * Touch each page to make sure the PTEs for them 5678c2ecf20Sopenharmony_ci * are in the hash table - the aim is to try to avoid 5688c2ecf20Sopenharmony_ci * getting DSI exceptions while copying the kernel image. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci for (ptr = ((unsigned long) &_stext) & PAGE_MASK; 5718c2ecf20Sopenharmony_ci ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) 5728c2ecf20Sopenharmony_ci x = *(volatile unsigned long *)ptr; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Ok, now we need to generate a flattened device-tree to pass 5768c2ecf20Sopenharmony_ci * to the kernel 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci bootx_printf("Preparing boot params...\n"); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci hdr = bootx_flatten_dt(space); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 5838c2ecf20Sopenharmony_ci#ifdef SET_BOOT_BAT 5848c2ecf20Sopenharmony_ci bootx_printf("Preparing BAT...\n"); 5858c2ecf20Sopenharmony_ci btext_prepare_BAT(); 5868c2ecf20Sopenharmony_ci#else 5878c2ecf20Sopenharmony_ci btext_unmap(); 5888c2ecf20Sopenharmony_ci#endif 5898c2ecf20Sopenharmony_ci#endif 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci reloc_got2(-offset); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci __start(hdr, KERNELBASE + offset, 0); 5948c2ecf20Sopenharmony_ci} 595