diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2014-02-03 09:52:34 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2014-02-03 09:52:34 +0100 |
commit | ca10989374ffb6f08fde864a95c623fc7557b8e8 (patch) | |
tree | f87522919e6b55c7f7dcab5bc4e9e4e37eaa2a49 | |
parent | fd6fe68e95bb418456973cfa4b1760d9060ba4df (diff) | |
parent | 0c66005aeaeb85e247e715bd18ac3b621481e425 (diff) | |
download | barebox-ca10989374ffb6f08fde864a95c623fc7557b8e8.tar.gz barebox-ca10989374ffb6f08fde864a95c623fc7557b8e8.tar.xz |
Merge branch 'for-next/bootm'
-rw-r--r-- | arch/arm/lib/bootm.c | 173 | ||||
-rw-r--r-- | arch/blackfin/lib/blackfin_linux.c | 6 | ||||
-rw-r--r-- | arch/nios2/lib/bootm.c | 6 | ||||
-rw-r--r-- | arch/ppc/lib/ppclinux.c | 6 | ||||
-rw-r--r-- | common/bootm.c | 150 | ||||
-rw-r--r-- | include/boot.h | 5 | ||||
-rw-r--r-- | include/linux/list.h | 11 |
7 files changed, 288 insertions, 69 deletions
diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index c0e4e15ea6..c63cbc02e3 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -22,45 +22,61 @@ #include <asm/armlinux.h> #include <asm/system.h> -static int __do_bootm_linux(struct image_data *data, int swap) +/* + * sdram_start_and_size() - determine place for putting the kernel/oftree/initrd + * + * @start: returns the start address of the first RAM bank + * @size: returns the usable space at the beginning of the first RAM bank + * + * This function returns the base address of the first RAM bank and the free + * space found there. + * + * return: 0 for success, negative error code otherwise + */ +static int sdram_start_and_size(unsigned long *start, unsigned long *size) { - unsigned long kernel; - unsigned long initrd_start = 0, initrd_size = 0, initrd_end = 0; struct memory_bank *bank; - unsigned long load_address; + struct resource *res; - if (data->os_res) { - load_address = data->os_res->start; - } else if (data->os_address != UIMAGE_INVALID_ADDRESS) { - load_address = data->os_address; - } else { - bank = list_first_entry(&memory_banks, - struct memory_bank, list); - load_address = bank->start + SZ_32K; - if (bootm_verbose(data)) - printf("no os load address, defaulting to 0x%08lx\n", - load_address); + /* + * We use the first memory bank for the kernel and other resources + */ + bank = list_first_entry_or_null(&memory_banks, struct memory_bank, + list); + if (!bank) { + printf("cannot find first memory bank\n"); + return -EINVAL; } - if (!data->os_res && data->os) { - data->os_res = uimage_load_to_sdram(data->os, - data->os_num, load_address); - if (!data->os_res) - return -ENOMEM; - } + /* + * If the first memory bank has child resources we can use the bank up + * to the beginning of the first child resource, otherwise we can use + * the whole bank. + */ + res = list_first_entry_or_null(&bank->res->children, struct resource, + sibling); + if (res) + *size = res->start - bank->start; + else + *size = bank->size; - if (!data->os_res) { - data->os_res = file_to_sdram(data->os_file, load_address); - if (!data->os_res) - return -ENOMEM; - } + *start = bank->start; + + return 0; +} + +static int __do_bootm_linux(struct image_data *data, unsigned long free_mem, int swap) +{ + unsigned long kernel; + unsigned long initrd_start = 0, initrd_size = 0, initrd_end = 0; + int ret; kernel = data->os_res->start + data->os_entry; initrd_start = data->initrd_address; - if (data->initrd_file && initrd_start == UIMAGE_INVALID_ADDRESS) { - initrd_start = data->os_res->start + SZ_8M; + if (initrd_start == UIMAGE_INVALID_ADDRESS) { + initrd_start = PAGE_ALIGN(free_mem); if (bootm_verbose(data)) { printf("no initrd load address, defaulting to 0x%08lx\n", @@ -68,33 +84,20 @@ static int __do_bootm_linux(struct image_data *data, int swap) } } - if (data->initrd) { - data->initrd_res = uimage_load_to_sdram(data->initrd, - data->initrd_num, initrd_start); - if (!data->initrd_res) - return -ENOMEM; - } else if (data->initrd_file) { - data->initrd_res = file_to_sdram(data->initrd_file, initrd_start); - if (!data->initrd_res) - return -ENOMEM; - } + ret = bootm_load_initrd(data, initrd_start); + if (ret) + return ret; if (data->initrd_res) { initrd_start = data->initrd_res->start; initrd_end = data->initrd_res->end; initrd_size = resource_size(data->initrd_res); + free_mem = PAGE_ALIGN(initrd_end); } - if (IS_ENABLED(CONFIG_OFTREE) && data->of_root_node) { - of_add_initrd(data->of_root_node, initrd_start, initrd_end); - if (initrd_end) - of_add_reserve_entry(initrd_start, initrd_end); - data->oftree = of_get_fixed_tree(data->of_root_node); - fdt_add_reserve_map(data->oftree); - of_print_cmdline(data->of_root_node); - if (bootm_verbose(data) > 1) - of_print_nodes(data->of_root_node, 0); - } + ret = bootm_load_devicetree(data, free_mem); + if (ret) + return ret; if (bootm_verbose(data)) { printf("\nStarting kernel at 0x%08lx", kernel); @@ -114,7 +117,36 @@ static int __do_bootm_linux(struct image_data *data, int swap) static int do_bootm_linux(struct image_data *data) { - return __do_bootm_linux(data, 0); + unsigned long load_address, mem_start, mem_size, mem_free; + int ret; + + ret = sdram_start_and_size(&mem_start, &mem_size); + if (ret) + return ret; + + load_address = data->os_address; + + if (load_address == UIMAGE_INVALID_ADDRESS) { + load_address = mem_start + SZ_32K; + if (bootm_verbose(data)) + printf("no os load address, defaulting to 0x%08lx\n", + load_address); + } + + ret = bootm_load_os(data, load_address); + if (ret) + return ret; + + /* + * Put devicetree/initrd at maximum to 128MiB into RAM to not + * risk to put it outside of lowmem. + */ + if (mem_size > SZ_256M) + mem_free = mem_start + SZ_128M; + else + mem_free = PAGE_ALIGN(data->os_res->end + SZ_1M); + + return __do_bootm_linux(data, mem_free, 0); } static struct image_handler uimage_handler = { @@ -207,11 +239,23 @@ static int do_bootz_linux(struct image_data *data) void *zimage; u32 end; unsigned long load_address = data->os_address; + unsigned long mem_start, mem_size, mem_free; + + ret = sdram_start_and_size(&mem_start, &mem_size); + if (ret) + return ret; if (load_address == UIMAGE_INVALID_ADDRESS) { - struct memory_bank *bank = list_first_entry(&memory_banks, - struct memory_bank, list); - data->os_address = bank->start + SZ_8M; + /* + * The kernel should stay in the first 128MiB of RAM, recommended + * is 32MiB into RAM so that relocation prior to decompression + * can be avoided. + */ + if (mem_size > SZ_64M) + data->os_address = mem_start + SZ_32M; + else + data->os_address = mem_start + SZ_8M; + load_address = data->os_address; if (bootm_verbose(data)) printf("no os load address, defaulting to 0x%08lx\n", @@ -280,7 +324,17 @@ static int do_bootz_linux(struct image_data *data) goto err_out; close(fd); - return __do_bootm_linux(data, swap); + + /* + * Put devicetree/initrd at maximum to 128MiB into RAM to not + * risk to put it outside of lowmem. + */ + if (mem_size > SZ_256M) + mem_free = mem_start + SZ_128M; + else + mem_free = PAGE_ALIGN(data->os_res->end + SZ_1M); + + return __do_bootm_linux(data, mem_free, swap); err_out: close(fd); @@ -355,6 +409,7 @@ static int do_bootm_aimage(struct image_data *data) void *buf; int to_read; struct android_header_comp *cmp; + unsigned long mem_free; fd = open(data->os_file, O_RDONLY); if (fd < 0) { @@ -448,7 +503,17 @@ static int do_bootm_aimage(struct image_data *data) } close(fd); - return __do_bootm_linux(data, 0); + + /* + * Put devicetree right after initrd if present or after the kernel + * if not. + */ + if (data->initrd_res) + mem_free = PAGE_ALIGN(data->initrd_res->end); + else + mem_free = PAGE_ALIGN(data->os_res->end + SZ_1M); + + return __do_bootm_linux(data, mem_free, 0); err_out: linux_bootargs_overwrite(NULL); diff --git a/arch/blackfin/lib/blackfin_linux.c b/arch/blackfin/lib/blackfin_linux.c index bb3c7745ea..2561a7e152 100644 --- a/arch/blackfin/lib/blackfin_linux.c +++ b/arch/blackfin/lib/blackfin_linux.c @@ -41,9 +41,11 @@ static int do_bootm_linux(struct image_data *idata) int (*appl)(char *cmdline); const char *cmdline = linux_bootargs_get(); char *cmdlinedest = (char *) CMD_LINE_ADDR; + int ret; - if (!idata->os_res) - return -EINVAL; + ret = bootm_load_os(idata, idata->os_address); + if (ret) + return ret; appl = (void *)(idata->os_address + idata->os_entry); printf("Starting Kernel at 0x%p\n", appl); diff --git a/arch/nios2/lib/bootm.c b/arch/nios2/lib/bootm.c index cc96290b8d..77da119bde 100644 --- a/arch/nios2/lib/bootm.c +++ b/arch/nios2/lib/bootm.c @@ -36,9 +36,11 @@ static int do_bootm_linux(struct image_data *idata) { void (*kernel)(int, int, int, const char *); const char *commandline = linux_bootargs_get(); + int ret; - if (!idata->os_res) - return -EINVAL; + ret = bootm_load_os(idata, idata->os_address); + if (ret) + return ret; kernel = (void *)(idata->os_address + idata->os_entry); diff --git a/arch/ppc/lib/ppclinux.c b/arch/ppc/lib/ppclinux.c index 7c30ac3386..e25efecd43 100644 --- a/arch/ppc/lib/ppclinux.c +++ b/arch/ppc/lib/ppclinux.c @@ -47,9 +47,11 @@ static int do_bootm_linux(struct image_data *data) { void (*kernel)(void *, void *, unsigned long, unsigned long, unsigned long); + int ret; - if (!data->os_res) - return -EINVAL; + ret = bootm_load_os(data, data->os_address); + if (ret) + return ret; data->oftree = of_get_fixed_tree(data->of_root_node); if (!data->oftree) { diff --git a/common/bootm.c b/common/bootm.c index 2da6e59129..d62d011e2a 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -45,6 +45,145 @@ static struct image_handler *bootm_find_handler(enum filetype filetype, return NULL; } +/* + * bootm_load_os() - load OS to RAM + * + * @data: image data context + * @load_address: The address where the OS should be loaded to + * + * This loads the OS to a RAM location. load_address must be a valid + * address. If the image_data doesn't have a OS specified it's considered + * an error. + * + * Return: 0 on success, negative error code otherwise + */ +int bootm_load_os(struct image_data *data, unsigned long load_address) +{ + if (data->os_res) + return 0; + + if (load_address == UIMAGE_INVALID_ADDRESS) + return -EINVAL; + + if (data->os) { + data->os_res = uimage_load_to_sdram(data->os, + data->os_num, load_address); + if (!data->os_res) + return -ENOMEM; + + return 0; + } + + if (data->os_file) { + data->os_res = file_to_sdram(data->os_file, load_address); + if (!data->os_res) + return -ENOMEM; + + return 0; + } + + return -EINVAL; +} + +/* + * bootm_load_initrd() - load initrd to RAM + * + * @data: image data context + * @load_address: The address where the initrd should be loaded to + * + * This loads the initrd to a RAM location. load_address must be a valid + * address. If the image_data doesn't have a initrd specified this function + * still returns successful as an initrd is optional. Check data->initrd_res + * to see if an initrd has been loaded. + * + * Return: 0 on success, negative error code otherwise + */ +int bootm_load_initrd(struct image_data *data, unsigned long load_address) +{ + if (data->initrd_res) + return 0; + + if (data->initrd) { + data->initrd_res = uimage_load_to_sdram(data->initrd, + data->initrd_num, load_address); + if (!data->initrd_res) + return -ENOMEM; + + return 0; + } + + if (data->initrd_file) { + data->initrd_res = file_to_sdram(data->initrd_file, load_address); + if (!data->initrd_res) + return -ENOMEM; + + return 0; + } + + return 0; +} + +/* + * bootm_load_devicetree() - load devicetree + * + * @data: image data context + * @load_address: The address where the devicetree should be loaded to + * + * This loads the devicetree to a RAM location. load_address must be a valid + * address. The resulting devicetree will be found at data->oftree. + * + * Return: 0 on success, negative error code otherwise + */ +int bootm_load_devicetree(struct image_data *data, unsigned long load_address) +{ + int fdt_size; + struct fdt_header *oftree; + + if (data->oftree) + return 0; + + if (!IS_ENABLED(CONFIG_OFTREE)) + return 0; + + if (!data->of_root_node) + return 0; + + if (data->initrd_res) { + of_add_initrd(data->of_root_node, data->initrd_res->start, + data->initrd_res->end); + of_add_reserve_entry(data->initrd_res->start, data->initrd_res->end); + } + + oftree = of_get_fixed_tree(data->of_root_node); + if (!oftree) + return -EINVAL; + + fdt_size = be32_to_cpu(oftree->totalsize); + + data->oftree_res = request_sdram_region("oftree", load_address, + fdt_size); + if (!data->oftree_res) { + free(oftree); + return -ENOMEM; + } + + memcpy((void *)data->oftree_res->start, oftree, fdt_size); + + free(oftree); + + oftree = (void *)data->oftree_res->start; + + fdt_add_reserve_map(oftree); + + of_print_cmdline(data->of_root_node); + if (bootm_verbose(data) > 1) + of_print_nodes(data->of_root_node, 0); + + data->oftree = oftree; + + return 0; +} + static int bootm_open_os_uimage(struct image_data *data) { int ret; @@ -75,15 +214,6 @@ static int bootm_open_os_uimage(struct image_data *data) if (data->os_address == UIMAGE_SOME_ADDRESS) data->os_address = data->os->header.ih_load; - if (data->os_address != UIMAGE_INVALID_ADDRESS) { - data->os_res = uimage_load_to_sdram(data->os, 0, - data->os_address); - if (!data->os_res) { - uimage_close(data->os); - return -ENOMEM; - } - } - return 0; } @@ -343,6 +473,8 @@ err_out: release_sdram_region(data->os_res); if (data->initrd_res) release_sdram_region(data->initrd_res); + if (data->oftree_res) + release_sdram_region(data->oftree_res); if (data->initrd && data->initrd != data->os) uimage_close(data->initrd); if (data->os) diff --git a/include/boot.h b/include/boot.h index 56f6c359b9..bdd5477d35 100644 --- a/include/boot.h +++ b/include/boot.h @@ -61,6 +61,7 @@ struct image_data { struct device_node *of_root_node; struct fdt_header *oftree; + struct resource *oftree_res; int verify; int verbose; @@ -108,6 +109,10 @@ static inline int linux_bootargs_overwrite(const char *bootargs) } #endif +int bootm_load_os(struct image_data *data, unsigned long load_address); +int bootm_load_initrd(struct image_data *data, unsigned long load_address); +int bootm_load_devicetree(struct image_data *data, unsigned long load_address); + #define UIMAGE_SOME_ADDRESS (UIMAGE_INVALID_ADDRESS - 1) #endif /* __BOOT_H */ diff --git a/include/linux/list.h b/include/linux/list.h index 5ae90b43a3..bc63ece95d 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -292,6 +292,17 @@ static inline void list_splice_init(struct list_head *list, list_entry((head)->prev, type, member) /** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) + +/** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. |