diff --git a/xen/arch/ppc/Kconfig.debug b/xen/arch/ppc/Kconfig.debug
index e69de29bb2..608c9ff832 100644
--- a/xen/arch/ppc/Kconfig.debug
+++ b/xen/arch/ppc/Kconfig.debug
@@ -0,0 +1,5 @@
+config EARLY_PRINTK
+    bool "Enable early printk"
+    default DEBUG
+    help
+      Enables early printk debug messages
diff --git a/xen/arch/ppc/Makefile b/xen/arch/ppc/Makefile
index 530fba2121..098a4dd0a9 100644
--- a/xen/arch/ppc/Makefile
+++ b/xen/arch/ppc/Makefile
@@ -1,5 +1,7 @@
 obj-$(CONFIG_PPC64) += ppc64/
 
+obj-y += boot-of.init.o
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.init.o
 obj-y += setup.o
 
 $(TARGET): $(TARGET)-syms
diff --git a/xen/arch/ppc/boot-of.c b/xen/arch/ppc/boot-of.c
new file mode 100644
index 0000000000..a06546871e
--- /dev/null
+++ b/xen/arch/ppc/boot-of.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * This file was derived from Xen 3.2's xen/arch/powerpc/boot_of.c,
+ * originally licensed under GPL version 2 or later.
+ *
+ * Copyright IBM Corp. 2005, 2006, 2007
+ * Copyright Raptor Engineering, LLC
+ *
+ * Authors: Jimi Xenidis <jimix@watson.ibm.com>
+ *          Hollis Blanchard <hollisb@us.ibm.com>
+ *          Shawn Anastasio <sanastasio@raptorengineering.com>
+ */
+
+#include <xen/init.h>
+#include <xen/macros.h>
+#include <xen/stdarg.h>
+#include <xen/types.h>
+#include <asm/boot.h>
+#include <asm/byteorder.h>
+#include <asm/early_printk.h>
+
+/*
+ * The Open Firmware client interface is called in 32-bit mode with the MMU off,
+ * so any addresses passed to it must be physical addresses under 4GB.
+ *
+ * Since the client interface is only used during early boot before the MMU is on
+ * and Xen itself was loaded by Open Firmware (and therefore resides below 4GB),
+ * we can achieve the desired result with a simple cast to uint32_t.
+ */
+#define ADDR(x) ((uint32_t)(unsigned long)(x))
+
+/* OF entrypoint*/
+static unsigned long __initdata of_vec;
+
+static int __initdata of_out;
+
+static int __init of_call(const char *service, uint32_t nargs, uint32_t nrets,
+                          int32_t rets[], ...)
+{
+    int rc;
+    va_list args;
+    unsigned int i;
+    struct of_service s = { 0 };
+
+    s.ofs_service = cpu_to_be32(ADDR(service));
+    s.ofs_nargs = cpu_to_be32(nargs);
+    s.ofs_nrets = cpu_to_be32(nrets);
+
+    /* Copy all the params into the args array */
+    va_start(args, rets);
+
+    for ( i = 0; i < nargs; i++ )
+        s.ofs_args[i] = cpu_to_be32(va_arg(args, uint32_t));
+
+    va_end(args);
+
+    rc = enter_of(&s, of_vec);
+
+    /* Copy all return values to the output rets array */
+    for ( i = 0; i < nrets; i++ )
+        rets[i] = be32_to_cpu(s.ofs_args[i + nargs]);
+
+    return rc;
+}
+
+static int __init of_finddevice(const char *devspec)
+{
+    int32_t rets[1] = { OF_FAILURE };
+
+    of_call("finddevice", 1, ARRAY_SIZE(rets), rets, ADDR(devspec));
+    return rets[0];
+}
+
+static int __init of_getprop(int ph, const char *name, void *buf, uint32_t buflen)
+{
+    int32_t rets[1] = { OF_FAILURE };
+
+    of_call("getprop", 4, ARRAY_SIZE(rets), rets, ph, ADDR(name), ADDR(buf),
+            buflen);
+    return rets[0];
+}
+
+int __init of_write(int ih, const char *addr, uint32_t len)
+{
+    int32_t rets[1] = { OF_FAILURE };
+
+    of_call("write", 3, ARRAY_SIZE(rets), rets, ih, ADDR(addr), len);
+    return rets[0];
+}
+
+static void __init of_putchar(char c)
+{
+    if ( c == '\n' )
+    {
+        char buf = '\r';
+        of_write(of_out, &buf, 1);
+    }
+    of_write(of_out, &c, 1);
+}
+
+void __init boot_of_init(unsigned long vec)
+{
+    int bof_chosen;
+
+    of_vec = vec;
+
+    /* Get a handle to the default console */
+    bof_chosen = of_finddevice("/chosen");
+    of_getprop(bof_chosen, "stdout", &of_out, sizeof(of_out));
+    of_out = be32_to_cpu(of_out);
+
+    early_printk_init(of_putchar);
+}
diff --git a/xen/arch/ppc/configs/ppc64_defconfig b/xen/arch/ppc/configs/ppc64_defconfig
index 8783eb3488..f7cc075e45 100644
--- a/xen/arch/ppc/configs/ppc64_defconfig
+++ b/xen/arch/ppc/configs/ppc64_defconfig
@@ -10,4 +10,5 @@
 CONFIG_PPC64=y
 CONFIG_DEBUG=y
 CONFIG_DEBUG_INFO=y
