1 /* 2 * (C) Copyright 2008 Semihalf 3 * 4 * (C) Copyright 2000-2006 5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 6 * 7 * See file CREDITS for list of people who contributed to this 8 * project. 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation; either version 2 of 13 * the License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 23 * MA 02111-1307 USA 24 */ 25 26 27 #include <common.h> 28 #include <watchdog.h> 29 #include <command.h> 30 #include <image.h> 31 #include <malloc.h> 32 #include <u-boot/zlib.h> 33 #include <bzlib.h> 34 #include <environment.h> 35 #include <asm/byteorder.h> 36 37 #if defined(CONFIG_OF_LIBFDT) 38 #include <fdt.h> 39 #include <libfdt.h> 40 #include <fdt_support.h> 41 42 #endif 43 44 #ifdef CONFIG_SYS_INIT_RAM_LOCK 45 #include <asm/cache.h> 46 #endif 47 48 DECLARE_GLOBAL_DATA_PTR; 49 50 extern ulong get_effective_memsize(void); 51 static ulong get_sp (void); 52 static void set_clocks_in_mhz (bd_t *kbd); 53 54 #ifndef CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE 55 #define CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE (768*1024*1024) 56 #endif 57 58 static void boot_jump_linux(bootm_headers_t *images) 59 { 60 void (*kernel)(bd_t *, ulong r4, ulong r5, ulong r6, 61 ulong r7, ulong r8, ulong r9); 62 #ifdef CONFIG_OF_LIBFDT 63 char *of_flat_tree = images->ft_addr; 64 #endif 65 66 kernel = (void (*)(bd_t *, ulong, ulong, ulong, 67 ulong, ulong, ulong))images->ep; 68 debug ("## Transferring control to Linux (at address %08lx) ...\n", 69 (ulong)kernel); 70 71 show_boot_progress (15); 72 73 #if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500) 74 unlock_ram_in_cache(); 75 #endif 76 77 #if defined(CONFIG_OF_LIBFDT) 78 if (of_flat_tree) { /* device tree; boot new style */ 79 /* 80 * Linux Kernel Parameters (passing device tree): 81 * r3: pointer to the fdt 82 * r4: 0 83 * r5: 0 84 * r6: epapr magic 85 * r7: size of IMA in bytes 86 * r8: 0 87 * r9: 0 88 */ 89 #if defined(CONFIG_85xx) || defined(CONFIG_440) 90 #define EPAPR_MAGIC (0x45504150) 91 #else 92 #define EPAPR_MAGIC (0x65504150) 93 #endif 94 95 debug (" Booting using OF flat tree...\n"); 96 WATCHDOG_RESET (); 97 (*kernel) ((bd_t *)of_flat_tree, 0, 0, EPAPR_MAGIC, 98 CONFIG_SYS_BOOTMAPSZ, 0, 0); 99 /* does not return */ 100 } else 101 #endif 102 { 103 /* 104 * Linux Kernel Parameters (passing board info data): 105 * r3: ptr to board info data 106 * r4: initrd_start or 0 if no initrd 107 * r5: initrd_end - unused if r4 is 0 108 * r6: Start of command line string 109 * r7: End of command line string 110 * r8: 0 111 * r9: 0 112 */ 113 ulong cmd_start = images->cmdline_start; 114 ulong cmd_end = images->cmdline_end; 115 ulong initrd_start = images->initrd_start; 116 ulong initrd_end = images->initrd_end; 117 bd_t *kbd = images->kbd; 118 119 debug (" Booting using board info...\n"); 120 WATCHDOG_RESET (); 121 (*kernel) (kbd, initrd_start, initrd_end, 122 cmd_start, cmd_end, 0, 0); 123 /* does not return */ 124 } 125 return ; 126 } 127 128 void arch_lmb_reserve(struct lmb *lmb) 129 { 130 phys_size_t bootm_size; 131 ulong size, sp, bootmap_base; 132 133 bootmap_base = getenv_bootm_low(); 134 bootm_size = getenv_bootm_size(); 135 136 #ifdef DEBUG 137 if (((u64)bootmap_base + bootm_size) > 138 (CONFIG_SYS_SDRAM_BASE + (u64)gd->ram_size)) 139 puts("WARNING: bootm_low + bootm_size exceed total memory\n"); 140 if ((bootmap_base + bootm_size) > get_effective_memsize()) 141 puts("WARNING: bootm_low + bootm_size exceed eff. memory\n"); 142 #endif 143 144 size = min(bootm_size, get_effective_memsize()); 145 size = min(size, CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE); 146 147 if (size < bootm_size) { 148 ulong base = bootmap_base + size; 149 printf("WARNING: adjusting available memory to %lx\n", size); 150 lmb_reserve(lmb, base, bootm_size - size); 151 } 152 153 /* 154 * Booting a (Linux) kernel image 155 * 156 * Allocate space for command line and board info - the 157 * address should be as high as possible within the reach of 158 * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused 159 * memory, which means far enough below the current stack 160 * pointer. 161 */ 162 sp = get_sp(); 163 debug ("## Current stack ends at 0x%08lx\n", sp); 164 165 /* adjust sp by 4K to be safe */ 166 sp -= 4096; 167 lmb_reserve(lmb, sp, (CONFIG_SYS_SDRAM_BASE + get_effective_memsize() - sp)); 168 169 return ; 170 } 171 172 static void boot_prep_linux(void) 173 { 174 #ifdef CONFIG_MP 175 /* if we are MP make sure to flush the dcache() to any changes are made 176 * visibile to all other cores */ 177 flush_dcache(); 178 #endif 179 return ; 180 } 181 182 static int boot_cmdline_linux(bootm_headers_t *images) 183 { 184 ulong bootmap_base = getenv_bootm_low(); 185 ulong of_size = images->ft_len; 186 struct lmb *lmb = &images->lmb; 187 ulong *cmd_start = &images->cmdline_start; 188 ulong *cmd_end = &images->cmdline_end; 189 190 int ret = 0; 191 192 if (!of_size) { 193 /* allocate space and init command line */ 194 ret = boot_get_cmdline (lmb, cmd_start, cmd_end, bootmap_base); 195 if (ret) { 196 puts("ERROR with allocation of cmdline\n"); 197 return ret; 198 } 199 } 200 201 return ret; 202 } 203 204 static int boot_bd_t_linux(bootm_headers_t *images) 205 { 206 ulong bootmap_base = getenv_bootm_low(); 207 ulong of_size = images->ft_len; 208 struct lmb *lmb = &images->lmb; 209 bd_t **kbd = &images->kbd; 210 211 int ret = 0; 212 213 if (!of_size) { 214 /* allocate space for kernel copy of board info */ 215 ret = boot_get_kbd (lmb, kbd, bootmap_base); 216 if (ret) { 217 puts("ERROR with allocation of kernel bd\n"); 218 return ret; 219 } 220 set_clocks_in_mhz(*kbd); 221 } 222 223 return ret; 224 } 225 226 static int boot_body_linux(bootm_headers_t *images) 227 { 228 ulong rd_len; 229 struct lmb *lmb = &images->lmb; 230 ulong *initrd_start = &images->initrd_start; 231 ulong *initrd_end = &images->initrd_end; 232 #if defined(CONFIG_OF_LIBFDT) 233 ulong bootmap_base = getenv_bootm_low(); 234 ulong of_size = images->ft_len; 235 char **of_flat_tree = &images->ft_addr; 236 #endif 237 238 int ret; 239 240 /* allocate space and init command line */ 241 ret = boot_cmdline_linux(images); 242 if (ret) 243 return ret; 244 245 /* allocate space for kernel copy of board info */ 246 ret = boot_bd_t_linux(images); 247 if (ret) 248 return ret; 249 250 rd_len = images->rd_end - images->rd_start; 251 ret = boot_ramdisk_high (lmb, images->rd_start, rd_len, initrd_start, initrd_end); 252 if (ret) 253 return ret; 254 255 #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_SYS_BOOTMAPSZ) 256 ret = boot_relocate_fdt(lmb, bootmap_base, of_flat_tree, &of_size); 257 if (ret) 258 return ret; 259 260 /* 261 * Add the chosen node if it doesn't exist, add the env and bd_t 262 * if the user wants it (the logic is in the subroutines). 263 */ 264 if (of_size) { 265 if (fdt_chosen(*of_flat_tree, 1) < 0) { 266 puts ("ERROR: "); 267 puts ("/chosen node create failed"); 268 puts (" - must RESET the board to recover.\n"); 269 return -1; 270 } 271 #ifdef CONFIG_OF_BOARD_SETUP 272 /* Call the board-specific fixup routine */ 273 ft_board_setup(*of_flat_tree, gd->bd); 274 #endif 275 276 /* Delete the old LMB reservation */ 277 lmb_free(lmb, (phys_addr_t)(u32)*of_flat_tree, 278 (phys_size_t)fdt_totalsize(*of_flat_tree)); 279 280 ret = fdt_resize(*of_flat_tree); 281 if (ret < 0) 282 return ret; 283 of_size = ret; 284 285 if (*initrd_start && *initrd_end) 286 of_size += FDT_RAMDISK_OVERHEAD; 287 /* Create a new LMB reservation */ 288 lmb_reserve(lmb, (ulong)*of_flat_tree, of_size); 289 290 /* fixup the initrd now that we know where it should be */ 291 if (*initrd_start && *initrd_end) 292 fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); 293 } 294 #endif /* CONFIG_OF_LIBFDT && CONFIG_SYS_BOOTMAPSZ */ 295 return 0; 296 } 297 298 __attribute__((noinline)) 299 int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images) 300 { 301 int ret; 302 303 if (flag & BOOTM_STATE_OS_CMDLINE) { 304 boot_cmdline_linux(images); 305 return 0; 306 } 307 308 if (flag & BOOTM_STATE_OS_BD_T) { 309 boot_bd_t_linux(images); 310 return 0; 311 } 312 313 if (flag & BOOTM_STATE_OS_PREP) { 314 boot_prep_linux(); 315 return 0; 316 } 317 318 if (flag & BOOTM_STATE_OS_GO) { 319 boot_jump_linux(images); 320 return 0; 321 } 322 323 boot_prep_linux(); 324 ret = boot_body_linux(images); 325 if (ret) 326 return ret; 327 boot_jump_linux(images); 328 329 return 0; 330 } 331 332 static ulong get_sp (void) 333 { 334 ulong sp; 335 336 asm( "mr %0,1": "=r"(sp) : ); 337 return sp; 338 } 339 340 static void set_clocks_in_mhz (bd_t *kbd) 341 { 342 char *s; 343 344 if ((s = getenv ("clocks_in_mhz")) != NULL) { 345 /* convert all clock information to MHz */ 346 kbd->bi_intfreq /= 1000000L; 347 kbd->bi_busfreq /= 1000000L; 348 #if defined(CONFIG_MPC8220) 349 kbd->bi_inpfreq /= 1000000L; 350 kbd->bi_pcifreq /= 1000000L; 351 kbd->bi_pevfreq /= 1000000L; 352 kbd->bi_flbfreq /= 1000000L; 353 kbd->bi_vcofreq /= 1000000L; 354 #endif 355 #if defined(CONFIG_CPM2) 356 kbd->bi_cpmfreq /= 1000000L; 357 kbd->bi_brgfreq /= 1000000L; 358 kbd->bi_sccfreq /= 1000000L; 359 kbd->bi_vco /= 1000000L; 360 #endif 361 #if defined(CONFIG_MPC5xxx) 362 kbd->bi_ipbfreq /= 1000000L; 363 kbd->bi_pcifreq /= 1000000L; 364 #endif /* CONFIG_MPC5xxx */ 365 } 366 } 367