| /* | |
| * (C) Copyright 2008 - 2013 Tensilica Inc. | |
| * (C) Copyright 2014 Cadence Design Systems Inc. | |
| * | |
| * SPDX-License-Identifier: GPL-2.0+ | |
| */ | |
| #include <common.h> | |
| #include <command.h> | |
| #include <u-boot/zlib.h> | |
| #include <asm/byteorder.h> | |
| #include <asm/addrspace.h> | |
| #include <asm/bootparam.h> | |
| #include <asm/cache.h> | |
| #include <image.h> | |
| DECLARE_GLOBAL_DATA_PTR; | |
| /* | |
| * Setup boot-parameters. | |
| */ | |
| static struct bp_tag *setup_first_tag(struct bp_tag *params) | |
| { | |
| params->id = BP_TAG_FIRST; | |
| params->size = sizeof(long); | |
| *(unsigned long *)¶ms->data = BP_VERSION; | |
| return bp_tag_next(params); | |
| } | |
| static struct bp_tag *setup_last_tag(struct bp_tag *params) | |
| { | |
| params->id = BP_TAG_LAST; | |
| params->size = 0; | |
| return bp_tag_next(params); | |
| } | |
| static struct bp_tag *setup_memory_tag(struct bp_tag *params) | |
| { | |
| struct bd_info *bd = gd->bd; | |
| struct meminfo *mem; | |
| params->id = BP_TAG_MEMORY; | |
| params->size = sizeof(struct meminfo); | |
| mem = (struct meminfo *)params->data; | |
| mem->type = MEMORY_TYPE_CONVENTIONAL; | |
| mem->start = bd->bi_memstart; | |
| mem->end = bd->bi_memstart + bd->bi_memsize; | |
| printf(" MEMORY: tag:0x%04x, type:0X%lx, start:0X%lx, end:0X%lx\n", | |
| BP_TAG_MEMORY, mem->type, mem->start, mem->end); | |
| return bp_tag_next(params); | |
| } | |
| static struct bp_tag *setup_commandline_tag(struct bp_tag *params, | |
| char *cmdline) | |
| { | |
| int len; | |
| if (!cmdline) | |
| return params; | |
| len = strlen(cmdline); | |
| params->id = BP_TAG_COMMAND_LINE; | |
| params->size = (len + 3) & -4; | |
| strcpy((char *)params->data, cmdline); | |
| printf(" COMMAND_LINE: tag:0x%04x, size:%u, data:'%s'\n", | |
| BP_TAG_COMMAND_LINE, params->size, cmdline); | |
| return bp_tag_next(params); | |
| } | |
| static struct bp_tag *setup_ramdisk_tag(struct bp_tag *params, | |
| unsigned long rd_start, | |
| unsigned long rd_end) | |
| { | |
| struct meminfo *mem; | |
| if (rd_start == rd_end) | |
| return params; | |
| /* Add a single banked memory */ | |
| params->id = BP_TAG_INITRD; | |
| params->size = sizeof(struct meminfo); | |
| mem = (struct meminfo *)params->data; | |
| mem->type = MEMORY_TYPE_CONVENTIONAL; | |
| mem->start = PHYSADDR(rd_start); | |
| mem->end = PHYSADDR(rd_end); | |
| printf(" INITRD: tag:0x%x, type:0X%04lx, start:0X%lx, end:0X%lx\n", | |
| BP_TAG_INITRD, mem->type, mem->start, mem->end); | |
| return bp_tag_next(params); | |
| } | |
| static struct bp_tag *setup_serial_tag(struct bp_tag *params) | |
| { | |
| params->id = BP_TAG_SERIAL_BAUDRATE; | |
| params->size = sizeof(unsigned long); | |
| params->data[0] = gd->baudrate; | |
| printf(" SERIAL_BAUDRATE: tag:0x%04x, size:%u, baudrate:%lu\n", | |
| BP_TAG_SERIAL_BAUDRATE, params->size, params->data[0]); | |
| return bp_tag_next(params); | |
| } | |
| #ifdef CONFIG_OF_LIBFDT | |
| static struct bp_tag *setup_fdt_tag(struct bp_tag *params, void *fdt_start) | |
| { | |
| params->id = BP_TAG_FDT; | |
| params->size = sizeof(unsigned long); | |
| params->data[0] = (unsigned long)fdt_start; | |
| printf(" FDT: tag:0x%04x, size:%u, start:0x%lx\n", | |
| BP_TAG_FDT, params->size, params->data[0]); | |
| return bp_tag_next(params); | |
| } | |
| #endif | |
| /* | |
| * Boot Linux. | |
| */ | |
| int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) | |
| { | |
| struct bp_tag *params, *params_start; | |
| ulong initrd_start, initrd_end; | |
| char *commandline = env_get("bootargs"); | |
| if (!(flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO))) | |
| return 0; | |
| show_boot_progress(15); | |
| if (images->rd_start) { | |
| initrd_start = images->rd_start; | |
| initrd_end = images->rd_end; | |
| } else { | |
| initrd_start = 0; | |
| initrd_end = 0; | |
| } | |
| params_start = (struct bp_tag *)gd->bd->bi_boot_params; | |
| params = params_start; | |
| params = setup_first_tag(params); | |
| params = setup_memory_tag(params); | |
| params = setup_commandline_tag(params, commandline); | |
| params = setup_serial_tag(params); | |
| if (initrd_start) | |
| params = setup_ramdisk_tag(params, initrd_start, initrd_end); | |
| #ifdef CONFIG_OF_LIBFDT | |
| if (images->ft_addr) | |
| params = setup_fdt_tag(params, images->ft_addr); | |
| #endif | |
| printf("\n"); | |
| params = setup_last_tag(params); | |
| show_boot_progress(15); | |
| printf("Transferring Control to Linux @0x%08lx ...\n\n", | |
| (ulong)images->ep); | |
| flush_dcache_range((unsigned long)params_start, (unsigned long)params); | |
| if (flag & BOOTM_STATE_OS_FAKE_GO) | |
| return 0; | |
| /* | |
| * _start() in vmlinux expects boot params in register a2. | |
| * NOTE: | |
| * Disable/delete your u-boot breakpoints before stepping into linux. | |
| */ | |
| asm volatile ("mov a2, %0\n\t" | |
| "jx %1\n\t" | |
| : : "a" (params_start), "a" (images->ep) | |
| : "a2"); | |
| /* Does not return */ | |
| return 1; | |
| } | |