summaryrefslogtreecommitdiffstats
path: root/arch/ppc/lib
diff options
context:
space:
mode:
authorRenaud Barbier <renaud.barbier@ge.com>2013-09-06 11:53:12 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2013-09-09 17:08:39 +0200
commit9d2745425e554c61a8cadf48e10de2220a16df19 (patch)
treea4ebf47e60ffede86d117e6c2e9de8871de3acd1 /arch/ppc/lib
parent91a68050e1607ca53f9733b90a43a0b473bce58e (diff)
downloadbarebox-9d2745425e554c61a8cadf48e10de2220a16df19.tar.gz
barebox-9d2745425e554c61a8cadf48e10de2220a16df19.tar.xz
ppc: bootm: relocate fdt to valid boot memory
For the MPC85xx family of SOCs Linux expects any boot firmware information to be passed in the first 64MiB of memory. This adds support to ensure that the device tree is relocated to a valid location if it is outside that address range. For the other SOC family currently present in the ppc architecture, the default is not to relocate as at Linux startup the virtual address equals the physical address. Signed-off-by: Renaud Barbier <renaud.barbier@ge.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/ppc/lib')
-rw-r--r--arch/ppc/lib/ppclinux.c49
1 files changed, 48 insertions, 1 deletions
diff --git a/arch/ppc/lib/ppclinux.c b/arch/ppc/lib/ppclinux.c
index ef69eadfb7..7c30ac3386 100644
--- a/arch/ppc/lib/ppclinux.c
+++ b/arch/ppc/lib/ppclinux.c
@@ -4,12 +4,45 @@
#include <command.h>
#include <image.h>
#include <init.h>
+#include <malloc.h>
#include <environment.h>
#include <asm/bitops.h>
+#include <asm/processor.h>
#include <boot.h>
#include <errno.h>
#include <fs.h>
+static int bootm_relocate_fdt(void *addr, struct image_data *data)
+{
+ if (addr < LINUX_TLB1_MAX_ADDR) {
+ /* The kernel is within the boot TLB mapping.
+ * Put the DTB above if there is no space
+ * below.
+ */
+ if (addr < (void *)data->oftree->totalsize) {
+ addr = (void *)PAGE_ALIGN((phys_addr_t)addr +
+ data->os->header.ih_size);
+ addr += data->oftree->totalsize;
+ if (addr < LINUX_TLB1_MAX_ADDR)
+ addr = LINUX_TLB1_MAX_ADDR;
+ }
+ }
+
+ if (addr > LINUX_TLB1_MAX_ADDR) {
+ pr_crit("Unable to relocate DTB to Linux TLB\n");
+ return 1;
+ }
+
+ addr = (void *)PAGE_ALIGN_DOWN((phys_addr_t)addr -
+ data->oftree->totalsize);
+ memcpy(addr, data->oftree, data->oftree->totalsize);
+ free(data->oftree);
+ data->oftree = addr;
+
+ pr_info("Relocating device tree to 0x%p\n", addr);
+ return 0;
+}
+
static int do_bootm_linux(struct image_data *data)
{
void (*kernel)(void *, void *, unsigned long,
@@ -24,6 +57,20 @@ static int do_bootm_linux(struct image_data *data)
return -EINVAL;
}
+ /* Relocate the device tree if outside the initial
+ * Linux mapped TLB.
+ */
+ if (IS_ENABLED(CONFIG_MPC85xx)) {
+ void *addr = data->oftree;
+
+ if ((addr + data->oftree->totalsize) > LINUX_TLB1_MAX_ADDR) {
+ addr = (void *)data->os_address;
+
+ if (bootm_relocate_fdt(addr, data))
+ goto error;
+ }
+ }
+
fdt_add_reserve_map(data->oftree);
kernel = (void *)(data->os_address + data->os_entry);
@@ -40,7 +87,7 @@ static int do_bootm_linux(struct image_data *data)
reset_cpu(0);
- /* not reached */
+error:
return -1;
}