diff --git a/Config.in.kernel b/Config.in.kernel index f10488b4d..3caa857dc 100644 --- a/Config.in.kernel +++ b/Config.in.kernel @@ -125,7 +125,18 @@ config OF_ADDRESS default "0x61000000" if SAMA7G5 default "0x21000000" - endmenu +config IMG_FIT + depends on LOAD_LINUX && (NANDFLASH || DATAFLASH) + select OF_LIBFDT + bool "Support loading a FIT image" + help + A FIT image is a container. It usualy contains the kernel, one or more DTB, + and optionnaly a initramfs. It also specifies where each of those images must + be loaded in memory. + The FIT image is loaded in memory at the address defined by IMG_ADDRESS. Care must + taken to select this address so that it does not overlap with the adresses specified + in the FIT. + endmenu diff --git a/driver/load_kernel.c b/driver/load_kernel.c index de7102931..45d418822 100644 --- a/driver/load_kernel.c +++ b/driver/load_kernel.c @@ -23,13 +23,22 @@ #include "debug.h" +#ifdef CONFIG_MMU +#include "mmu.h" +#endif + +#ifdef CONFIG_CACHES +#include "l1cache.h" +#endif + static char cmdline_buf[256]; static char *bootargs; #ifdef CONFIG_OF_LIBFDT -static int setup_dt_blob(void *blob) +static int setup_dt_blob(void *blob, struct image_info *image) { + char *bootargs_start = NULL; int ret; #if !defined(CONFIG_LOAD_OPTEE) unsigned int mem_bank = AT91C_BASE_DDRCS; @@ -63,11 +72,13 @@ static int setup_dt_blob(void *blob) if (*p == '\0') return -1; - ret = fixup_chosen_node(blob, p); - if (ret) - return ret; + bootargs_start = p; } + ret = fixup_chosen_node(blob, bootargs_start, image); + if (ret) + return ret; + /* * When using OP-TEE the memory node should match the configuration of the DDR * that has been secured. Since this can't easily be inferred from @@ -362,6 +373,7 @@ int load_kernel(struct image_info *image) unsigned int mach_type; int ret; unsigned int mem_size; + const char *mem_size_str = NULL; #if defined(CONFIG_SDRAM) mem_size = get_sdram_size(); @@ -373,53 +385,37 @@ int load_kernel(struct image_info *image) void (*kernel_entry)(int zero, int arch, unsigned int params); - bootargs = board_override_cmd_line(); - if (sizeof(cmdline_buf) < 10 + strlen(bootargs)){ - dbg_very_loud("\nKERNEL: buffer for bootargs is too small\n\n"); - return -1; - } switch(mem_size){ case 0x800000: - memcpy(cmdline_buf, "mem=8M ", 7); - memcpy(&cmdline_buf[7], bootargs, strlen(bootargs)); + mem_size_str = "mem=8M"; break; case 0x1000000: - memcpy(cmdline_buf, "mem=16M ", 8); - memcpy(&cmdline_buf[8], bootargs, strlen(bootargs)); + mem_size_str = "mem=16M"; break; case 0x2000000: - memcpy(cmdline_buf, "mem=32M ", 8); - memcpy(&cmdline_buf[8], bootargs, strlen(bootargs)); + mem_size_str = "mem=32M"; break; case 0x4000000: - memcpy(cmdline_buf, "mem=64M ", 8); - memcpy(&cmdline_buf[8], bootargs, strlen(bootargs)); + mem_size_str = "mem=64M"; break; case 0x8000000: - memcpy(cmdline_buf, "mem=128M ", 9); - memcpy(&cmdline_buf[9], bootargs, strlen(bootargs)); + mem_size_str = "mem=128M"; break; case 0x10000000: - memcpy(cmdline_buf, "mem=256M ", 9); - memcpy(&cmdline_buf[9], bootargs, strlen(bootargs)); + mem_size_str = "mem=256M"; break; case 0x20000000: - memcpy(cmdline_buf, "mem=512M ", 9); - memcpy(&cmdline_buf[9], bootargs, strlen(bootargs)); + mem_size_str = "mem=512M"; break; default: dbg_very_loud("\nKERNEL: bootargs incorrect due to the memory size is not a multiple of MB\n\n"); break; } - bootargs = cmdline_buf; ret = load_kernel_image(image); if (ret) return ret; -#ifdef CONFIG_OVERRIDE_CMDLINE_FROM_EXT_FILE - bootargs = board_override_cmd_line_ext(image->cmdline_args); -#endif #if defined(CONFIG_SECURE) ret = secure_check(image->dest); if (ret) @@ -427,6 +423,42 @@ int load_kernel(struct image_info *image) image->dest += sizeof(at91_secure_header_t); #endif +#ifdef CONFIG_IMG_FIT + if (!check_dt_blob_valid(image->dest)) + deploy_fit_image(image->dest, image, NULL); +#endif + + int total_bootargs_sz; + const char *base_bootargs; + + base_bootargs = board_override_cmd_line(); + if (strlen(base_bootargs) == 0) { + /* look for bootargs in the DT */ + base_bootargs = bootargs_from_dt(image->of_dest); + if (base_bootargs) + dbg_loud("Using bootargs from the device-tree\n"); + } + + total_bootargs_sz = (mem_size_str ? strlen(mem_size_str) + 1 /* for the separator ' ' */ : 0) + + (base_bootargs ? strlen(base_bootargs) : 0) + 1 /* for the terminal '\0' */; + + if (sizeof(cmdline_buf) < total_bootargs_sz ){ + dbg_info("\nKERNEL: buffer for bootargs is too small\n\n"); + return -1; + } + + if (mem_size_str) + strcpy(cmdline_buf, mem_size_str); + if (strlen(base_bootargs)) { + strcat(cmdline_buf, " "); + strcat(cmdline_buf, base_bootargs); + } + bootargs = cmdline_buf; + +#ifdef CONFIG_OVERRIDE_CMDLINE_FROM_EXT_FILE + bootargs = board_override_cmd_line_ext(image->cmdline_args); +#endif + #ifdef CONFIG_SCLK slowclk_switch_osc32(); #endif @@ -441,7 +473,7 @@ int load_kernel(struct image_info *image) kernel_entry = (void (*)(int, int, unsigned int))entry_point; #ifdef CONFIG_OF_LIBFDT - ret = setup_dt_blob((char *)image->of_dest); + ret = setup_dt_blob((char *)image->of_dest, image); if (ret) return ret; @@ -454,6 +486,13 @@ int load_kernel(struct image_info *image) r2 = (unsigned int)(AT91C_BASE_DDRCS + 0x100); #endif +#ifdef CONFIG_CACHES + dcache_disable(); +#endif +#ifdef CONFIG_MMU + mmu_disable(); +#endif + dbg_info("\nKERNEL: Starting linux kernel ..., machid: %x\n\n", mach_type); #if defined(CONFIG_ENTER_NWD) diff --git a/driver/nandflash.c b/driver/nandflash.c index 67b8cf117..9cea0d9a7 100644 --- a/driver/nandflash.c +++ b/driver/nandflash.c @@ -1362,6 +1362,9 @@ static int update_image_length(struct nand_info *nand, int load_nandflash(struct image_info *image) { +#if defined(CONFIG_LOAD_LINUX) || defined(CONFIG_LOAD_ANDROID) + int length; +#endif struct nand_info nand; int ret; @@ -1384,8 +1387,21 @@ int load_nandflash(struct image_info *image) dbg_info("NAND: Using Software ECC\n"); #endif +#ifdef CONFIG_IMG_FIT + length = update_image_length(&nand, + image->offset, image->dest, DT_BLOB); + if (length > 0) { + image->length = length; + + dbg_info("NAND: FIT image: Loading %x bytes from %x to %x\n", + image->length, image->offset, image->dest); + + return nand_loadimage(&nand, image->offset, image->length, image->dest); + } +#endif + #if defined(CONFIG_LOAD_LINUX) || defined(CONFIG_LOAD_ANDROID) - int length = update_image_length(&nand, + length = update_image_length(&nand, image->offset, image->dest, KERNEL_IMAGE); if (length == -1) return -1; diff --git a/driver/spi_flash/spi_flash.c b/driver/spi_flash/spi_flash.c index a9b332dd3..73970164f 100644 --- a/driver/spi_flash/spi_flash.c +++ b/driver/spi_flash/spi_flash.c @@ -337,6 +337,27 @@ int spi_flash_loadimage(struct spi_flash *flash, struct image_info *image) return 0; #else /* CONFIG_QSPI_XIP */ +#ifdef CONFIG_IMG_FIT + length = update_image_length(flash, + image->offset, image->dest, DT_BLOB); + if (length > 0) { + image->length = length; + + dbg_info("SF: Copy FIT %x bytes from %x to %x\n", + image->length, image->offset, image->dest); + + ret = spi_flash_read(flash, + image->offset, + image->length, + image->dest); + if (ret) { + dbg_info("** SF: Serial flash read error**\n"); + ret = -1; + goto err_exit; + } + } +#endif + #if defined(CONFIG_LOAD_LINUX) || defined(CONFIG_LOAD_ANDROID) length = update_image_length(flash, image->offset, diff --git a/include/common.h b/include/common.h index 93b2bc64c..1cc2d9dc8 100644 --- a/include/common.h +++ b/include/common.h @@ -48,6 +48,8 @@ struct image_info unsigned int of_offset; unsigned int of_length; #endif + void *initrd_dest; + unsigned int initrd_length; #ifdef CONFIG_SDCARD char *of_filename; #endif diff --git a/include/fdt.h b/include/fdt.h index c05dbc11a..74d26f642 100644 --- a/include/fdt.h +++ b/include/fdt.h @@ -7,9 +7,11 @@ #ifndef __FDT_H__ #define __FDT_H__ +extern const char *bootargs_from_dt(void *blob); +extern int deploy_fit_image(void *blob, struct image_info *image, const char *configuration_name); extern unsigned int of_get_dt_total_size(void *blob); extern int check_dt_blob_valid(void *blob); -extern int fixup_chosen_node(void *blob, char *bootargs); +extern int fixup_chosen_node(void *blob, char *bootargs, struct image_info *image); extern int fixup_memory_node(void *blob, unsigned int *mem_bank, unsigned int *mem_bank2, diff --git a/lib/fdt.c b/lib/fdt.c index aa20426c9..8609c6902 100644 --- a/lib/fdt.c +++ b/lib/fdt.c @@ -32,6 +32,13 @@ struct boot_param_header { unsigned int dt_struct_len; }; +struct fdt_property { + unsigned int be_tag; + unsigned int be_len; + unsigned int be_nameoff; + char data[0]; +}; + static inline unsigned int of_get_magic_number(void *blob) { struct boot_param_header *header = (struct boot_param_header *)blob; @@ -227,21 +234,15 @@ static int of_get_nextnode_offset(void *blob, return 0; } -static int of_get_node_offset(void *blob, const char *name, int *offset) +static int __of_get_node_offset(void *blob, const char *name, int start_offset, int *offset) { - int start_offset = 0; int nodeoffset = 0; int nextoffset = 0; int depth = 0; - unsigned int token; unsigned int namelen = strlen(name); char *nodename; int ret; - /* find the root node*/ - ret = of_get_token_nextoffset(blob, 0, &start_offset, &token); - if (ret) - return -1; while (1) { ret = of_get_nextnode_offset(blob, start_offset, @@ -266,6 +267,20 @@ static int of_get_node_offset(void *blob, const char *name, int *offset) return 0; } + +static int of_get_node_offset(void *blob, const char *name, int *offset) +{ + int ret; + int start_offset = 0; + unsigned int token; + /* find the root node*/ + ret = of_get_token_nextoffset(blob, 0, &start_offset, &token); + if (ret) + return -1; + + return __of_get_node_offset(blob, name, start_offset, offset); +} + /* -------------------------------------------------------- */ static int of_blob_move_dt_struct(void *blob, @@ -541,9 +556,10 @@ int check_dt_blob_valid(void *blob) * property "bootargs": This zero-terminated string is passed * as the kernel command line. */ -int fixup_chosen_node(void *blob, char *bootargs) +int fixup_chosen_node(void *blob, char *bootargs, struct image_info *image) { int nodeoffset; + unsigned int value_u32; char *value = bootargs; int valuelen = strlen(value) + 1; int ret; @@ -558,12 +574,31 @@ int fixup_chosen_node(void *blob, char *bootargs) * if the property doesn't exit, add it * if the property exists, update it. */ - ret = of_set_property(blob, nodeoffset, "bootargs", value, valuelen); - if (ret) { - dbg_info("fail to set bootargs property\n"); - return ret; + if (bootargs) { + ret = of_set_property(blob, nodeoffset, "bootargs", value, valuelen); + if (ret) { + dbg_info("fail to set bootargs property\n"); + return ret; + } } + if (image->initrd_length) { + value_u32 = swap_uint32((unsigned int) image->initrd_dest); + ret = of_set_property(blob, nodeoffset, "linux,initrd-start", &value_u32, sizeof(value_u32)); + if (ret) { + dbg_loud("unable to set linux,initrd-start\n"); + return ret; + } + dbg_loud("linux,initrd-start = %x\n", value_u32); + + value_u32 = swap_uint32((unsigned int) image->initrd_dest + image->initrd_length); + ret = of_set_property(blob, nodeoffset, "linux,initrd-end", &value_u32, sizeof(value_u32)); + if (ret) { + dbg_loud("unable to set linux,initrd-end\n"); + return ret; + } + dbg_loud("linux,initrd-end = %x\n", value_u32); + } return 0; } @@ -617,3 +652,177 @@ int fixup_memory_node(void *blob, return 0; } + +static const void *fdt_getprop(void *blob, int nodeoffset, + const char *name, int *lenp) +{ + int rc; + int property_offset; + const struct fdt_property *prop; + + rc = of_get_property_offset_by_name(blob, nodeoffset, + name, &property_offset); + if (rc) { + dbg_loud("can't find property '%s'\n", name); + return NULL; + } + prop = (struct fdt_property *) of_dt_struct_offset(blob, property_offset); + if(lenp) + *lenp = swap_uint32(prop->be_len); + return prop->data; +} + +const char *bootargs_from_dt(void *blob) +{ + int nodeoffset; + int ret; + + ret = of_get_node_offset(blob, "chosen", &nodeoffset); + if (ret) + return NULL; + + return (const char *)fdt_getprop(blob, nodeoffset, "bootargs", NULL); +} + +#ifdef CONFIG_IMG_FIT + +struct dt_img_info { + unsigned int load; + unsigned int entry; + const void *data; + int data_len; + const char *type; + const char *desc; +}; + +static int get_image_info_from_node(void *blob, int node_offset, struct dt_img_info *info) +{ + const unsigned int *p; + + info->data_len = 0; + info->desc = fdt_getprop(blob, node_offset, "description", NULL); + info->type = fdt_getprop(blob, node_offset, "type", NULL); + info->data = fdt_getprop(blob, node_offset, "data", &info->data_len); + p = fdt_getprop(blob, node_offset, "load", NULL); + info->load = p ? swap_uint32(*p) : (unsigned int) -1; + p = fdt_getprop(blob, node_offset, "entry", NULL); + info->entry = p ? swap_uint32(*p) : (unsigned int) -1; + + return 0; +} + +static inline void fast_and_unprecise_memset(unsigned int *dest, unsigned int w, unsigned l) +{ + l = l / 16; + dest = (unsigned int*)((unsigned int)dest | 3) + 1; + while (l--) { + dest[0] = w; + dest[1] = w; + dest[2] = w; + dest[3] = w; + dest += 4; + } +} + +int deploy_fit_image(void *blob, struct image_info *image, const char *configuration_name) +{ + int rc; + int configurations_node_offset; + int images_node_offset; + int conf_node_offset; + int property_offset = 0; + int nextoffset, startoffset; + const char *conf_desc; + void *fit_addr = image->dest; + unsigned int fit_length = image->length; + + + rc = check_dt_blob_valid(blob); + if (rc) { + dbg_info("invalid FDT blob\n"); + return -1; + } + + rc = of_get_node_offset(blob, "configurations", &configurations_node_offset); + if (rc) { + dbg_info("can't find 'configurations' node\n"); + return -1; + } + + rc = of_get_node_offset(blob, "images", &images_node_offset); + if (rc) { + dbg_info("can't find 'images'' node\n"); + return -1; + } + + if (!configuration_name) + configuration_name = fdt_getprop(blob, configurations_node_offset, "default", NULL); + + if (!configuration_name) { + dbg_info("No configuration name provided or found in ITB\n"); + return -1; + } + + rc = __of_get_node_offset(blob, configuration_name, configurations_node_offset, &conf_node_offset); + if (rc) { + dbg_info("can't find configuration node '%s'\n", configuration_name); + return -1; + } + + conf_desc = fdt_getprop(blob, conf_node_offset, "description", NULL); + if (conf_desc) + dbg_info("chosen configuration is '%s'\n", conf_desc); + + + // iterate over all the images found in the selected configuration node + nextoffset = conf_node_offset; + while ((startoffset = nextoffset) != 0) { + struct dt_img_info info; + const struct fdt_property *prop; + int img_node_offset; + rc = of_get_next_property_offset(blob, startoffset, + &property_offset, &nextoffset); + if (rc) + break; + + prop = (const struct fdt_property *)of_dt_struct_offset(blob, property_offset); + rc = __of_get_node_offset(blob, prop->data, images_node_offset, &img_node_offset); + if (rc) { + // not fiding a node may be perfectly fine. it could just be that the + // property is a string or whatever. Continue with the next property. + continue; + } + + get_image_info_from_node(blob, img_node_offset, &info); + dbg_loud("description : %s\n", info.desc ? info.desc : "-"); + dbg_loud("type : %s\n", info.type ? info.type : "-"); + dbg_loud("data : %x (len %x)\n", info.data, info.data_len); + dbg_loud("load : %x\n", info.load); + dbg_loud("entry : %x\n", info.entry); + + if (!info.type) + continue; + + if (strcmp(info.type, "flat_dt") == 0) { + image->of_dest = (void*) info.load; + image->of_length = info.data_len; + } else if (strcmp(info.type, "kernel") == 0) { + image->dest = (void*) info.load; + image->length = info.data_len; + } else if (strcmp(info.type, "ramdisk") == 0) { + image->initrd_dest = (void*) info.load; + image->initrd_length = info.data_len; + } else { + continue; + } + dbg_loud("copying '%s' (type: %s): %x -> %x (%x bytes)\n", info.desc ? info.desc : "-", info.type, info.data, info.load, info.data_len); + memcpy((void*) info.load, info.data, info.data_len); + } + + // Clean-up the FIT image memory just in case it contains some sensitive data (secure boot) + dbg_loud("zeroing fit img (%x -> %x)\n", fit_addr, fit_addr + fit_length); + fast_and_unprecise_memset(fit_addr, 0, fit_length); + + return 0; +} +#endif