+CONFIG_EARLY_PRINTK=y
 CONFIG_EXPERT=y
diff --git a/xen/arch/ppc/early_printk.c b/xen/arch/ppc/early_printk.c
new file mode 100644
index 0000000000..781f82133e
--- /dev/null
+++ b/xen/arch/ppc/early_printk.c
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <xen/init.h>
+#include <asm/boot.h>
+
+static void __initdata (*putchar_func)(char);
+
+void __init early_printk_init(void (*putchar)(char))
+{
+    putchar_func = putchar;
+}
+
+void __init early_puts(const char *s, size_t nr)
+{
+    if ( !putchar_func )
+        return;
+
+    while ( nr-- > 0 )
+        putchar_func(*s++);
+}
+
+void __init early_printk(const char *s)
+{
+    if ( !putchar_func )
+        return;
+
+    while ( *s )
+        putchar_func(*s++);
+}
diff --git a/xen/arch/ppc/include/asm/asm-defns.h b/xen/arch/ppc/include/asm/asm-defns.h
index 6ea35f6edb..9d7328f4a2 100644
--- a/xen/arch/ppc/include/asm/asm-defns.h
+++ b/xen/arch/ppc/include/asm/asm-defns.h
@@ -37,4 +37,21 @@
     .long 0xa6037b7d; /* mtsrr1 r11                         */                 \
     .long 0x2400004c  /* rfid                               */
 
+
+/* Taken from Linux kernel source (arch/powerpc/boot/crt0.S) */
+.macro OP_REGS op, width, start, end, base, offset
+	.Lreg=\start
+	.rept (\end - \start + 1)
+	\op	.Lreg,\offset+\width*.Lreg(\base)
+	.Lreg=.Lreg+1
+	.endr
+.endm
+
+#define SAVE_GPRS(start, end, base) OP_REGS std, 8, start, end, base, 0
+#define REST_GPRS(start, end, base) OP_REGS ld, 8, start, end, base, 0
+#define SAVE_GPR(n, base)           SAVE_GPRS(n, n, base)
+#define REST_GPR(n, base)           REST_GPRS(n, n, base)
+#define SAVE_NVGPRS(base)           SAVE_GPRS(14, 31, base)
+#define REST_NVGPRS(base)           REST_GPRS(14, 31, base)
+
 #endif /* _ASM_PPC_ASM_DEFNS_H */
