summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/lib/ppclinux.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/lib/ppclinux.c')
-rw-r--r--arch/powerpc/lib/ppclinux.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/arch/powerpc/lib/ppclinux.c b/arch/powerpc/lib/ppclinux.c
new file mode 100644
index 0000000000..9b8404962c
--- /dev/null
+++ b/arch/powerpc/lib/ppclinux.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define DEBUG
+
+#include <common.h>
+#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 <bootm.h>
+#include <errno.h>
+#include <restart.h>
+#include <fs.h>
+
+static struct fdt_header *bootm_relocate_fdt(struct image_data *data,
+ struct fdt_header *fdt)
+{
+ void *os = (void *)data->os_address;
+ void *newfdt;
+
+ if (os < LINUX_TLB1_MAX_ADDR) {
+ /* The kernel is within the boot TLB mapping.
+ * Put the DTB above if there is no space
+ * below.
+ */
+ if (os < (void *)fdt->totalsize) {
+ os = (void *)PAGE_ALIGN((phys_addr_t)os +
+ data->os->header.ih_size);
+ os += fdt->totalsize;
+ if (os < LINUX_TLB1_MAX_ADDR)
+ os = LINUX_TLB1_MAX_ADDR;
+ }
+ }
+
+ if (os > LINUX_TLB1_MAX_ADDR) {
+ pr_crit("Unable to relocate DTB to Linux TLB\n");
+ return NULL;
+ }
+
+ newfdt = (void *)PAGE_ALIGN_DOWN((phys_addr_t)os - fdt->totalsize);
+ memcpy(newfdt, fdt, fdt->totalsize);
+ free(fdt);
+
+ pr_info("Relocating device tree to 0x%p\n", newfdt);
+ return newfdt;
+}
+
+static int do_bootm_linux(struct image_data *data)
+{
+ void (*kernel)(void *, void *, unsigned long,
+ unsigned long, unsigned long);
+ int ret;
+ struct fdt_header *fdt;
+
+ ret = bootm_load_os(data, data->os_address);
+ if (ret)
+ return ret;
+
+ fdt = of_get_fixed_tree(data->of_root_node);
+ if (!fdt) {
+ pr_err("bootm: No devicetree given.\n");
+ return -EINVAL;
+ }
+
+ if (data->dryrun)
+ return 0;
+
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
+ /* Relocate the device tree if outside the initial
+ * Linux mapped TLB.
+ */
+ if (IS_ENABLED(CONFIG_MPC85xx)) {
+ if (((void *)fdt + fdt->totalsize) > LINUX_TLB1_MAX_ADDR) {
+ fdt = bootm_relocate_fdt(data, fdt);
+ if (!fdt)
+ goto error;
+ }
+ }
+
+ fdt_add_reserve_map(fdt);
+
+ kernel = (void *)(data->os_address + data->os_entry);
+
+ /*
+ * Linux Kernel Parameters (passing device tree):
+ * r3: ptr to OF flat tree, followed by the board info data
+ * r4: physical pointer to the kernel itself
+ * r5: NULL
+ * r6: NULL
+ * r7: NULL
+ */
+ kernel(fdt, kernel, 0, 0, 0);
+
+ restart_machine();
+
+error:
+ return -1;
+}
+
+static struct image_handler handler = {
+ .name = "PowerPC Linux",
+ .bootm = do_bootm_linux,
+ .filetype = filetype_uimage,
+ .ih_os = IH_OS_LINUX,
+};
+
+static int ppclinux_register_image_handler(void)
+{
+ return register_image_handler(&handler);
+}
+
+late_initcall(ppclinux_register_image_handler);