diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/arch/arm/lib/bootm.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/arch/arm/lib/bootm.c')
-rw-r--r-- | roms/u-boot/arch/arm/lib/bootm.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arm/lib/bootm.c b/roms/u-boot/arch/arm/lib/bootm.c new file mode 100644 index 000000000..f60ee3a7e --- /dev/null +++ b/roms/u-boot/arch/arm/lib/bootm.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + * - Added prep subcommand support + * - Reorganized source - modeled after powerpc version + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + */ + +#include <common.h> +#include <bootstage.h> +#include <command.h> +#include <cpu_func.h> +#include <dm.h> +#include <lmb.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/root.h> +#include <env.h> +#include <image.h> +#include <u-boot/zlib.h> +#include <asm/byteorder.h> +#include <linux/libfdt.h> +#include <mapmem.h> +#include <fdt_support.h> +#include <asm/bootm.h> +#include <asm/secure.h> +#include <linux/compiler.h> +#include <bootm.h> +#include <vxworks.h> +#include <asm/cache.h> + +#ifdef CONFIG_ARMV7_NONSEC +#include <asm/armv7.h> +#endif +#include <asm/setup.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct tag *params; + +static ulong get_sp(void) +{ + ulong ret; + + asm("mov %0, sp" : "=r"(ret) : ); + return ret; +} + +void arch_lmb_reserve(struct lmb *lmb) +{ + ulong sp, bank_end; + int bank; + + /* + * Booting a (Linux) kernel image + * + * Allocate space for command line and board info - the + * address should be as high as possible within the reach of + * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused + * memory, which means far enough below the current stack + * pointer. + */ + sp = get_sp(); + debug("## Current stack ends at 0x%08lx ", sp); + + /* adjust sp by 4K to be safe */ + sp -= 4096; + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + if (!gd->bd->bi_dram[bank].size || + sp < gd->bd->bi_dram[bank].start) + continue; + /* Watch out for RAM at end of address space! */ + bank_end = gd->bd->bi_dram[bank].start + + gd->bd->bi_dram[bank].size - 1; + if (sp > bank_end) + continue; + if (bank_end > gd->ram_top) + bank_end = gd->ram_top - 1; + + lmb_reserve(lmb, sp, bank_end - sp + 1); + break; + } +} + +__weak void board_quiesce_devices(void) +{ +} + +/** + * announce_and_cleanup() - Print message and prepare for kernel boot + * + * @fake: non-zero to do everything except actually boot + */ +static void announce_and_cleanup(int fake) +{ + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); +#ifdef CONFIG_BOOTSTAGE_FDT + bootstage_fdt_add_report(); +#endif +#ifdef CONFIG_BOOTSTAGE_REPORT + bootstage_report(); +#endif + +#ifdef CONFIG_USB_DEVICE + udc_disconnect(); +#endif + + board_quiesce_devices(); + + printf("\nStarting kernel ...%s\n\n", fake ? + "(fake run for tracing)" : ""); + /* + * Call remove function of all devices with a removal flag set. + * This may be useful for last-stage operations, like cancelling + * of DMA operation or releasing device internal buffers. + */ + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL | DM_REMOVE_NON_VITAL); + + /* Remove all active vital devices next */ + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); + + cleanup_before_linux(); +} + +static void setup_start_tag (struct bd_info *bd) +{ + params = (struct tag *)bd->bi_boot_params; + + params->hdr.tag = ATAG_CORE; + params->hdr.size = tag_size (tag_core); + + params->u.core.flags = 0; + params->u.core.pagesize = 0; + params->u.core.rootdev = 0; + + params = tag_next (params); +} + +static void setup_memory_tags(struct bd_info *bd) +{ + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + params->hdr.tag = ATAG_MEM; + params->hdr.size = tag_size (tag_mem32); + + params->u.mem.start = bd->bi_dram[i].start; + params->u.mem.size = bd->bi_dram[i].size; + + params = tag_next (params); + } +} + +static void setup_commandline_tag(struct bd_info *bd, char *commandline) +{ + char *p; + + if (!commandline) + return; + + /* eat leading white space */ + for (p = commandline; *p == ' '; p++); + + /* skip non-existent command lines so the kernel will still + * use its default command line. + */ + if (*p == '\0') + return; + + params->hdr.tag = ATAG_CMDLINE; + params->hdr.size = + (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; + + strcpy (params->u.cmdline.cmdline, p); + + params = tag_next (params); +} + +static void setup_initrd_tag(struct bd_info *bd, ulong initrd_start, + ulong initrd_end) +{ + /* an ATAG_INITRD node tells the kernel where the compressed + * ramdisk can be found. ATAG_RDIMG is a better name, actually. + */ + params->hdr.tag = ATAG_INITRD2; + params->hdr.size = tag_size (tag_initrd); + + params->u.initrd.start = initrd_start; + params->u.initrd.size = initrd_end - initrd_start; + + params = tag_next (params); +} + +static void setup_serial_tag(struct tag **tmp) +{ + struct tag *params = *tmp; + struct tag_serialnr serialnr; + + get_board_serial(&serialnr); + params->hdr.tag = ATAG_SERIAL; + params->hdr.size = tag_size (tag_serialnr); + params->u.serialnr.low = serialnr.low; + params->u.serialnr.high= serialnr.high; + params = tag_next (params); + *tmp = params; +} + +static void setup_revision_tag(struct tag **in_params) +{ + u32 rev = 0; + + rev = get_board_rev(); + params->hdr.tag = ATAG_REVISION; + params->hdr.size = tag_size (tag_revision); + params->u.revision.rev = rev; + params = tag_next (params); +} + +static void setup_end_tag(struct bd_info *bd) +{ + params->hdr.tag = ATAG_NONE; + params->hdr.size = 0; +} + +__weak void setup_board_tags(struct tag **in_params) {} + +#ifdef CONFIG_ARM64 +static void do_nonsec_virt_switch(void) +{ + smp_kick_all_cpus(); + dcache_disable(); /* flush cache before swtiching to EL2 */ +} +#endif + +__weak void board_prep_linux(bootm_headers_t *images) { } + +/* Subcommand: PREP */ +static void boot_prep_linux(bootm_headers_t *images) +{ + char *commandline = env_get("bootargs"); + + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { +#ifdef CONFIG_OF_LIBFDT + debug("using: FDT\n"); + if (image_setup_linux(images)) { + panic("FDT creation failed!"); + } +#endif + } else if (BOOTM_ENABLE_TAGS) { + debug("using: ATAGS\n"); + setup_start_tag(gd->bd); + if (BOOTM_ENABLE_SERIAL_TAG) + setup_serial_tag(¶ms); + if (BOOTM_ENABLE_CMDLINE_TAG) + setup_commandline_tag(gd->bd, commandline); + if (BOOTM_ENABLE_REVISION_TAG) + setup_revision_tag(¶ms); + if (BOOTM_ENABLE_MEMORY_TAGS) + setup_memory_tags(gd->bd); + if (BOOTM_ENABLE_INITRD_TAG) { + /* + * In boot_ramdisk_high(), it may relocate ramdisk to + * a specified location. And set images->initrd_start & + * images->initrd_end to relocated ramdisk's start/end + * addresses. So use them instead of images->rd_start & + * images->rd_end when possible. + */ + if (images->initrd_start && images->initrd_end) { + setup_initrd_tag(gd->bd, images->initrd_start, + images->initrd_end); + } else if (images->rd_start && images->rd_end) { + setup_initrd_tag(gd->bd, images->rd_start, + images->rd_end); + } + } + setup_board_tags(¶ms); + setup_end_tag(gd->bd); + } else { + panic("FDT and ATAGS support not compiled in\n"); + } + + board_prep_linux(images); +} + +__weak bool armv7_boot_nonsec_default(void) +{ +#ifdef CONFIG_ARMV7_BOOT_SEC_DEFAULT + return false; +#else + return true; +#endif +} + +#ifdef CONFIG_ARMV7_NONSEC +bool armv7_boot_nonsec(void) +{ + char *s = env_get("bootm_boot_mode"); + bool nonsec = armv7_boot_nonsec_default(); + + if (s && !strcmp(s, "sec")) + nonsec = false; + + if (s && !strcmp(s, "nonsec")) + nonsec = true; + + return nonsec; +} +#endif + +#ifdef CONFIG_ARM64 +__weak void update_os_arch_secondary_cores(uint8_t os_arch) +{ +} + +#ifdef CONFIG_ARMV8_SWITCH_TO_EL1 +static void switch_to_el1(void) +{ + if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && + (images.os.arch == IH_ARCH_ARM)) + armv8_switch_to_el1(0, (u64)gd->bd->bi_arch_number, + (u64)images.ft_addr, 0, + (u64)images.ep, + ES_TO_AARCH32); + else + armv8_switch_to_el1((u64)images.ft_addr, 0, 0, 0, + images.ep, + ES_TO_AARCH64); +} +#endif +#endif + +/* Subcommand: GO */ +static void boot_jump_linux(bootm_headers_t *images, int flag) +{ +#ifdef CONFIG_ARM64 + void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, + void *res2); + int fake = (flag & BOOTM_STATE_OS_FAKE_GO); + + kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1, + void *res2))images->ep; + + debug("## Transferring control to Linux (at address %lx)...\n", + (ulong) kernel_entry); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + announce_and_cleanup(fake); + + if (!fake) { +#ifdef CONFIG_ARMV8_PSCI + armv8_setup_psci(); +#endif + do_nonsec_virt_switch(); + + update_os_arch_secondary_cores(images->os.arch); + +#ifdef CONFIG_ARMV8_SWITCH_TO_EL1 + armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, + (u64)switch_to_el1, ES_TO_AARCH64); +#else + if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && + (images->os.arch == IH_ARCH_ARM)) + armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, + (u64)images->ft_addr, 0, + (u64)images->ep, + ES_TO_AARCH32); + else + armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, + images->ep, + ES_TO_AARCH64); +#endif + } +#else + unsigned long machid = gd->bd->bi_arch_number; + char *s; + void (*kernel_entry)(int zero, int arch, uint params); + unsigned long r2; + int fake = (flag & BOOTM_STATE_OS_FAKE_GO); + + kernel_entry = (void (*)(int, int, uint))images->ep; +#ifdef CONFIG_CPU_V7M + ulong addr = (ulong)kernel_entry | 1; + kernel_entry = (void *)addr; +#endif + s = env_get("machid"); + if (s) { + if (strict_strtoul(s, 16, &machid) < 0) { + debug("strict_strtoul failed!\n"); + return; + } + printf("Using machid 0x%lx from environment\n", machid); + } + + debug("## Transferring control to Linux (at address %08lx)" \ + "...\n", (ulong) kernel_entry); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + announce_and_cleanup(fake); + + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) + r2 = (unsigned long)images->ft_addr; + else + r2 = gd->bd->bi_boot_params; + + if (!fake) { +#ifdef CONFIG_ARMV7_NONSEC + if (armv7_boot_nonsec()) { + armv7_init_nonsec(); + secure_ram_addr(_do_nonsec_entry)(kernel_entry, + 0, machid, r2); + } else +#endif + kernel_entry(0, machid, r2); + } +#endif +} + +/* Main Entry point for arm bootm implementation + * + * Modeled after the powerpc implementation + * DIFFERENCE: Instead of calling prep and go at the end + * they are called if subcommand is equal 0. + */ +int do_bootm_linux(int flag, int argc, char *const argv[], + bootm_headers_t *images) +{ + /* No need for those on ARM */ + if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) + return -1; + + if (flag & BOOTM_STATE_OS_PREP) { + boot_prep_linux(images); + return 0; + } + + if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { + boot_jump_linux(images, flag); + return 0; + } + + boot_prep_linux(images); + boot_jump_linux(images, flag); + return 0; +} + +#if defined(CONFIG_BOOTM_VXWORKS) +void boot_prep_vxworks(bootm_headers_t *images) +{ +#if defined(CONFIG_OF_LIBFDT) + int off; + + if (images->ft_addr) { + off = fdt_path_offset(images->ft_addr, "/memory"); + if (off > 0) { + if (arch_fixup_fdt(images->ft_addr)) + puts("## WARNING: fixup memory failed!\n"); + } + } +#endif + cleanup_before_linux(); +} +void boot_jump_vxworks(bootm_headers_t *images) +{ +#if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI) + armv8_setup_psci(); + smp_kick_all_cpus(); +#endif + + /* ARM VxWorks requires device tree physical address to be passed */ + ((void (*)(void *))images->ep)(images->ft_addr); +} +#endif |