summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-02-03 09:52:34 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2014-02-03 09:52:34 +0100
commitca10989374ffb6f08fde864a95c623fc7557b8e8 (patch)
treef87522919e6b55c7f7dcab5bc4e9e4e37eaa2a49
parentfd6fe68e95bb418456973cfa4b1760d9060ba4df (diff)
parent0c66005aeaeb85e247e715bd18ac3b621481e425 (diff)
downloadbarebox-ca10989374ffb6f08fde864a95c623fc7557b8e8.tar.gz
barebox-ca10989374ffb6f08fde864a95c623fc7557b8e8.tar.xz
Merge branch 'for-next/bootm'
-rw-r--r--arch/arm/lib/bootm.c173
-rw-r--r--arch/blackfin/lib/blackfin_linux.c6
-rw-r--r--arch/nios2/lib/bootm.c6
-rw-r--r--arch/ppc/lib/ppclinux.c6
-rw-r--r--common/bootm.c150
-rw-r--r--include/boot.h5
-rw-r--r--include/linux/list.h11
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.