diff --git a/xen/arch/ppc/include/asm/boot.h b/xen/arch/ppc/include/asm/boot.h
new file mode 100644
index 0000000000..9b8a7c43c2
--- /dev/null
+++ b/xen/arch/ppc/include/asm/boot.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_PPC_BOOT_H
+#define _ASM_PPC_BOOT_H
+
+#include <xen/types.h>
+
+/* a collection of interfaces used during boot. */
+enum {
+    OF_FAILURE = -1,
+    OF_SUCCESS = 0,
+};
+
+struct of_service {
+    __be32 ofs_service;
+    __be32 ofs_nargs;
+    __be32 ofs_nrets;
+    __be32 ofs_args[10];
+};
+
+int enter_of(struct of_service *args, unsigned long entry);
+void boot_of_init(unsigned long vec);
+
+#endif /* _ASM_PPC_BOOT_H */
diff --git a/xen/arch/ppc/include/asm/byteorder.h b/xen/arch/ppc/include/asm/byteorder.h
new file mode 100644
index 0000000000..2b5f6b9f63
--- /dev/null
+++ b/xen/arch/ppc/include/asm/byteorder.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_PPC_BYTEORDER_H
+#define _ASM_PPC_BYTEORDER_H
+
+#define __arch__swab16 __builtin_bswap16
+#define __arch__swab32 __builtin_bswap32
+#define __arch__swab64 __builtin_bswap64
+
+#define __BYTEORDER_HAS_U64__
+
+#include <xen/byteorder/little_endian.h>
+
+#endif /* _ASM_PPC_BYTEORDER_H */
diff --git a/xen/arch/ppc/include/asm/config.h b/xen/arch/ppc/include/asm/config.h
index 01ca5d0803..cb27d2781e 100644
--- a/xen/arch/ppc/include/asm/config.h
+++ b/xen/arch/ppc/include/asm/config.h
@@ -52,6 +52,9 @@
 /* size of minimum stack frame; C code can write into the caller's stack */
 #define STACK_FRAME_OVERHEAD 32
 
