| /* |
| * Copyright (c) 2015-2017, 2020 The Linux Foundation. All rights reserved. |
| |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <image.h> |
| #include <nand.h> |
| #include <errno.h> |
| #include <asm/arch-qca-common/scm.h> |
| #include <part.h> |
| #include <linux/mtd/ubi.h> |
| #include <asm/arch-qca-common/smem.h> |
| #include <mmc.h> |
| #include <part_efi.h> |
| #include <fdtdec.h> |
| #include "fdt_info.h" |
| #include <asm/errno.h> |
| #include <asm/arch-qca-common/qca_common.h> |
| #include <usb.h> |
| #include <elf.h> |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| #include <android_image.h> |
| #endif |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| #include <android_ab_boot.h> |
| #endif |
| |
| #define SEC_AUTH_SW_ID 0x17 |
| #define ROOTFS_IMAGE_TYPE 0x13 |
| #define NO_OF_PROGRAM_HDRS 3 |
| #define ELF_HDR_PLUS_PHDR_SIZE sizeof(Elf32_Ehdr) + \ |
| (NO_OF_PROGRAM_HDRS * sizeof(Elf32_Phdr)) |
| |
| unsigned long __stack_chk_guard = 0x000a0dff; |
| static int debug = 0; |
| static char mtdids[256]; |
| DECLARE_GLOBAL_DATA_PTR; |
| static qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| static int ipq_fs_on_nand = 0; |
| extern int nand_env_device; |
| extern qca_mmc mmc_host; |
| |
| #ifdef CONFIG_QCA_MMC |
| static qca_mmc *host = &mmc_host; |
| #endif |
| |
| /* boot.img source for bootipq command */ |
| #define QCA_BOOT_FROM_FLASH 0 |
| #define QCA_BOOT_FROM_USB 1 |
| #define QCA_BOOT_FROM_MEM 2 |
| |
| #ifndef MIN |
| #define MIN(x,y) ((x) < (y) ? (x) : (y)) |
| #endif |
| |
| typedef struct { |
| unsigned int image_type; |
| unsigned int header_vsn_num; |
| unsigned int image_src; |
| unsigned char *image_dest_ptr; |
| unsigned int image_size; |
| unsigned int code_size; |
| unsigned char *signature_ptr; |
| unsigned int signature_size; |
| unsigned char *cert_chain_ptr; |
| unsigned int cert_chain_size; |
| } mbn_header_t; |
| |
| typedef struct { |
| unsigned int kernel_load_addr; |
| unsigned int kernel_load_size; |
| } kernel_img_info_t; |
| |
| kernel_img_info_t kernel_img_info; |
| |
| char dtb_config_name[64]; |
| char kernelpart[ALT_PART_NAME_LENGTH]; |
| char rootfspart[ALT_PART_NAME_LENGTH]; |
| |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| typedef struct { |
| unsigned int img_offset; |
| unsigned int img_load_addr; |
| unsigned int img_size; |
| } image_info; |
| #endif |
| |
| void __stack_chk_fail(void) |
| { |
| printf("stack-protector: U-boot stack is corrupted.\n"); |
| bad_mode (); |
| } |
| |
| /* |
| * Memory layout: |
| * |
| * CONFIG_SYS_SDRAM_BASE - CONFIG_IPQ_FDT_HIGH (can be used by uboot) |
| * CONFIG_IPQ_FDT_HIGH - CONFIG_TZ_END_ADDR (reserved, can not be used by uboot) |
| * CONFIG_TZ_END_ADDR - CONFIG_SYS_SDRAM_END (can be used by uboot) |
| * |
| * This function checks if the memory between start_addr and start_addr+size |
| * resides in areas which can be used by uboot |
| */ |
| bool validate_bootimg_memory(unsigned int start_addr, unsigned int size) |
| { |
| if (((start_addr >= CONFIG_SYS_SDRAM_BASE) && |
| (start_addr + size < CONFIG_IPQ_FDT_HIGH)) || |
| ((start_addr >= CONFIG_TZ_END_ADDR) && |
| (start_addr + size < CONFIG_SYS_SDRAM_END))) |
| return true; |
| |
| return false; |
| } |
| |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| /* |
| * Set the root device and bootargs for mounting root filesystem. |
| */ |
| static int set_fs_bootargs(int *fs_on_nand) |
| { |
| char *bootargs; |
| unsigned int active_part = 0; |
| int ret = 0; |
| char boot_args[MAX_BOOT_ARGS_SIZE] = {'\0'}; |
| |
| |
| |
| #define nand_rootfs "ubi.mtd=" QCA_ROOT_FS_PART_NAME " root=mtd:ubi_rootfs rootfstype=squashfs" |
| |
| if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) { |
| if (get_which_flash_param("rootfs") || |
| ((sfi->flash_secondary_type == SMEM_BOOT_NAND_FLASH) || |
| (sfi->flash_secondary_type == SMEM_BOOT_QSPI_NAND_FLASH))) { |
| bootargs = nand_rootfs; |
| *fs_on_nand = 1; |
| |
| if (sfi->rootfs.offset == 0xBAD0FF5E) { |
| if(smem_bootconfig_info() == 0) |
| active_part = get_rootfs_active_partition(); |
| |
| sfi->rootfs.offset = active_part * IPQ_NAND_ROOTFS_SIZE; |
| sfi->rootfs.size = IPQ_NAND_ROOTFS_SIZE; |
| } |
| |
| fdt_setprop((void *)gd->fdt_blob, 0, "nor_nand_available", |
| fs_on_nand, sizeof(int)); |
| snprintf(mtdids, sizeof(mtdids), |
| "nand%d=nand%d,nand%d=" QCA_SPI_NOR_DEVICE, |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| CONFIG_SPI_FLASH_INFO_IDX |
| ); |
| |
| if (getenv("fsbootargs") == NULL) |
| setenv("fsbootargs", bootargs); |
| } else { |
| bootargs = boot_args; |
| if (smem_bootconfig_info() == 0) { |
| active_part = get_rootfs_active_partition(); |
| if (active_part) { |
| strlcpy(bootargs, "rootfsname=rootfs_1", sizeof(boot_args)); |
| if (sfi->rootfs.offset == 0xBAD0FF5E) |
| ret = set_uuid_bootargs(bootargs, "rootfs_1", sizeof(boot_args), false); |
| } else { |
| strlcpy(bootargs, "rootfsname=rootfs", sizeof(boot_args)); |
| if (sfi->rootfs.offset == 0xBAD0FF5E) |
| ret = set_uuid_bootargs(bootargs, "rootfs", sizeof(boot_args), false); |
| } |
| } else { |
| strlcpy(bootargs, "rootfsname=rootfs", sizeof(boot_args)); |
| if (sfi->rootfs.offset == 0xBAD0FF5E) |
| ret = set_uuid_bootargs(bootargs, "rootfs", sizeof(boot_args), false); |
| } |
| |
| if (ret) |
| return ret; |
| |
| *fs_on_nand = 0; |
| |
| snprintf(mtdids, sizeof(mtdids), "nand%d=" |
| QCA_SPI_NOR_DEVICE, CONFIG_SPI_FLASH_INFO_IDX); |
| |
| if (getenv("fsbootargs") == NULL) |
| setenv("fsbootargs", bootargs); |
| } |
| } else if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) || |
| (sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { |
| bootargs = nand_rootfs; |
| if (getenv("fsbootargs") == NULL) |
| setenv("fsbootargs", bootargs); |
| *fs_on_nand = 1; |
| |
| snprintf(mtdids, sizeof(mtdids), "nand0=nand0"); |
| |
| #ifdef CONFIG_QCA_MMC |
| } else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH) { |
| bootargs = boot_args; |
| if (smem_bootconfig_info() == 0) { |
| active_part = get_rootfs_active_partition(); |
| snprintf(bootargs, sizeof(boot_args), "rootfsname=%s gpt", rootfspart); |
| ret = set_uuid_bootargs(bootargs, rootfspart, sizeof(boot_args), true); |
| } else { |
| strlcpy(bootargs, "rootfsname=rootfs gpt", sizeof(boot_args)); |
| ret = set_uuid_bootargs(bootargs, "rootfs", sizeof(boot_args), true); |
| if (ret) { |
| strlcpy(bootargs, "rootfsname=system_a gpt", sizeof(boot_args)); |
| ret = set_uuid_bootargs(bootargs, "system_a", sizeof(boot_args), true); |
| } |
| } |
| |
| if (ret) |
| return ret; |
| |
| *fs_on_nand = 0; |
| if (getenv("fsbootargs") == NULL) |
| setenv("fsbootargs", bootargs); |
| #endif |
| } else { |
| printf("bootipq: unsupported boot flash type\n"); |
| return -EINVAL; |
| } |
| |
| return run_command("setenv bootargs ${bootargs} ${fsbootargs} rootwait", 0); |
| } |
| #endif |
| |
| int config_select(unsigned int addr, char *rcmd, int rcmd_size) |
| { |
| /* Selecting a config name from the list of available |
| * config names by passing them to the fit_conf_get_node() |
| * function which is used to get the node_offset with the |
| * config name passed. Based on the return value index based |
| * or board name based config is used. |
| */ |
| |
| #ifdef CONFIG_ARCH_IPQ806x |
| int soc_version = 0; |
| #endif |
| int i, strings_count; |
| const char *config = getenv("config_name"); |
| |
| if (config) { |
| printf("Manual device tree config selected!\n"); |
| strlcpy(dtb_config_name, config, sizeof(dtb_config_name)); |
| if (fit_conf_get_node((void *)addr, dtb_config_name) >= 0) { |
| snprintf(rcmd, rcmd_size, "bootm 0x%x#%s\n", |
| addr, dtb_config_name); |
| return 0; |
| } |
| |
| } else { |
| strings_count = fdt_count_strings(gd->fdt_blob, 0, "config_name"); |
| |
| if (!strings_count) { |
| printf("Failed to get config_name\n"); |
| return -1; |
| } |
| |
| for (i = 0; i < strings_count; i++) { |
| fdt_get_string_index(gd->fdt_blob, 0, "config_name", |
| i, &config); |
| |
| snprintf((char *)dtb_config_name, |
| sizeof(dtb_config_name), "%s", config); |
| |
| #ifdef CONFIG_ARCH_IPQ806x |
| ipq_smem_get_socinfo_version((uint32_t *)&soc_version); |
| if(SOCINFO_VERSION_MAJOR(soc_version) >= 2) { |
| snprintf(dtb_config_name + strlen("config@"), |
| sizeof(dtb_config_name) - strlen("config@"), |
| "v%d.0-%s", |
| SOCINFO_VERSION_MAJOR(soc_version), |
| config + strlen("config@")); |
| } |
| #endif |
| if (fit_conf_get_node((void *)addr, dtb_config_name) >= 0) { |
| snprintf(rcmd, rcmd_size, "bootm 0x%x#%s\n", |
| addr, dtb_config_name); |
| return 0; |
| } |
| } |
| } |
| |
| |
| printf("Config not available\n"); |
| return -1; |
| } |
| |
| __weak int switch_ce_channel_buf(unsigned int channel_id) |
| { |
| return 0; |
| } |
| |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| static int parse_elf_image_phdr(image_info *img_info, unsigned int addr) |
| { |
| Elf32_Ehdr *ehdr; /* Elf header structure pointer */ |
| Elf32_Phdr *phdr; /* Program header structure pointer */ |
| int i; |
| |
| ehdr = (Elf32_Ehdr *)addr; |
| phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff); |
| |
| if (!IS_ELF(*ehdr)) { |
| printf("It is not a elf image \n"); |
| return -EINVAL; |
| } |
| |
| if (ehdr->e_type != ET_EXEC) { |
| printf("Not a valid elf image\n"); |
| return -EINVAL; |
| } |
| |
| /* Load each program header */ |
| for (i = 0; i < NO_OF_PROGRAM_HDRS; ++i) { |
| printf("Parsing phdr load addr 0x%x offset 0x%x size 0x%x type 0x%x\n", |
| phdr->p_paddr, phdr->p_offset, phdr->p_filesz, phdr->p_type); |
| if(phdr->p_type == PT_LOAD) { |
| img_info->img_offset = phdr->p_offset; |
| img_info->img_load_addr = phdr->p_paddr; |
| img_info->img_size = phdr->p_filesz; |
| return 0; |
| } |
| ++phdr; |
| } |
| |
| return -EINVAL; |
| } |
| #endif |
| |
| #ifdef CONFIG_IPQ_ROOTFS_AUTH |
| static int copy_rootfs(unsigned int request, uint32_t size) |
| { |
| int ret; |
| char runcmd[256] = {0}; |
| #ifdef CONFIG_QCA_MMC |
| block_dev_desc_t *blk_dev; |
| disk_partition_t disk_info; |
| #endif |
| |
| if (ipq_fs_on_nand) { |
| snprintf(runcmd, sizeof(runcmd), |
| "ubi read 0x%x ubi_rootfs &&", request); |
| #ifdef CONFIG_QCA_MMC |
| } else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH || |
| ((sfi->flash_type == SMEM_BOOT_SPI_FLASH) && |
| (sfi->rootfs.offset == 0xBAD0FF5E))) { |
| blk_dev = mmc_get_dev(host->dev_num); |
| if (smem_bootconfig_info() == 0) { |
| get_rootfs_active_partition(); |
| ret = get_partition_info_efi_by_name(blk_dev, |
| rootfspart, &disk_info); |
| }else { |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "rootfs", &disk_info); |
| if (ret) |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "system_a", &disk_info); |
| } |
| if(ret == 0) |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%X 0x%X &&", |
| request, (uint)disk_info.start, |
| (uint)(size / disk_info.blksz) + 1); |
| else |
| return CMD_RET_FAILURE; |
| #endif |
| } else { |
| snprintf(runcmd, sizeof(runcmd), |
| "sf read 0x%x 0x%x 0x%x && ", |
| request, (uint)sfi->rootfs.offset, (uint)sfi->rootfs.size); |
| } |
| if (debug) |
| printf("runcmd: %s\n", runcmd); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| return 0; |
| } |
| |
| #ifndef CONFIG_IPQ_ELF_AUTH |
| static int authenticate_rootfs(unsigned int kernel_addr) |
| { |
| unsigned int kernel_imgsize; |
| unsigned int request; |
| int ret; |
| mbn_header_t *mbn_ptr; |
| struct { |
| unsigned long type; |
| unsigned long size; |
| unsigned long addr; |
| } rootfs_img_info; |
| |
| request = CONFIG_ROOTFS_LOAD_ADDR; |
| rootfs_img_info.addr = CONFIG_ROOTFS_LOAD_ADDR; |
| rootfs_img_info.type = SEC_AUTH_SW_ID; |
| request += sizeof(mbn_header_t);/* space for mbn header */ |
| |
| /* get , kernel size = header + kernel + certificate */ |
| mbn_ptr = (mbn_header_t *) kernel_addr; |
| kernel_imgsize = mbn_ptr->image_size + sizeof(mbn_header_t); |
| |
| /* get rootfs MBN header and validate it */ |
| mbn_ptr = (mbn_header_t *)((uint32_t)mbn_ptr + kernel_imgsize); |
| if (mbn_ptr->image_type != ROOTFS_IMAGE_TYPE && |
| (mbn_ptr->code_size + mbn_ptr->signature_size + |
| mbn_ptr->cert_chain_size != mbn_ptr->image_size)) |
| return CMD_RET_FAILURE; |
| |
| /* pack, MBN header + rootfs + certificate */ |
| /* copy rootfs from the boot device */ |
| copy_rootfs(request, mbn_ptr->code_size); |
| |
| /* copy rootfs MBN header */ |
| memcpy((void *)CONFIG_ROOTFS_LOAD_ADDR, (void *)kernel_addr + kernel_imgsize, |
| sizeof(mbn_header_t)); |
| /* copy rootfs certificate */ |
| memcpy((void *)request + mbn_ptr->code_size, |
| (void *)kernel_addr + kernel_imgsize + sizeof(mbn_header_t), |
| mbn_ptr->signature_size + mbn_ptr->cert_chain_size); |
| |
| /* copy rootfs size */ |
| rootfs_img_info.size = sizeof(mbn_header_t) + mbn_ptr->image_size; |
| |
| ret = qca_scm_secure_authenticate(&rootfs_img_info, sizeof(rootfs_img_info)); |
| if (ret) |
| return CMD_RET_FAILURE; |
| |
| return CMD_RET_SUCCESS; |
| } |
| |
| #else |
| static int authenticate_rootfs_elf(unsigned int rootfs_hdr) |
| { |
| int ret; |
| unsigned int request; |
| image_info img_info; |
| struct { |
| unsigned long type; |
| unsigned long size; |
| unsigned long addr; |
| } rootfs_img_info; |
| |
| request = CONFIG_ROOTFS_LOAD_ADDR; |
| rootfs_img_info.addr = request; |
| rootfs_img_info.type = SEC_AUTH_SW_ID; |
| |
| if (parse_elf_image_phdr(&img_info, rootfs_hdr)) |
| return CMD_RET_FAILURE; |
| |
| memcpy((void*)request, (void*)rootfs_hdr, img_info.img_offset); |
| |
| request += img_info.img_offset; |
| |
| /* copy rootfs from the boot device */ |
| copy_rootfs(request, img_info.img_size); |
| |
| rootfs_img_info.size = img_info.img_offset + img_info.img_size; |
| ret = qca_scm_secure_authenticate(&rootfs_img_info, sizeof(rootfs_img_info)); |
| if (ret) |
| return CMD_RET_FAILURE; |
| |
| return CMD_RET_SUCCESS; |
| } |
| #endif |
| #endif |
| |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| static bool bootimage_startswith_android_header(disk_partition_t *disk_info) |
| { |
| char runcmd[256] = {0}; |
| uint bootimg_start = (uint)disk_info->start; |
| |
| /* Load the first sector of boot.img |
| * and check if boot.img is signed or not. |
| * There is no mbn header for unsigned boot.img |
| * so we can use boot.img magic number at the beginning of |
| * the image to check if boot.img is signed or not. |
| * */ |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR, bootimg_start, 1); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return false; |
| } |
| |
| return (android_image_check_header((const struct andr_img_hdr *)CONFIG_SYS_LOAD_ADDR) == 0); |
| |
| } |
| |
| static int load_bootimg(disk_partition_t *disk_info, bool bootimg_is_signed) |
| { |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| char runcmd[256] = {0}; |
| const struct andr_img_hdr *hdr; |
| mbn_header_t *mbn_hdr=(mbn_header_t *)CONFIG_SYS_LOAD_ADDR; |
| uint total_img_size; |
| uint bootimg_start = (uint)disk_info->start; |
| |
| if (bootimg_is_signed) { |
| /* load signed boot.img */ |
| /* load mbn header first */ |
| /* 1 sector (512B) is enough for mbn header */ |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR, bootimg_start, 1); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* check if this is valid signed boot.img with mbn header, |
| * mbn header doesn't have magic number, so we use a tricky way |
| * here */ |
| if (mbn_hdr->image_size != |
| (mbn_hdr->code_size + mbn_hdr->signature_size + mbn_hdr->cert_chain_size)) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* calculate the remaining image size to read */ |
| total_img_size = sizeof(mbn_header_t) + |
| mbn_hdr->image_size; |
| |
| /* calculate image size in # of sectors */ |
| total_img_size = (total_img_size + MMC_MAX_BLOCK_LEN - 1) / MMC_MAX_BLOCK_LEN; |
| if ((total_img_size > (uint)disk_info->size) || |
| (total_img_size <= 1)) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* read remaining boot.img */ |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR + MMC_MAX_BLOCK_LEN, |
| bootimg_start + 1, |
| total_img_size - 1); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| } else { |
| /* load unsigned boot.img */ |
| /* load boot.img header first */ |
| /* 1 sector (512B) is enough for boot.img header */ |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR, bootimg_start, 1); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* check the magic number of android boot.img */ |
| hdr = (const struct andr_img_hdr *)CONFIG_SYS_LOAD_ADDR; |
| if (android_image_check_header(hdr) != 0) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* calculate the remaining boot.img size to read */ |
| total_img_size = hdr->page_size + |
| ALIGN(hdr->kernel_size, hdr->page_size) + |
| ALIGN(hdr->ramdisk_size, hdr->page_size) + |
| ALIGN(hdr->second_size, hdr->page_size); |
| /* calculate image size in # of sectors */ |
| total_img_size = (total_img_size + MMC_MAX_BLOCK_LEN - 1) / MMC_MAX_BLOCK_LEN; |
| if ((total_img_size > (uint)disk_info->size) || |
| (total_img_size <= 1)) { |
| return CMD_RET_FAILURE; |
| } |
| |
| /* read remaining boot.img */ |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR + MMC_MAX_BLOCK_LEN, |
| bootimg_start + 1, |
| total_img_size - 1); |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| } |
| |
| return CMD_RET_SUCCESS; |
| #else |
| return CMD_RET_FAILURE; |
| #endif // CONFIG_ANDROID_BOOT_IMAGE |
| } |
| #endif // CONFIG_ANDROID_AB_BOOT |
| |
| static int do_boot_signedimg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| char runcmd[256] = {0}; |
| int ret; |
| int boot_src = QCA_BOOT_FROM_FLASH; /* default boot from flash */ |
| unsigned int request; |
| #ifdef CONFIG_QCA_MMC |
| block_dev_desc_t *blk_dev; |
| disk_partition_t disk_info; |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| char boot_part_name[20]={0}; |
| #endif // CONFIG_ANDROID_AB_BOOT |
| #endif |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| image_info img_info; |
| #endif |
| |
| if (argc == 2 && strncmp(argv[1], "debug", 5) == 0) { |
| debug = 1; |
| } else if (argc == 3 && strncmp(argv[1], "mem", 3) == 0) { |
| boot_src = QCA_BOOT_FROM_MEM; |
| } else if (argc == 4 && strncmp(argv[1], "usb", 3) == 0) { |
| boot_src = QCA_BOOT_FROM_USB; |
| } |
| |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| if ((ret = set_fs_bootargs(&ipq_fs_on_nand))) |
| return ret; |
| #endif |
| |
| switch (boot_src) { |
| case QCA_BOOT_FROM_FLASH: |
| /* check the smem info to see which flash used for booting */ |
| if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) { |
| if (debug) { |
| printf("Using nand device %d\n", CONFIG_SPI_FLASH_INFO_IDX); |
| } |
| } else if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) || |
| (sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { |
| if (debug) { |
| printf("Using nand device 0\n"); |
| } |
| } else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH) { |
| if (debug) { |
| printf("Using MMC device\n"); |
| } |
| } else { |
| printf("Unsupported BOOT flash type\n"); |
| return -1; |
| } |
| if (debug) { |
| run_command("printenv bootargs", 0); |
| printf("Booting from flash\n"); |
| } |
| request = CONFIG_SYS_LOAD_ADDR; |
| break; |
| |
| case QCA_BOOT_FROM_USB: |
| printf("Boot from USB device\n"); |
| request = CONFIG_SYS_LOAD_ADDR; |
| break; |
| |
| case QCA_BOOT_FROM_MEM: |
| printf("Boot from memory\n"); |
| request = simple_strtoul(argv[2], NULL, 16); |
| break; |
| |
| default: |
| printf("Unsupported BOOT source\n"); |
| return -1; |
| } |
| |
| kernel_img_info.kernel_load_addr = request; |
| |
| switch (boot_src) { |
| case QCA_BOOT_FROM_FLASH: |
| if (ipq_fs_on_nand) { |
| #ifdef CONFIG_CMD_UBI |
| /* |
| * The kernel will be available inside a UBI volume |
| */ |
| snprintf(runcmd, sizeof(runcmd), |
| "nand device %d && " |
| "setenv mtdids nand%d=nand%d && " |
| "setenv mtdparts mtdparts=nand%d:0x%llx@0x%llx(fs),${msmparts} && " |
| "ubi part fs && ", is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| sfi->rootfs.size, sfi->rootfs.offset); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| snprintf(runcmd, sizeof(runcmd), |
| "ubi read 0x%x kernel 0x%x && ", |
| request, ELF_HDR_PLUS_PHDR_SIZE); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| if (parse_elf_image_phdr(&img_info, request)) |
| return CMD_RET_FAILURE; |
| |
| request = img_info.img_load_addr - img_info.img_offset; |
| #endif |
| snprintf(runcmd, sizeof(runcmd), |
| "ubi read 0x%x kernel && ", request); |
| |
| if (debug) |
| printf("%s", runcmd); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| kernel_img_info.kernel_load_size = |
| (unsigned int)ubi_get_volume_size("kernel"); |
| #endif |
| #ifdef CONFIG_QCA_MMC |
| } else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH || |
| ((sfi->flash_type == SMEM_BOOT_SPI_FLASH) && |
| (sfi->rootfs.offset == 0xBAD0FF5E))) { |
| blk_dev = mmc_get_dev(host->dev_num); |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| if (smem_bootconfig_info() != 0) |
| return -1; |
| |
| snprintf(boot_part_name, sizeof(boot_part_name), |
| "boot%s", android_get_active_slot()); |
| ret = get_partition_info_efi_by_name(blk_dev, |
| boot_part_name, &disk_info); |
| |
| if (load_bootimg(&disk_info, true) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| #else |
| if (smem_bootconfig_info() == 0) { |
| get_rootfs_active_partition(); |
| ret = get_partition_info_efi_by_name(blk_dev, |
| kernelpart, &disk_info); |
| } else { |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "0:HLOS", &disk_info); |
| |
| if (ret) |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "boot_a", &disk_info); |
| } |
| |
| if (ret == 0) { |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%X 0x%X", |
| CONFIG_SYS_LOAD_ADDR, |
| (uint)disk_info.start, ELF_HDR_PLUS_PHDR_SIZE); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| if (parse_elf_image_phdr(&img_info, request)) |
| return CMD_RET_FAILURE; |
| |
| request = img_info.img_load_addr - img_info.img_offset; |
| #endif |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%X 0x%X", |
| request, |
| (uint)disk_info.start, (uint)disk_info.size); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| kernel_img_info.kernel_load_size = disk_info.size * disk_info.blksz; |
| } |
| #endif // CONFIG_ANDROID_AB_BOOT |
| #endif |
| } else { |
| /* |
| * Kernel is in a separate partition |
| */ |
| snprintf(runcmd, sizeof(runcmd), "sf probe &&"); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| snprintf(runcmd, sizeof(runcmd), |
| "sf read 0x%x 0x%x 0x%x && ", |
| CONFIG_SYS_LOAD_ADDR, |
| (uint)sfi->hlos.offset, ELF_HDR_PLUS_PHDR_SIZE); |
| |
| if (debug) |
| printf("%s", runcmd); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| if (parse_elf_image_phdr(&img_info, request)) |
| return CMD_RET_FAILURE; |
| |
| request = img_info.img_load_addr - img_info.img_offset; |
| #endif |
| snprintf(runcmd, sizeof(runcmd), |
| "sf read 0x%x 0x%x 0x%x && ", |
| request, |
| (uint)sfi->hlos.offset, (uint)sfi->hlos.size); |
| |
| if (debug) |
| printf("%s", runcmd); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| kernel_img_info.kernel_load_size = sfi->hlos.size; |
| } |
| break; |
| |
| case QCA_BOOT_FROM_USB: |
| snprintf(runcmd, sizeof(runcmd), "fatsize usb %s %s", argv[2], argv[3]); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| |
| kernel_img_info.kernel_load_size = MIN(getenv_hex("filesize", 0), CONFIG_BOOGIMG_SIZE_MAX); |
| if (validate_bootimg_memory(kernel_img_info.kernel_load_addr, kernel_img_info.kernel_load_size)) { |
| snprintf(runcmd, sizeof(runcmd), "fatload usb %s %x %s %x", argv[2], kernel_img_info.kernel_load_addr, argv[3], kernel_img_info.kernel_load_size); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| } else { |
| printf("file size too large\n"); |
| return CMD_RET_FAILURE; |
| } |
| break; |
| } |
| |
| setenv("mtdids", mtdids); |
| |
| #ifndef CONFIG_IPQ_ELF_AUTH |
| request += sizeof(mbn_header_t); |
| #else |
| kernel_img_info.kernel_load_addr = request; |
| request = img_info.img_load_addr; |
| #endif |
| |
| /* This sys call will switch the CE1 channel to register usage */ |
| ret = switch_ce_channel_buf(0); |
| |
| if (ret) |
| return CMD_RET_FAILURE; |
| |
| ret = qca_scm_auth_kernel(&kernel_img_info, |
| sizeof(kernel_img_info)); |
| |
| if (ret) { |
| printf("Kernel image authentication failed \n"); |
| BUG(); |
| } |
| #ifdef CONFIG_IPQ_ROOTFS_AUTH |
| #ifdef CONFIG_IPQ_ELF_AUTH |
| if (authenticate_rootfs_elf(img_info.img_load_addr + |
| img_info.img_size) != CMD_RET_SUCCESS) { |
| printf("Rootfs elf image authentication failed\n"); |
| BUG(); |
| } |
| #else |
| /* Rootfs's header and certificate at end of kernel image, copy from |
| * there and pack with rootfs image and authenticate rootfs */ |
| if (authenticate_rootfs(CONFIG_SYS_LOAD_ADDR) != CMD_RET_SUCCESS) { |
| printf("Rootfs image authentication failed\n"); |
| BUG(); |
| } |
| #endif |
| #endif |
| /* |
| * This sys call will switch the CE1 channel to ADM usage |
| * so that HLOS can use it. |
| */ |
| ret = switch_ce_channel_buf(1); |
| |
| if (ret) |
| return CMD_RET_FAILURE; |
| |
| dcache_enable(); |
| |
| ret = genimg_get_format((void *)request); |
| if (ret == IMAGE_FORMAT_FIT) { |
| ret = config_select(request, runcmd, sizeof(runcmd)); |
| } else if ((ret == IMAGE_FORMAT_LEGACY) |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| || (ret == IMAGE_FORMAT_ANDROID) |
| #endif |
| ) { |
| snprintf(runcmd, sizeof(runcmd), |
| "bootm 0x%x\n", request); |
| } else { |
| printf("Image Format not supported \n"); |
| ret = -1; |
| } |
| |
| if (debug) |
| printf("%s", runcmd); |
| |
| if (ret < 0 || run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| #ifdef CONFIG_QCA_MMC |
| mmc_initialize(gd->bd); |
| #endif |
| #ifdef CONFIG_USB_XHCI_IPQ |
| ipq_board_usb_init(); |
| #endif |
| dcache_disable(); |
| return CMD_RET_FAILURE; |
| } |
| |
| #ifndef CONFIG_QCA_APPSBL_DLOAD |
| reset_crashdump(); |
| #endif |
| return CMD_RET_SUCCESS; |
| } |
| |
| static int do_boot_unsignedimg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| int ret; |
| int boot_src = QCA_BOOT_FROM_FLASH; /* default boot from flash */ |
| unsigned int request; |
| char runcmd[256] = {0}; |
| #ifdef CONFIG_QCA_MMC |
| block_dev_desc_t *blk_dev; |
| disk_partition_t disk_info; |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| char boot_part_name[20]={0}; |
| bool image_is_unsigned; |
| #endif // CONFIG_ANDROID_AB_BOOT |
| #endif // CONFIG_QCA_MMC |
| |
| if (argc == 2 && strncmp(argv[1], "debug", 5) == 0) { |
| debug = 1; |
| } else if (argc == 3 && strncmp(argv[1], "mem", 3) == 0) { |
| boot_src = QCA_BOOT_FROM_MEM; |
| } else if (argc == 4 && strncmp(argv[1], "usb", 3) == 0) { |
| boot_src = QCA_BOOT_FROM_USB; |
| } |
| |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| if ((ret = set_fs_bootargs(&ipq_fs_on_nand))) |
| return ret; |
| #endif // !CONFIG_ANDROID_AB_BOOT |
| |
| request = CONFIG_SYS_LOAD_ADDR; |
| |
| switch (boot_src) { |
| case QCA_BOOT_FROM_FLASH: |
| if (debug) { |
| run_command("printenv bootargs", 0); |
| printf("Booting from flash\n"); |
| } |
| |
| if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) || |
| (sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { |
| if (debug) { |
| printf("Using nand device 0\n"); |
| } |
| |
| /* |
| * The kernel is in seperate partition |
| */ |
| if (sfi->rootfs.offset == 0xBAD0FF5E) { |
| printf(" bad offset of hlos"); |
| return -1; |
| } |
| |
| snprintf(runcmd, sizeof(runcmd), |
| "setenv mtdids nand0=nand0 && " |
| "setenv mtdparts mtdparts=nand0:0x%llx@0x%llx(fs),${msmparts} && " |
| "ubi part fs && " |
| "ubi read 0x%x kernel && ", |
| sfi->rootfs.size, sfi->rootfs.offset, |
| CONFIG_SYS_LOAD_ADDR); |
| |
| } else if (((sfi->flash_type == SMEM_BOOT_SPI_FLASH) && |
| (sfi->rootfs.offset != 0xBAD0FF5E)) || |
| ipq_fs_on_nand) { |
| if (get_which_flash_param("rootfs") || ipq_fs_on_nand) { |
| snprintf(runcmd, sizeof(runcmd), |
| "nand device %d && " |
| "setenv mtdids nand%d=nand%d && " |
| "setenv mtdparts mtdparts=nand%d:0x%llx@0x%llx(fs),${msmparts} && " |
| "ubi part fs && " |
| "ubi read 0x%x kernel && ", |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| sfi->rootfs.size, sfi->rootfs.offset, |
| CONFIG_SYS_LOAD_ADDR); |
| } else { |
| /* |
| * Kernel is in a separate partition |
| */ |
| snprintf(runcmd, sizeof(runcmd), |
| "sf probe &&" |
| "sf read 0x%x 0x%x 0x%x && ", |
| CONFIG_SYS_LOAD_ADDR, (uint)sfi->hlos.offset, (uint)sfi->hlos.size); |
| } |
| #ifdef CONFIG_QCA_MMC |
| } else if ((sfi->flash_type == SMEM_BOOT_MMC_FLASH) || |
| ((sfi->flash_type == SMEM_BOOT_SPI_FLASH) && |
| (sfi->rootfs.offset == 0xBAD0FF5E))) { |
| if (debug) { |
| printf("Using MMC device\n"); |
| } |
| blk_dev = mmc_get_dev(host->dev_num); |
| #ifdef CONFIG_ANDROID_AB_BOOT |
| if (smem_bootconfig_info() != 0) |
| return -1; |
| |
| snprintf(boot_part_name, sizeof(boot_part_name), |
| "boot%s", android_get_active_slot()); |
| ret = get_partition_info_efi_by_name(blk_dev, |
| boot_part_name, &disk_info); |
| image_is_unsigned = bootimage_startswith_android_header(&disk_info); |
| if (load_bootimg(&disk_info, !image_is_unsigned) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| #else |
| if (smem_bootconfig_info() == 0) { |
| get_rootfs_active_partition(); |
| ret = get_partition_info_efi_by_name(blk_dev, |
| kernelpart, &disk_info); |
| } else { |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "0:HLOS", &disk_info); |
| if (ret) |
| ret = get_partition_info_efi_by_name(blk_dev, |
| "boot_a", &disk_info); |
| } |
| |
| if (ret == 0) { |
| snprintf(runcmd, sizeof(runcmd), "mmc read 0x%x 0x%x 0x%x", |
| CONFIG_SYS_LOAD_ADDR, |
| (uint)disk_info.start, (uint)disk_info.size); |
| } |
| #endif // CONFIG_ANDROID_AB_BOOT |
| #endif /* CONFIG_QCA_MMC */ |
| |
| } else { |
| printf("Unsupported BOOT flash type\n"); |
| return -1; |
| } |
| break; |
| |
| case QCA_BOOT_FROM_USB: |
| printf("Boot from USB device\n"); |
| |
| snprintf(runcmd, sizeof(runcmd), "fatsize usb %s %s", argv[2], argv[3]); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| |
| kernel_img_info.kernel_load_size = MIN(getenv_hex("filesize", 0), CONFIG_BOOGIMG_SIZE_MAX); |
| kernel_img_info.kernel_load_addr = CONFIG_SYS_LOAD_ADDR; |
| |
| if (validate_bootimg_memory(kernel_img_info.kernel_load_addr, kernel_img_info.kernel_load_size)) { |
| snprintf(runcmd, sizeof(runcmd), "fatload usb %s %x %s %x", argv[2], CONFIG_SYS_LOAD_ADDR, argv[3], kernel_img_info.kernel_load_size); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| } else { |
| printf("file size too large\n"); |
| return CMD_RET_FAILURE; |
| } |
| |
| break; |
| |
| case QCA_BOOT_FROM_MEM: |
| request = simple_strtoul(argv[2], NULL, 16); |
| printf("Boot from memory\n"); |
| break; |
| } |
| |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| #ifdef CONFIG_QCA_MMC |
| mmc_initialize(gd->bd); |
| #endif |
| return CMD_RET_FAILURE; |
| } |
| #endif |
| |
| dcache_enable(); |
| |
| setenv("mtdids", mtdids); |
| |
| ret = genimg_get_format((void *)request); |
| if (ret == IMAGE_FORMAT_FIT) { |
| ret = config_select(request, |
| runcmd, sizeof(runcmd)); |
| } else if ((ret == IMAGE_FORMAT_LEGACY) |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| || (ret == IMAGE_FORMAT_ANDROID) |
| #endif |
| ) { |
| snprintf(runcmd, sizeof(runcmd), |
| "bootm 0x%x\n", request); |
| } else { |
| ret = genimg_get_format((void *)request + |
| sizeof(mbn_header_t)); |
| if (ret == IMAGE_FORMAT_FIT) { |
| ret = config_select((request |
| + sizeof(mbn_header_t)), |
| runcmd, sizeof(runcmd)); |
| } else if ((ret == IMAGE_FORMAT_LEGACY) |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| || (ret == IMAGE_FORMAT_ANDROID) |
| #endif |
| ) { |
| snprintf(runcmd, sizeof(runcmd), |
| "bootm 0x%x\n", (request + |
| sizeof(mbn_header_t))); |
| } else { |
| dcache_disable(); |
| return CMD_RET_FAILURE; |
| } |
| } |
| |
| if (ret < 0 || run_command(runcmd, 0) != CMD_RET_SUCCESS) { |
| #ifdef CONFIG_USB_XHCI_IPQ |
| ipq_board_usb_init(); |
| #endif |
| dcache_disable(); |
| return CMD_RET_FAILURE; |
| } |
| #ifndef CONFIG_QCA_APPSBL_DLOAD |
| reset_crashdump(); |
| #endif |
| return CMD_RET_SUCCESS; |
| } |
| |
| int do_bootipq(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| int ret; |
| char buf = 0; |
| /* |
| * set fdt_high parameter so that u-boot will not load |
| * dtb above CONFIG_IPQ40XX_FDT_HIGH region. |
| */ |
| if (run_command("setenv fdt_high " MK_STR(CONFIG_IPQ_FDT_HIGH) "\n", 0) |
| != CMD_RET_SUCCESS) { |
| return CMD_RET_FAILURE; |
| } |
| |
| ret = qca_scm_call(SCM_SVC_FUSE, QFPROM_IS_AUTHENTICATE_CMD, &buf, sizeof(char)); |
| |
| aquantia_phy_reset_init_done(); |
| /* |
| || if atf is enable in env ,do_boot_signedimg is skip. |
| || Note: This features currently support in ipq50XX. |
| */ |
| if (ret == 0 && buf == 1 && !getenv("atf")) { |
| printf("%s: boot signed image\n", __func__); |
| ret = do_boot_signedimg(cmdtp, flag, argc, argv); |
| } else if (ret == 0 || ret == -EOPNOTSUPP) { |
| printf("%s: boot unsigned image\n", __func__); |
| ret = do_boot_unsignedimg(cmdtp, flag, argc, argv); |
| } |
| |
| if (ret == CMD_RET_FAILURE) { |
| #ifdef CONFIG_IPQ_ETH_INIT_DEFER |
| puts("\nNet: "); |
| eth_initialize(); |
| #endif |
| } |
| return ret; |
| } |
| |
| U_BOOT_CMD(bootipq, 4, 0, do_bootipq, |
| "bootipq from flash device", |
| "bootipq [debug] - Load image(s) and boots the kernel\n"); |