/* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #define TAG_KERNEL 0x4C4E524B #define DTB_FILE "rk-kernel.dtb" #define BOOTLOADER_MESSAGE_OFFSET_IN_MISC (16 * 1024) #define BOOTLOADER_MESSAGE_BLK_OFFSET (BOOTLOADER_MESSAGE_OFFSET_IN_MISC >> 9) struct bootloader_message { char command[32]; char status[32]; char recovery[768]; /* * The 'recovery' field used to be 1024 bytes. It has only ever * been used to store the recovery command line, so 768 bytes * should be plenty. We carve off the last 256 bytes to store the * stage string (for multistage packages) and possible future * expansion. */ char stage[32]; char slot_suffix[32]; char reserved[192]; }; struct rockchip_image { uint32_t tag; uint32_t size; int8_t image[1]; uint32_t crc; }; #if !defined(CONFIG_ARM64) #ifdef CONFIG_LMB static void boot_start_lmb(bootm_headers_t *images) { ulong mem_start; phys_size_t mem_size; lmb_init(&images->lmb); mem_start = env_get_bootm_low(); mem_size = env_get_bootm_size(); lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); arch_lmb_reserve(&images->lmb); board_lmb_reserve(&images->lmb); } #else static inline void boot_start_lmb(bootm_headers_t *images) { } #endif static void boot_lmb_init(bootm_headers_t *images) { boot_start_lmb(images); images->state = BOOTM_STATE_OS_GO; } #endif /* * non-OTA packaged kernel.img & boot.img * return the image size on success, and a * negative value on error. */ static int read_rockchip_image(struct blk_desc *dev_desc, disk_partition_t *part_info, void *dst) { struct rockchip_image *img; int header_len = 8; int cnt; int ret; #ifdef CONFIG_ROCKCHIP_CRC u32 crc32; #endif img = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE); if (!img) { printf("out of memory\n"); return -ENOMEM; } /* read first block with header imformation */ ret = blk_dread(dev_desc, part_info->start, 1, img); if (ret != 1) { ret = -EIO; goto err; } if (img->tag != TAG_KERNEL) { printf("%s: invalid image tag(0x%x)\n", part_info->name, img->tag); ret = -EINVAL; goto err; } memcpy(dst, img->image, RK_BLK_SIZE - header_len); /* * read the rest blks * total size = image size + 8 bytes header + 4 bytes crc32 */ cnt = DIV_ROUND_UP(img->size + 8 + 4, RK_BLK_SIZE); ret = blk_dread(dev_desc, part_info->start + 1, cnt - 1, dst + RK_BLK_SIZE - header_len); if (ret != (cnt - 1)) { printf("%s try to read %d blocks failed, only read %d blocks\n", part_info->name, cnt - 1, ret); ret = -EIO; } else { ret = img->size; } #ifdef CONFIG_ROCKCHIP_CRC printf("%s image CRC32 verify... ", part_info->name); crc32 = rockchip_crc_verify((unsigned char *)(unsigned long)dst, img->size + 4); if (!crc32) { printf("fail!\n"); ret = -EINVAL; } else { printf("okay.\n"); } #endif err: free(img); return ret; } /* Gets the storage type of the current device */ int get_bootdev_type(void) { int type = 0; char *boot_media = NULL; static int appended; #ifdef CONFIG_EMMC_BOOT type = IF_TYPE_MMC; boot_media = "storagemedia=emmc"; #endif /* CONFIG_EMMC_BOOT */ #ifdef CONFIG_QSPI_BOOT type = IF_TYPE_SPI_NAND; boot_media = "storagemedia=nand"; #endif /* CONFIG_QSPI_BOOT */ #ifdef CONFIG_NAND_BOOT type = IF_TYPE_RKNAND; boot_media = "storagemedia=nand"; #endif /* CONFIG_NAND_BOOT */ #ifdef CONFIG_NOR_BOOT type = IF_TYPE_SPI_NOR; #endif /* CONFIG_NOR_BOOT */ /* For current use(Only EMMC support!) */ if (!type) { type = IF_TYPE_MMC; boot_media = "storagemedia=emmc"; } if (!appended && boot_media) { appended = 1; env_update("bootargs", boot_media); } return type; } struct blk_desc *rockchip_get_bootdev(void) { struct blk_desc *dev_desc; int dev_type; dev_type = get_bootdev_type(); dev_desc = blk_get_devnum_by_type(dev_type, 0); return dev_desc; } static int boot_mode = -1; int rockchip_get_boot_mode(void) { struct blk_desc *dev_desc; disk_partition_t part_info; struct bootloader_message *bmsg; int size = DIV_ROUND_UP(sizeof(struct bootloader_message), RK_BLK_SIZE) * RK_BLK_SIZE; int ret; uint32_t reg_boot_mode; if (boot_mode != -1) return boot_mode; dev_desc = rockchip_get_bootdev(); ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info); if (ret < 0) { printf("get part %s fail %d\n", PART_MISC, ret); return -EIO; } bmsg = memalign(ARCH_DMA_MINALIGN, size); ret = blk_dread(dev_desc, part_info.start + BOOTLOADER_MESSAGE_BLK_OFFSET, size >> 9, bmsg); if (ret != (size >> 9)) { free(bmsg); return -EIO; } /* Mode from misc partition */ if (!strcmp(bmsg->command, "boot-recovery")) { boot_mode = BOOT_MODE_RECOVERY; } else { /* Mode from boot mode register */ reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); writel(BOOT_NORMAL, (void *)CONFIG_ROCKCHIP_BOOT_MODE_REG); switch (reg_boot_mode) { case BOOT_NORMAL: printf("boot mode: normal\n"); boot_mode = BOOT_MODE_NORMAL; break; case BOOT_FASTBOOT: printf("boot mode: bootloader\n"); boot_mode = BOOT_MODE_BOOTLOADER; break; case BOOT_LOADER: printf("boot mode: loader\n"); boot_mode = BOOT_MODE_LOADER; break; case BOOT_RECOVERY: /* printf("boot mode: recovery\n"); */ boot_mode = BOOT_MODE_RECOVERY; break; case BOOT_UMS: printf("boot mode: ums\n"); boot_mode = BOOT_MODE_UMS; break; case BOOT_CHARGING: printf("boot mode: charging\n"); boot_mode = BOOT_MODE_CHARGING; break; default: printf("boot mode: None\n"); boot_mode = BOOT_MODE_UNDEFINE; } } return boot_mode; } int boot_rockchip_image(struct blk_desc *dev_desc, disk_partition_t *boot_part) { ulong fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0); ulong ramdisk_addr_r = env_get_ulong("ramdisk_addr_r", 16, 0); ulong kernel_addr_r = env_get_ulong("kernel_addr_r", 16, 0x480000); disk_partition_t kernel_part; int ramdisk_size; int kernel_size; int fdt_size; int ret = 0; int part_num; printf("=Booting Rockchip format image=\n"); part_num = part_get_info_by_name(dev_desc, PART_KERNEL, &kernel_part); if (part_num < 0 || !boot_part) { printf("%s kernel or boot part info error\n", __func__); ret = -EINVAL; goto out; } kernel_size = read_rockchip_image(dev_desc, &kernel_part, (void *)kernel_addr_r); if (kernel_size < 0) { printf("%s kernel part read error\n", __func__); ret = -EINVAL; goto out; } ramdisk_size = read_rockchip_image(dev_desc, boot_part, (void *)ramdisk_addr_r); if (ramdisk_size < 0) { printf("%s ramdisk part %s read error\n", __func__, boot_part->name); ret = -EINVAL; goto out; } fdt_size = rockchip_read_resource_file((void *)fdt_addr_r, DTB_FILE, 0, 0); if (fdt_size < 0) { printf("%s fdt read error\n", __func__); ret = -EINVAL; goto out; } printf("kernel @ 0x%08lx (0x%08lx)\n", kernel_addr_r, kernel_size); printf("ramdisk @ 0x%08lx (0x%08lx)\n", ramdisk_addr_r, ramdisk_size); #if defined(CONFIG_ARM64) char cmdbuf[64]; sprintf(cmdbuf, "booti 0x%lx 0x%lx:0x%lx 0x%lx", kernel_addr_r, ramdisk_addr_r, ramdisk_size, fdt_addr_r); run_command(cmdbuf, 0); #else boot_lmb_init(&images); images.ep = kernel_addr_r; images.initrd_start = ramdisk_addr_r; images.initrd_end = ramdisk_addr_r + ramdisk_size; images.ft_addr = (void *)fdt_addr_r; images.ft_len = fdt_totalsize(fdt_addr_r); do_bootm_linux(0, 0, NULL, &images); #endif out: return ret; }