+/* ELFv2 ABI mandates 16 byte alignment */
+#define STACK_ALIGN 16
+
 #endif /* __PPC_CONFIG_H__ */
 /*
  * Local variables:
diff --git a/xen/arch/ppc/include/asm/early_printk.h b/xen/arch/ppc/include/asm/early_printk.h
new file mode 100644
index 0000000000..d1d8b416f4
--- /dev/null
+++ b/xen/arch/ppc/include/asm/early_printk.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_PPC_EARLY_PRINTK_H
+#define _ASM_PPC_EARLY_PRINTK_H
+
+#include <xen/early_printk.h>
+
+#ifdef CONFIG_EARLY_PRINTK
+void early_printk_init(void (*putchar)(char));
+void early_printk(const char *s);
+#else
+static inline void early_printk_init(void (*putchar)(char)) {}
+static inline void early_printk(const char *s) {}
+#endif
+
+#endif /* _ASM_PPC_EARLY_PRINTK_H */
diff --git a/xen/arch/ppc/include/asm/msr.h b/xen/arch/ppc/include/asm/msr.h
new file mode 100644
index 0000000000..144511e5c3
--- /dev/null
+++ b/xen/arch/ppc/include/asm/msr.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) IBM Corp. 2005
+ * Copyright Raptor Engineering, LLC
+ *
+ * Authors: Jimi Xenidis <jimix@watson.ibm.com>
+ *          Shawn Anastasio <sanastasio@raptorengineering.com>
+ */
+
+#ifndef _ASM_PPC_MSR_H
+#define _ASM_PPC_MSR_H
+
+#include <xen/const.h>
+
+/* Flags in MSR: */
+#define MSR_SF      _AC(0x8000000000000000, ULL)
+#define MSR_TA      _AC(0x4000000000000000, ULL)
+#define MSR_ISF     _AC(0x2000000000000000, ULL)
+#define MSR_HV      _AC(0x1000000000000000, ULL)
+#define MSR_VMX     _AC(0x0000000002000000, ULL)
+#define MSR_MER     _AC(0x0000000000200000, ULL)
+#define MSR_POW     _AC(0x0000000000040000, ULL)
+#define MSR_ILE     _AC(0x0000000000010000, ULL)
+#define MSR_EE      _AC(0x0000000000008000, ULL)
+#define MSR_PR      _AC(0x0000000000004000, ULL)
+#define MSR_FP      _AC(0x0000000000002000, ULL)
+#define MSR_ME      _AC(0x0000000000001000, ULL)
+#define MSR_FE0     _AC(0x0000000000000800, ULL)
+#define MSR_SE      _AC(0x0000000000000400, ULL)
+#define MSR_BE      _AC(0x0000000000000200, ULL)
+#define MSR_FE1     _AC(0x0000000000000100, ULL)
+#define MSR_IP      _AC(0x0000000000000040, ULL)
+#define MSR_IR      _AC(0x0000000000000020, ULL)
+#define MSR_DR      _AC(0x0000000000000010, ULL)
+#define MSR_PMM     _AC(0x0000000000000004, ULL)
+#define MSR_RI      _AC(0x0000000000000002, ULL)
+#define MSR_LE      _AC(0x0000000000000001, ULL)
+
+/* MSR bits set on the systemsim simulator */
+#define MSR_SIM       _AC(0x0000000020000000, ULL)
+#define MSR_SYSTEMSIM _AC(0x0000000010000000, ULL)
+
+/* On a trap, srr1's copy of msr defines some bits as follows: */
+#define MSR_TRAP_FE     _AC(0x0000000000100000, ULL) /* Floating Point Exception */
+#define MSR_TRAP_IOP    _AC(0x0000000000080000, ULL) /* Illegal Instruction */
+#define MSR_TRAP_PRIV   _AC(0x0000000000040000, ULL) /* Privileged Instruction */
+#define MSR_TRAP        _AC(0x0000000000020000, ULL) /* Trap Instruction */
+#define MSR_TRAP_NEXT   _AC(0x0000000000010000, ULL) /* PC is next instruction */
+#define MSR_TRAP_BITS  (MSR_TRAP_FE|MSR_TRAP_IOP|MSR_TRAP_PRIV|MSR_TRAP)
+
+#endif /* _ASM_PPC_MSR_H */
diff --git a/xen/arch/ppc/include/asm/processor.h b/xen/arch/ppc/include/asm/processor.h
new file mode 100644
index 0000000000..838d279508
--- /dev/null
+++ b/xen/arch/ppc/include/asm/processor.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright IBM Corp. 2005, 2006, 2007
+ * Copyright Raptor Engineering, LLC
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ *          Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *          Timothy Pearson <tpearson@raptorengineering.com>
+ *          Shawn Anastasio <sanastasio@raptorengineering.com>
+ */
+
+#ifndef _ASM_PPC_PROCESSOR_H
+#define _ASM_PPC_PROCESSOR_H
+
+#define IOBMP_BYTES          8192
+#define IOBMP_INVALID_OFFSET 0x8000
+
+/* Processor Version Register (PVR) field extraction */
+
+#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
+#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF)  /* Revison field */
+
+#define __is_processor(pv) (PVR_VER(mfspr(SPRN_PVR)) == (pv))
+
+/*
+ * IBM has further subdivided the standard PowerPC 16-bit version and
+ * revision subfields of the PVR for the PowerPC 403s into the following:
+ */
+
+#define PVR_FAM(pvr)  (((pvr) >> 20) & 0xFFF) /* Family field */
+#define PVR_MEM(pvr)  (((pvr) >> 16) & 0xF)   /* Member field */
+#define PVR_CORE(pvr) (((pvr) >> 12) & 0xF)   /* Core field */
+#define PVR_CFG(pvr)  (((pvr) >> 8) & 0xF)    /* Configuration field */
+#define PVR_MAJ(pvr)  (((pvr) >> 4) & 0xF)    /* Major revision field */
+#define PVR_MIN(pvr)  (((pvr) >> 0) & 0xF)    /* Minor revision field */
+
+/* Processor Version Numbers */
+
+#define PVR_403GA    0x00200000
+#define PVR_403GB    0x00200100
+#define PVR_403GC    0x00200200
+#define PVR_403GCX   0x00201400
+#define PVR_405GP    0x40110000
+#define PVR_STB03XXX 0x40310000
+#define PVR_NP405H   0x41410000
+#define PVR_NP405L   0x41610000
+#define PVR_601      0x00010000
+#define PVR_602      0x00050000
+#define PVR_603      0x00030000
+#define PVR_603e     0x00060000
+#define PVR_603ev    0x00070000
+#define PVR_603r     0x00071000
+#define PVR_604      0x00040000
+#define PVR_604e     0x00090000
+#define PVR_604r     0x000A0000
+#define PVR_620      0x00140000
+#define PVR_740      0x00080000
+#define PVR_750      PVR_740
+#define PVR_740P     0x10080000
+#define PVR_750P     PVR_740P
+#define PVR_7400     0x000C0000
+#define PVR_7410     0x800C0000
+#define PVR_7450     0x80000000
+#define PVR_8540     0x80200000
+#define PVR_8560     0x80200000
+/*
+ * For the 8xx processors, all of them report the same PVR family for
+ * the PowerPC core. The various versions of these processors must be
+ * differentiated by the version number in the Communication Processor
+ * Module (CPM).
+ */
+#define PVR_821  0x00500000
+#define PVR_823  PVR_821
+#define PVR_850  PVR_821
+#define PVR_860  PVR_821
+#define PVR_8240 0x00810100
+#define PVR_8245 0x80811014
+#define PVR_8260 PVR_8240
+
+/* 64-bit processors */
+#define PVR_NORTHSTAR 0x0033
+#define PVR_PULSAR    0x0034
+#define PVR_POWER4    0x0035
+#define PVR_ICESTAR   0x0036
+#define PVR_SSTAR     0x0037
+#define PVR_POWER4p   0x0038
+#define PVR_970       0x0039
+#define PVR_POWER5    0x003A
+#define PVR_POWER5p   0x003B
+#define PVR_970FX     0x003C
+#define PVR_POWER6    0x003E
+#define PVR_POWER7    0x003F
+#define PVR_630       0x0040
+#define PVR_630p      0x0041
+#define PVR_970MP     0x0044
+#define PVR_970GX     0x0045
+#define PVR_POWER7p   0x004A
+#define PVR_POWER8E   0x004B
+#define PVR_POWER8NVL 0x004C
+#define PVR_POWER8    0x004D
+#define PVR_POWER9    0x004E
+#define PVR_POWER10   0x0080
+#define PVR_BE        0x0070
+#define PVR_PA6T      0x0090
+
+#ifndef __ASSEMBLY__
+
+#include <xen/types.h>
+
+/* Macro to adjust thread priority for hardware multithreading */
+#define HMT_very_low()  asm volatile ( "or %r31, %r31, %r31" )
+
+/*
+ * User-accessible registers: most of these need to be saved/restored
+ * for every nested Xen invocation.
+ */
+struct cpu_user_regs
+{
+    uint64_t gprs[32];
+    uint64_t lr;
+    uint64_t ctr;
+    uint64_t srr0;
+    uint64_t srr1;
+    uint64_t pc;
+    uint64_t msr;
+    uint64_t fpscr;
+    uint64_t xer;
+    uint64_t hid4;  /* debug only */
+    uint64_t dar;   /* debug only */
+    uint32_t dsisr; /* debug only */
+    uint32_t cr;
+    uint32_t __pad; /* good spot for another 32bit reg */
+    uint32_t entry_vector;
+};
+
+#endif
+
+#endif /* _ASM_PPC_PROCESSOR_H */
diff --git a/xen/arch/ppc/include/asm/types.h b/xen/arch/ppc/include/asm/types.h
new file mode 100644
index 0000000000..cee08e111a
--- /dev/null
+++ b/xen/arch/ppc/include/asm/types.h
@@ -0,0 +1,21 @@
+/* from xen/arch/x86/include/asm/types.h */
+
+#ifndef _ASM_PPC_TYPES_H
+#define _ASM_PPC_TYPES_H
+
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed int s32;
+typedef unsigned int u32;
+
+typedef signed long s64;
+typedef unsigned long u64;
+typedef unsigned long paddr_t;
+#define INVALID_PADDR (~0UL)
+#define PRIpaddr "016lx"
+
+#endif /* _ASM_PPC_TYPES_H */
diff --git a/xen/arch/ppc/ppc64/Makefile b/xen/arch/ppc/ppc64/Makefile
index 3340058c08..f4956daaa9 100644
--- a/xen/arch/ppc/ppc64/Makefile
+++ b/xen/arch/ppc/ppc64/Makefile
@@ -1 +1,2 @@
 obj-y += head.o
