1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2008 Semihalf 4 * 5 * (C) Copyright 2000-2006 6 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 7 */ 8 9 10#include <common.h> 11#include <bootm.h> 12#include <bootstage.h> 13#include <cpu_func.h> 14#include <env.h> 15#include <init.h> 16#include <lmb.h> 17#include <log.h> 18#include <watchdog.h> 19#include <command.h> 20#include <image.h> 21#include <malloc.h> 22#include <asm/global_data.h> 23#include <u-boot/zlib.h> 24#include <bzlib.h> 25#include <asm/byteorder.h> 26#include <asm/mp.h> 27#include <bootm.h> 28#include <vxworks.h> 29 30#if defined(CONFIG_OF_LIBFDT) 31#include <linux/libfdt.h> 32#include <fdt_support.h> 33#endif 34 35#ifdef CONFIG_SYS_INIT_RAM_LOCK 36#include <asm/cache.h> 37#endif 38 39DECLARE_GLOBAL_DATA_PTR; 40 41static ulong get_sp (void); 42extern void ft_fixup_num_cores(void *blob); 43static void set_clocks_in_mhz (struct bd_info *kbd); 44 45#ifndef CFG_SYS_LINUX_LOWMEM_MAX_SIZE 46#define CFG_SYS_LINUX_LOWMEM_MAX_SIZE (768*1024*1024) 47#endif 48 49static void boot_jump_linux(struct bootm_headers *images) 50{ 51 void (*kernel)(struct bd_info *, ulong r4, ulong r5, ulong r6, 52 ulong r7, ulong r8, ulong r9); 53#ifdef CONFIG_OF_LIBFDT 54 char *of_flat_tree = images->ft_addr; 55#endif 56 57 kernel = (void (*)(struct bd_info *, ulong, ulong, ulong, 58 ulong, ulong, ulong))images->ep; 59 debug("## Transferring control to Linux (at address %08lx) ...\n", 60 (ulong)kernel); 61 62 bootstage_mark(BOOTSTAGE_ID_RUN_OS); 63 64#ifdef CONFIG_BOOTSTAGE_FDT 65 bootstage_fdt_add_report(); 66#endif 67#ifdef CONFIG_BOOTSTAGE_REPORT 68 bootstage_report(); 69#endif 70 71#if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500) 72 unlock_ram_in_cache(); 73#endif 74 75#if defined(CONFIG_OF_LIBFDT) 76 if (of_flat_tree) { /* device tree; boot new style */ 77 /* 78 * Linux Kernel Parameters (passing device tree): 79 * r3: pointer to the fdt 80 * r4: 0 81 * r5: 0 82 * r6: epapr magic 83 * r7: size of IMA in bytes 84 * r8: 0 85 * r9: 0 86 */ 87 debug(" Booting using OF flat tree...\n"); 88 schedule(); 89 (*kernel) ((struct bd_info *)of_flat_tree, 0, 0, EPAPR_MAGIC, 90 env_get_bootm_mapsize(), 0, 0); 91 /* does not return */ 92 } else 93#endif 94 { 95 /* 96 * Linux Kernel Parameters (passing board info data): 97 * r3: ptr to board info data 98 * r4: initrd_start or 0 if no initrd 99 * r5: initrd_end - unused if r4 is 0 100 * r6: Start of command line string 101 * r7: End of command line string 102 * r8: 0 103 * r9: 0 104 */ 105 ulong cmd_start = images->cmdline_start; 106 ulong cmd_end = images->cmdline_end; 107 ulong initrd_start = images->initrd_start; 108 ulong initrd_end = images->initrd_end; 109 struct bd_info *kbd = images->kbd; 110 111 debug(" Booting using board info...\n"); 112 schedule(); 113 (*kernel) (kbd, initrd_start, initrd_end, 114 cmd_start, cmd_end, 0, 0); 115 /* does not return */ 116 } 117 return; 118} 119 120void arch_lmb_reserve(struct lmb *lmb) 121{ 122 phys_size_t bootm_size; 123 ulong size, bootmap_base; 124 125 bootmap_base = env_get_bootm_low(); 126 bootm_size = env_get_bootm_size(); 127 128#ifdef DEBUG 129 if (((u64)bootmap_base + bootm_size) > 130 (CFG_SYS_SDRAM_BASE + (u64)gd->ram_size)) 131 puts("WARNING: bootm_low + bootm_size exceed total memory\n"); 132 if ((bootmap_base + bootm_size) > get_effective_memsize()) 133 puts("WARNING: bootm_low + bootm_size exceed eff. memory\n"); 134#endif 135 136 size = min(bootm_size, get_effective_memsize()); 137 size = min(size, (ulong)CFG_SYS_LINUX_LOWMEM_MAX_SIZE); 138 139 if (size < bootm_size) { 140 ulong base = bootmap_base + size; 141 printf("WARNING: adjusting available memory from 0x%lx to 0x%llx\n", 142 size, (unsigned long long)bootm_size); 143 lmb_reserve(lmb, base, bootm_size - size); 144 } 145 146 arch_lmb_reserve_generic(lmb, get_sp(), gd->ram_top, 4096); 147 148#ifdef CONFIG_MP 149 cpu_mp_lmb_reserve(lmb); 150#endif 151 152 return; 153} 154 155static void boot_prep_linux(struct bootm_headers *images) 156{ 157#ifdef CONFIG_MP 158 /* 159 * if we are MP make sure to flush the device tree so any changes are 160 * made visibile to all other cores. In AMP boot scenarios the cores 161 * might not be HW cache coherent with each other. 162 */ 163 flush_cache((unsigned long)images->ft_addr, images->ft_len); 164#endif 165} 166 167static int boot_cmdline_linux(struct bootm_headers *images) 168{ 169 ulong of_size = images->ft_len; 170 struct lmb *lmb = &images->lmb; 171 ulong *cmd_start = &images->cmdline_start; 172 ulong *cmd_end = &images->cmdline_end; 173 174 int ret = 0; 175 176 if (!of_size) { 177 /* allocate space and init command line */ 178 ret = boot_get_cmdline (lmb, cmd_start, cmd_end); 179 if (ret) { 180 puts("ERROR with allocation of cmdline\n"); 181 return ret; 182 } 183 } 184 185 return ret; 186} 187 188static int boot_bd_t_linux(struct bootm_headers *images) 189{ 190 ulong of_size = images->ft_len; 191 struct lmb *lmb = &images->lmb; 192 struct bd_info **kbd = &images->kbd; 193 194 int ret = 0; 195 196 if (!of_size) { 197 /* allocate space for kernel copy of board info */ 198 ret = boot_get_kbd (lmb, kbd); 199 if (ret) { 200 puts("ERROR with allocation of kernel bd\n"); 201 return ret; 202 } 203 set_clocks_in_mhz(*kbd); 204 } 205 206 return ret; 207} 208 209static int boot_body_linux(struct bootm_headers *images) 210{ 211 int ret; 212 213 /* allocate space for kernel copy of board info */ 214 ret = boot_bd_t_linux(images); 215 if (ret) 216 return ret; 217 218 if (IS_ENABLED(CONFIG_LMB)) { 219 ret = image_setup_linux(images); 220 if (ret) 221 return ret; 222 } 223 224 return 0; 225} 226 227int do_bootm_linux(int flag, struct bootm_info *bmi) 228{ 229 struct bootm_headers *images = bmi->images; 230 int ret; 231 232 if (flag & BOOTM_STATE_OS_CMDLINE) { 233 boot_cmdline_linux(images); 234 return 0; 235 } 236 237 if (flag & BOOTM_STATE_OS_BD_T) { 238 boot_bd_t_linux(images); 239 return 0; 240 } 241 242 if (flag & BOOTM_STATE_OS_PREP) { 243 boot_prep_linux(images); 244 return 0; 245 } 246 247 boot_prep_linux(images); 248 ret = boot_body_linux(images); 249 if (ret) 250 return ret; 251 boot_jump_linux(images); 252 253 return 0; 254} 255 256static ulong get_sp (void) 257{ 258 ulong sp; 259 260 asm( "mr %0,1": "=r"(sp) : ); 261 return sp; 262} 263 264static void set_clocks_in_mhz (struct bd_info *kbd) 265{ 266 char *s; 267 268 s = env_get("clocks_in_mhz"); 269 if (s) { 270 /* convert all clock information to MHz */ 271 kbd->bi_intfreq /= 1000000L; 272 kbd->bi_busfreq /= 1000000L; 273 } 274} 275 276#if defined(CONFIG_BOOTM_VXWORKS) 277void boot_prep_vxworks(struct bootm_headers *images) 278{ 279#if defined(CONFIG_OF_LIBFDT) 280 int off; 281 u64 base, size; 282 283 if (!images->ft_addr) 284 return; 285 286 base = (u64)gd->ram_base; 287 size = (u64)gd->ram_size; 288 289 off = fdt_path_offset(images->ft_addr, "/memory"); 290 if (off < 0) 291 fdt_fixup_memory(images->ft_addr, base, size); 292 293#if defined(CONFIG_MP) 294#if defined(CONFIG_MPC85xx) 295 ft_fixup_cpu(images->ft_addr, base + size); 296 ft_fixup_num_cores(images->ft_addr); 297#elif defined(CONFIG_MPC86xx) 298 off = fdt_add_mem_rsv(images->ft_addr, 299 determine_mp_bootpg(NULL), (u64)4096); 300 if (off < 0) 301 printf("## WARNING %s: %s\n", __func__, fdt_strerror(off)); 302 ft_fixup_num_cores(images->ft_addr); 303#endif 304 flush_cache((unsigned long)images->ft_addr, images->ft_len); 305#endif 306#endif 307} 308 309void boot_jump_vxworks(struct bootm_headers *images) 310{ 311 /* PowerPC VxWorks boot interface conforms to the ePAPR standard 312 * general purpuse registers: 313 * 314 * r3: Effective address of the device tree image 315 * r4: 0 316 * r5: 0 317 * r6: ePAPR magic value 318 * r7: shall be the size of the boot IMA in bytes 319 * r8: 0 320 * r9: 0 321 * TCR: WRC = 0, no watchdog timer reset will occur 322 */ 323 schedule(); 324 325 ((void (*)(void *, ulong, ulong, ulong, 326 ulong, ulong, ulong))images->ep)(images->ft_addr, 327 0, 0, EPAPR_MAGIC, env_get_bootm_mapsize(), 0, 0); 328} 329#endif 330