uboot/arch/powerpc/lib/bootm.c
<<
>>
Prefs
   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#include <asm/mp.h>
  37
  38#if defined(CONFIG_OF_LIBFDT)
  39#include <fdt.h>
  40#include <libfdt.h>
  41#include <fdt_support.h>
  42
  43#endif
  44
  45#ifdef CONFIG_SYS_INIT_RAM_LOCK
  46#include <asm/cache.h>
  47#endif
  48
  49DECLARE_GLOBAL_DATA_PTR;
  50
  51extern ulong get_effective_memsize(void);
  52static ulong get_sp (void);
  53static void set_clocks_in_mhz (bd_t *kbd);
  54
  55#ifndef CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE
  56#define CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE        (768*1024*1024)
  57#endif
  58
  59static void boot_jump_linux(bootm_headers_t *images)
  60{
  61        void    (*kernel)(bd_t *, ulong r4, ulong r5, ulong r6,
  62                          ulong r7, ulong r8, ulong r9);
  63#ifdef CONFIG_OF_LIBFDT
  64        char *of_flat_tree = images->ft_addr;
  65#endif
  66
  67        kernel = (void (*)(bd_t *, ulong, ulong, ulong,
  68                           ulong, ulong, ulong))images->ep;
  69        debug ("## Transferring control to Linux (at address %08lx) ...\n",
  70                (ulong)kernel);
  71
  72        show_boot_progress (15);
  73
  74#if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500)
  75        unlock_ram_in_cache();
  76#endif
  77
  78#if defined(CONFIG_OF_LIBFDT)
  79        if (of_flat_tree) {     /* device tree; boot new style */
  80                /*
  81                 * Linux Kernel Parameters (passing device tree):
  82                 *   r3: pointer to the fdt
  83                 *   r4: 0
  84                 *   r5: 0
  85                 *   r6: epapr magic
  86                 *   r7: size of IMA in bytes
  87                 *   r8: 0
  88                 *   r9: 0
  89                 */
  90#if defined(CONFIG_MPC85xx) || defined(CONFIG_440)
  91 #define EPAPR_MAGIC    (0x45504150)
  92#else
  93 #define EPAPR_MAGIC    (0x65504150)
  94#endif
  95
  96                debug ("   Booting using OF flat tree...\n");
  97                WATCHDOG_RESET ();
  98                (*kernel) ((bd_t *)of_flat_tree, 0, 0, EPAPR_MAGIC,
  99                           getenv_bootm_mapsize(), 0, 0);
 100                /* does not return */
 101        } else
 102#endif
 103        {
 104                /*
 105                 * Linux Kernel Parameters (passing board info data):
 106                 *   r3: ptr to board info data
 107                 *   r4: initrd_start or 0 if no initrd
 108                 *   r5: initrd_end - unused if r4 is 0
 109                 *   r6: Start of command line string
 110                 *   r7: End   of command line string
 111                 *   r8: 0
 112                 *   r9: 0
 113                 */
 114                ulong cmd_start = images->cmdline_start;
 115                ulong cmd_end = images->cmdline_end;
 116                ulong initrd_start = images->initrd_start;
 117                ulong initrd_end = images->initrd_end;
 118                bd_t *kbd = images->kbd;
 119
 120                debug ("   Booting using board info...\n");
 121                WATCHDOG_RESET ();
 122                (*kernel) (kbd, initrd_start, initrd_end,
 123                           cmd_start, cmd_end, 0, 0);
 124                /* does not return */
 125        }
 126        return ;
 127}
 128
 129void arch_lmb_reserve(struct lmb *lmb)
 130{
 131        phys_size_t bootm_size;
 132        ulong size, sp, bootmap_base;
 133
 134        bootmap_base = getenv_bootm_low();
 135        bootm_size = getenv_bootm_size();
 136
 137#ifdef DEBUG
 138        if (((u64)bootmap_base + bootm_size) >
 139            (CONFIG_SYS_SDRAM_BASE + (u64)gd->ram_size))
 140                puts("WARNING: bootm_low + bootm_size exceed total memory\n");
 141        if ((bootmap_base + bootm_size) > get_effective_memsize())
 142                puts("WARNING: bootm_low + bootm_size exceed eff. memory\n");
 143#endif
 144
 145        size = min(bootm_size, get_effective_memsize());
 146        size = min(size, CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE);
 147
 148        if (size < bootm_size) {
 149                ulong base = bootmap_base + size;
 150                printf("WARNING: adjusting available memory to %lx\n", size);
 151                lmb_reserve(lmb, base, bootm_size - size);
 152        }
 153
 154        /*
 155         * Booting a (Linux) kernel image
 156         *
 157         * Allocate space for command line and board info - the
 158         * address should be as high as possible within the reach of
 159         * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
 160         * memory, which means far enough below the current stack
 161         * pointer.
 162         */
 163        sp = get_sp();
 164        debug ("## Current stack ends at 0x%08lx\n", sp);
 165
 166        /* adjust sp by 4K to be safe */
 167        sp -= 4096;
 168        lmb_reserve(lmb, sp, (CONFIG_SYS_SDRAM_BASE + get_effective_memsize() - sp));
 169
 170#ifdef CONFIG_MP
 171        cpu_mp_lmb_reserve(lmb);
 172#endif
 173
 174        return ;
 175}
 176
 177static int boot_cmdline_linux(bootm_headers_t *images)
 178{
 179        ulong of_size = images->ft_len;
 180        struct lmb *lmb = &images->lmb;
 181        ulong *cmd_start = &images->cmdline_start;
 182        ulong *cmd_end = &images->cmdline_end;
 183
 184        int ret = 0;
 185
 186        if (!of_size) {
 187                /* allocate space and init command line */
 188                ret = boot_get_cmdline (lmb, cmd_start, cmd_end);
 189                if (ret) {
 190                        puts("ERROR with allocation of cmdline\n");
 191                        return ret;
 192                }
 193        }
 194
 195        return ret;
 196}
 197
 198static int boot_bd_t_linux(bootm_headers_t *images)
 199{
 200        ulong of_size = images->ft_len;
 201        struct lmb *lmb = &images->lmb;
 202        bd_t **kbd = &images->kbd;
 203
 204        int ret = 0;
 205
 206        if (!of_size) {
 207                /* allocate space for kernel copy of board info */
 208                ret = boot_get_kbd (lmb, kbd);
 209                if (ret) {
 210                        puts("ERROR with allocation of kernel bd\n");
 211                        return ret;
 212                }
 213                set_clocks_in_mhz(*kbd);
 214        }
 215
 216        return ret;
 217}
 218
 219/*
 220 * Verify the device tree.
 221 *
 222 * This function is called after all device tree fix-ups have been enacted,
 223 * so that the final device tree can be verified.  The definition of "verified"
 224 * is up to the specific implementation.  However, it generally means that the
 225 * addresses of some of the devices in the device tree are compared with the
 226 * actual addresses at which U-Boot has placed them.
 227 *
 228 * Returns 1 on success, 0 on failure.  If 0 is returned, U-boot will halt the
 229 * boot process.
 230 */
 231static int __ft_verify_fdt(void *fdt)
 232{
 233        return 1;
 234}
 235__attribute__((weak, alias("__ft_verify_fdt"))) int ft_verify_fdt(void *fdt);
 236
 237static int boot_body_linux(bootm_headers_t *images)
 238{
 239        ulong rd_len;
 240        struct lmb *lmb = &images->lmb;
 241        ulong *initrd_start = &images->initrd_start;
 242        ulong *initrd_end = &images->initrd_end;
 243#if defined(CONFIG_OF_LIBFDT)
 244        ulong of_size = images->ft_len;
 245        char **of_flat_tree = &images->ft_addr;
 246#endif
 247
 248        int ret;
 249
 250#if defined(CONFIG_OF_LIBFDT)
 251        boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);
 252#endif
 253
 254        /* allocate space and init command line */
 255        ret = boot_cmdline_linux(images);
 256        if (ret)
 257                return ret;
 258
 259        /* allocate space for kernel copy of board info */
 260        ret = boot_bd_t_linux(images);
 261        if (ret)
 262                return ret;
 263
 264        rd_len = images->rd_end - images->rd_start;
 265        ret = boot_ramdisk_high (lmb, images->rd_start, rd_len, initrd_start, initrd_end);
 266        if (ret)
 267                return ret;
 268
 269#if defined(CONFIG_OF_LIBFDT)
 270        ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);
 271        if (ret)
 272                return ret;
 273
 274        /*
 275         * Add the chosen node if it doesn't exist, add the env and bd_t
 276         * if the user wants it (the logic is in the subroutines).
 277         */
 278        if (of_size) {
 279                if (fdt_chosen(*of_flat_tree, 1) < 0) {
 280                        puts ("ERROR: ");
 281                        puts ("/chosen node create failed");
 282                        puts (" - must RESET the board to recover.\n");
 283                        return -1;
 284                }
 285#ifdef CONFIG_OF_BOARD_SETUP
 286                /* Call the board-specific fixup routine */
 287                ft_board_setup(*of_flat_tree, gd->bd);
 288#endif
 289
 290                /* Delete the old LMB reservation */
 291                lmb_free(lmb, (phys_addr_t)(u32)*of_flat_tree,
 292                                (phys_size_t)fdt_totalsize(*of_flat_tree));
 293
 294                ret = fdt_resize(*of_flat_tree);
 295                if (ret < 0)
 296                        return ret;
 297                of_size = ret;
 298
 299                if (*initrd_start && *initrd_end) {
 300                        of_size += FDT_RAMDISK_OVERHEAD;
 301                        fdt_set_totalsize(*of_flat_tree, of_size);
 302                }
 303                /* Create a new LMB reservation */
 304                lmb_reserve(lmb, (ulong)*of_flat_tree, of_size);
 305
 306                /* fixup the initrd now that we know where it should be */
 307                if (*initrd_start && *initrd_end)
 308                        fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1);
 309
 310                if (!ft_verify_fdt(*of_flat_tree))
 311                        return -1;
 312        }
 313#endif  /* CONFIG_OF_LIBFDT */
 314        return 0;
 315}
 316
 317__attribute__((noinline))
 318int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images)
 319{
 320        int     ret;
 321
 322        if (flag & BOOTM_STATE_OS_CMDLINE) {
 323                boot_cmdline_linux(images);
 324                return 0;
 325        }
 326
 327        if (flag & BOOTM_STATE_OS_BD_T) {
 328                boot_bd_t_linux(images);
 329                return 0;
 330        }
 331
 332        /*
 333         * We do nothing & report success to retain compatiablity with older
 334         * versions of u-boot in which this use to flush the dcache on MP
 335         * systems
 336         */
 337        if (flag & BOOTM_STATE_OS_PREP)
 338                return 0;
 339
 340        if (flag & BOOTM_STATE_OS_GO) {
 341                boot_jump_linux(images);
 342                return 0;
 343        }
 344
 345        ret = boot_body_linux(images);
 346        if (ret)
 347                return ret;
 348        boot_jump_linux(images);
 349
 350        return 0;
 351}
 352
 353static ulong get_sp (void)
 354{
 355        ulong sp;
 356
 357        asm( "mr %0,1": "=r"(sp) : );
 358        return sp;
 359}
 360
 361static void set_clocks_in_mhz (bd_t *kbd)
 362{
 363        char    *s;
 364
 365        if ((s = getenv ("clocks_in_mhz")) != NULL) {
 366                /* convert all clock information to MHz */
 367                kbd->bi_intfreq /= 1000000L;
 368                kbd->bi_busfreq /= 1000000L;
 369#if defined(CONFIG_MPC8220)
 370                kbd->bi_inpfreq /= 1000000L;
 371                kbd->bi_pcifreq /= 1000000L;
 372                kbd->bi_pevfreq /= 1000000L;
 373                kbd->bi_flbfreq /= 1000000L;
 374                kbd->bi_vcofreq /= 1000000L;
 375#endif
 376#if defined(CONFIG_CPM2)
 377                kbd->bi_cpmfreq /= 1000000L;
 378                kbd->bi_brgfreq /= 1000000L;
 379                kbd->bi_sccfreq /= 1000000L;
 380                kbd->bi_vco     /= 1000000L;
 381#endif
 382#if defined(CONFIG_MPC5xxx)
 383                kbd->bi_ipbfreq /= 1000000L;
 384                kbd->bi_pcifreq /= 1000000L;
 385#endif /* CONFIG_MPC5xxx */
 386        }
 387}
 388