+obj-y += of-call.o
diff --git a/xen/arch/ppc/ppc64/asm-offsets.c b/xen/arch/ppc/ppc64/asm-offsets.c
index e69de29bb2..7fc29d5db1 100644
--- a/xen/arch/ppc/ppc64/asm-offsets.c
+++ b/xen/arch/ppc/ppc64/asm-offsets.c
@@ -0,0 +1,59 @@
+/*
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed
+ * to extract and format the required data.
+ */
+
+#include <asm/processor.h>
+
+#define DEFINE(_sym, _val)                                                 \
+    asm volatile ("\n.ascii\"==>#define " #_sym " %0 /* " #_val " */<==\"" \
+                  : : "i" (_val) )
+#define BLANK()                                                            \
+    asm volatile ( "\n.ascii\"==><==\"" : : )
+#define OFFSET(_sym, _str, _mem)                                           \
+    DEFINE(_sym, offsetof(_str, _mem));
+
+/* base-2 logarithm */
+#define __L2(_x)  (((_x) & 0x00000002) ?   1 : 0)
+#define __L4(_x)  (((_x) & 0x0000000c) ? ( 2 + __L2( (_x)>> 2)) : __L2( _x))
+#define __L8(_x)  (((_x) & 0x000000f0) ? ( 4 + __L4( (_x)>> 4)) : __L4( _x))
+#define __L16(_x) (((_x) & 0x0000ff00) ? ( 8 + __L8( (_x)>> 8)) : __L8( _x))
+#define LOG_2(_x) (((_x) & 0xffff0000) ? (16 + __L16((_x)>>16)) : __L16(_x))
+
+void __dummy__(void)
+{
+    DEFINE(GPR_WIDTH, sizeof(unsigned long));
+    DEFINE(FPR_WIDTH, sizeof(double));
+
+    OFFSET(UREGS_gprs, struct cpu_user_regs, gprs);
+    OFFSET(UREGS_r0, struct cpu_user_regs, gprs[0]);
+    OFFSET(UREGS_r1, struct cpu_user_regs, gprs[1]);
+    OFFSET(UREGS_r13, struct cpu_user_regs, gprs[13]);
+    OFFSET(UREGS_srr0, struct cpu_user_regs, srr0);
+    OFFSET(UREGS_srr1, struct cpu_user_regs, srr1);
+    OFFSET(UREGS_pc, struct cpu_user_regs, pc);
+    OFFSET(UREGS_msr, struct cpu_user_regs, msr);
+    OFFSET(UREGS_lr, struct cpu_user_regs, lr);
+    OFFSET(UREGS_ctr, struct cpu_user_regs, ctr);
+    OFFSET(UREGS_xer, struct cpu_user_regs, xer);
+    OFFSET(UREGS_hid4, struct cpu_user_regs, hid4);
+    OFFSET(UREGS_dar, struct cpu_user_regs, dar);
+    OFFSET(UREGS_dsisr, struct cpu_user_regs, dsisr);
+    OFFSET(UREGS_cr, struct cpu_user_regs, cr);
+    OFFSET(UREGS_fpscr, struct cpu_user_regs, fpscr);
+    DEFINE(UREGS_sizeof, sizeof(struct cpu_user_regs));
+}
+
+/* TODO: Replace with BUILD_BUG_ON + IS_ALIGNED once we can use <xen/lib.h> */
+_Static_assert(sizeof(struct cpu_user_regs) % STACK_ALIGN == 0,
+               "struct cpu_user_regs not stack aligned!");
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/arch/ppc/ppc64/head.S b/xen/arch/ppc/ppc64/head.S
index 17f4a3e19c..301ba5c8a5 100644
--- a/xen/arch/ppc/ppc64/head.S
+++ b/xen/arch/ppc/ppc64/head.S
@@ -18,6 +18,15 @@ ENTRY(start)
     li %r11, 0
     stdu %r11, -STACK_FRAME_OVERHEAD(%r1)
 
