/* * (C) Copyright 2017 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #ifdef CONFIG_ANDROID_BOOT_IMAGE #include #include #endif #define PART_RESOURCE "resource" #define RESOURCE_MAGIC "RSCE" #define RESOURCE_MAGIC_SIZE 4 #define RESOURCE_VERSION 0 #define CONTENT_VERSION 0 #define ENTRY_TAG "ENTR" #define ENTRY_TAG_SIZE 4 #define MAX_FILE_NAME_LEN 256 /* * resource image structure * ---------------------------------------------- * | | * | header (1 block) | * | | * ---------------------------------------------| * | | | * | entry0 (1 block) | | * | | | * ------------------------ | * | | | * | entry1 (1 block) | contents (n blocks) | * | | | * ------------------------ | * | ...... | | * ------------------------ | * | | | * | entryn (1 block) | | * | | | * ---------------------------------------------- * | | * | file0 (x blocks) | * | | * ---------------------------------------------- * | | * | file1 (y blocks) | * | | * ---------------------------------------------- * | ...... | * |--------------------------------------------- * | | * | filen (z blocks) | * | | * ---------------------------------------------- */ /** * struct resource_image_header * * @magic: should be "RSCE" * @version: resource image version, current is 0 * @c_version: content version, current is 0 * @blks: the size of the header ( 1 block = 512 bytes) * @c_offset: contents offset(by block) in the image * @e_blks: the size(by block) of the entry in the contents * @e_num: numbers of the entrys. */ struct resource_img_hdr { char magic[4]; uint16_t version; uint16_t c_version; uint8_t blks; uint8_t c_offset; uint8_t e_blks; uint32_t e_nums; }; struct resource_entry { char tag[4]; char name[MAX_FILE_NAME_LEN]; uint32_t f_offset; uint32_t f_size; }; struct resource_file { char name[MAX_FILE_NAME_LEN]; uint32_t f_offset; uint32_t f_size; struct list_head link; uint32_t rsce_base; /* Base addr of resource */ }; static LIST_HEAD(entrys_head); static int resource_image_check_header(const struct resource_img_hdr *hdr) { int ret; ret = memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE); if (ret) { printf("bad resource image magic\n"); ret = -EINVAL; } debug("resource image header:\n"); debug("magic:%s\n", hdr->magic); debug("version:%d\n", hdr->version); debug("c_version:%d\n", hdr->c_version); debug("blks:%d\n", hdr->blks); debug("c_offset:%d\n", hdr->c_offset); debug("e_blks:%d\n", hdr->e_blks); debug("e_num:%d\n", hdr->e_nums); return ret; } static int add_file_to_list(struct resource_entry *entry, int rsce_base) { struct resource_file *file; if (memcmp(entry->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) { printf("invalid entry tag\n"); return -ENOENT; } file = malloc(sizeof(*file)); if (!file) { printf("out of memory\n"); return -ENOMEM; } strcpy(file->name, entry->name); file->rsce_base = rsce_base; file->f_offset = entry->f_offset; file->f_size = entry->f_size; list_add_tail(&file->link, &entrys_head); debug("entry:%p %s offset:%d size:%d\n", entry, file->name, file->f_offset, file->f_size); return 0; } static int init_resource_list(struct resource_img_hdr *hdr) { struct resource_entry *entry; void *content; int size; int ret; int e_num; int offset = 0; int mode = 0; struct blk_desc *dev_desc; disk_partition_t part_info; #ifdef CONFIG_ANDROID_BOOT_IMAGE struct andr_img_hdr *andr_hdr; char *boot_partname = PART_BOOT; #endif if (hdr) { content = (void *)((char *)hdr + (hdr->c_offset) * RK_BLK_SIZE); for (e_num = 0; e_num < hdr->e_nums; e_num++) { size = e_num * hdr->e_blks * RK_BLK_SIZE; entry = (struct resource_entry *)(content + size); add_file_to_list(entry, offset); } return 0; } dev_desc = rockchip_get_bootdev(); hdr = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE); if (!hdr) { printf("%s out of memory!\n", __func__); return -ENOMEM; } #ifdef CONFIG_ANDROID_BOOT_IMAGE /* Get boot mode from misc */ mode = rockchip_get_boot_mode(); if (mode == BOOT_MODE_RECOVERY) boot_partname = PART_RECOVERY; /* Read boot/recovery and chenc if this is an AOSP img */ ret = part_get_info_by_name(dev_desc, boot_partname, &part_info); if (ret < 0) { printf("fail to get %s part\n", boot_partname); goto out; } andr_hdr = (void *)hdr; ret = blk_dread(dev_desc, part_info.start, 1, andr_hdr); if (ret != 1) { printf("%s read fail\n", __func__); goto out; } ret = android_image_check_header(andr_hdr); if (!ret) { debug("%s Load resource from %s senond pos\n", __func__, part_info.name); /* Read resource from second offset */ offset = part_info.start * RK_BLK_SIZE; offset += andr_hdr->page_size; offset += ALIGN(andr_hdr->kernel_size, andr_hdr->page_size); offset += ALIGN(andr_hdr->ramdisk_size, andr_hdr->page_size); offset = offset / RK_BLK_SIZE; } else { /* Set mode to 0 in for recovery is not valid AOSP img */ mode = 0; } #endif if (!mode) { /* Read resource from Rockchip Resource partition */ ret = part_get_info_by_name(dev_desc, PART_RESOURCE, &part_info); if (ret < 0) { printf("fail to get %s part\n", PART_RESOURCE); goto out; } offset = part_info.start; debug("%s Load resource from %s\n", __func__, part_info.name); } ret = blk_dread(dev_desc, offset, 1, hdr); if (ret != 1) goto out; ret = resource_image_check_header(hdr); if (ret < 0) goto out; content = memalign(ARCH_DMA_MINALIGN, hdr->e_blks * hdr->e_nums * RK_BLK_SIZE); if (!content) { printf("alloc memory for content failed\n"); goto out; } ret = blk_dread(dev_desc, offset + hdr->c_offset, hdr->e_blks * hdr->e_nums, content); if (ret != (hdr->e_blks * hdr->e_nums)) goto err; for (e_num = 0; e_num < hdr->e_nums; e_num++) { size = e_num * hdr->e_blks * RK_BLK_SIZE; entry = (struct resource_entry *)(content + size); add_file_to_list(entry, offset); } err: free(content); out: free(hdr); return 0; } static struct resource_file *get_file_info(struct resource_img_hdr *hdr, const char *name) { struct resource_file *file; struct list_head *node; if (list_empty(&entrys_head)) init_resource_list(hdr); list_for_each(node, &entrys_head) { file = list_entry(node, struct resource_file, link); if (!strcmp(file->name, name)) return file; } return NULL; } int rockchip_get_resource_file(void *buf, const char *name) { struct resource_file *file; file = get_file_info(buf, name); return file->f_offset; } /* * read file from resource partition * @buf: destination buf to store file data; * @name: file name * @offset: blocks offset in the file, 1 block = 512 bytes * @len: the size(by bytes) of file to read. */ int rockchip_read_resource_file(void *buf, const char *name, int offset, int len) { struct resource_file *file; int ret = 0; int blks; struct blk_desc *dev_desc; file = get_file_info(NULL, name); if (!file) { printf("Can't find file:%s\n", name); return -ENOENT; } if (len <= 0 || len > file->f_size) len = file->f_size; blks = DIV_ROUND_UP(len, RK_BLK_SIZE); dev_desc = rockchip_get_bootdev(); ret = blk_dread(dev_desc, file->rsce_base + file->f_offset + offset, blks, buf); if (ret != blks) ret = -EIO; else ret = len; return ret; }