+    /* clear .bss */
+    LOAD_IMM32(%r14, __bss_start)
+    LOAD_IMM32(%r15, __bss_end)
+1:
+    std %r11, 0(%r14)
+    addi %r14, %r14, 8
+    cmpld %r14, %r15
+    blt 1b
+
     /* call the C entrypoint */
     bl start_xen
 
diff --git a/xen/arch/ppc/ppc64/of-call.S b/xen/arch/ppc/ppc64/of-call.S
new file mode 100644
index 0000000000..bde89f6aba
--- /dev/null
+++ b/xen/arch/ppc/ppc64/of-call.S
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Adapted from Linux's arch/powerpc/kernel/entry_64.S, with the
+ * following copyright notice:
+ *
+ *  PowerPC version
+ *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *  Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP
+ *    Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu>
+ *  Adapted for Power Macintosh by Paul Mackerras.
+ *  Low-level exception handlers and MMU support
+ *  rewritten by Paul Mackerras.
+ *    Copyright (C) 1996 Paul Mackerras.
+ *  MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/asm-defns.h>
+#include <asm/msr.h>
+
+/* size of minimum stack frame that can hold an entire cpu_user_regs struct */
+#define STACK_SWITCH_FRAME_SIZE UREGS_sizeof
+
+    .section .init.text, "ax", @progbits
+
+ENTRY(enter_of)
+    mflr %r0
+    std %r0, 16(%r1)
+    stdu %r1,-STACK_SWITCH_FRAME_SIZE(%r1) /* Save SP and create stack space */
+
+    /*
+     * Because PROM is running in 32b mode, it clobbers the high order half
+     * of all registers that it saves.  We therefore save those registers
+     * PROM might touch to the stack.  (%r0, %r3-%r13 are caller saved)
+     */
+    SAVE_GPR(2, %r1)
+    SAVE_GPR(13, %r1)
+    SAVE_NVGPRS(%r1)
+    mfcr %r10
+    mfmsr %r11
+    std %r10, UREGS_cr(%r1)
+    std %r11, UREGS_msr(%r1)
+
+    /* Put PROM address in SRR0 */
+    mtsrr0 %r4
+
+    /* Setup our trampoline return addr in LR */
+    bcl 20, 31, .+4
+0:  mflr %r4
+    addi %r4, %r4, 1f - 0b
+    mtlr %r4
+
+    /* Prepare a 32-bit mode big endian MSR */
+    LOAD_IMM64(%r12, MSR_SF | MSR_LE)
+    andc %r11, %r11, %r12
+    mtsrr1 %r11
+    rfid
+
+1:  /* Return from OF */
+    FIXUP_ENDIAN
+
+    /* Just make sure that %r1 top 32 bits didn't get corrupt by OF */
+    rldicl %r1, %r1, 0, 32
+
+    /* Restore the MSR (back to 64 bits) */
+    ld %r0, UREGS_msr(%r1)
+    mtmsrd %r0
+    isync
+
+    /* Restore other registers */
+    REST_GPR(2, %r1)
+    REST_GPR(13, %r1)
+    REST_NVGPRS(%r1)
+    ld %r4, UREGS_cr(%r1)
+    mtcr %r4
+
+    addi %r1, %r1, STACK_SWITCH_FRAME_SIZE
+    ld %r0, 16(%r1)
+    mtlr %r0
+    blr
+
+    .size enter_of, . - enter_of
+    .type enter_of, %function
diff --git a/xen/arch/ppc/setup.c b/xen/arch/ppc/setup.c
index 9e90a6179a..7c623a49f5 100644
--- a/xen/arch/ppc/setup.c
+++ b/xen/arch/ppc/setup.c
@@ -1,16 +1,29 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 #include <xen/init.h>
+#include <asm/boot.h>
+#include <asm/early_printk.h>
+#include <asm/processor.h>
 
 /* Xen stack for bringing up the first CPU. */
 unsigned char __initdata cpu0_boot_stack[STACK_SIZE] __aligned(STACK_SIZE);
 
-/* Macro to adjust thread priority for hardware multithreading */
-#define HMT_very_low()  asm volatile ( "or %r31, %r31, %r31" )
-
 void __init noreturn start_xen(unsigned long r3, unsigned long r4,
                                unsigned long r5, unsigned long r6,
                                unsigned long r7)
 {
+    if ( r5 )
+    {
+        /* OpenFirmware boot protocol */
+        boot_of_init(r5);
+    }
+    else
+    {
+        /* kexec boot: Unimplemented */
+        __builtin_trap();
+    }
+
+    early_printk("Hello, ppc64le!\n");
+
     for ( ; ; )
         /* Set current hardware thread to very low priority */
         HMT_very_low();
