aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeandro Lupori <luporl@FreeBSD.org>2021-10-20 18:48:33 +0000
committerLeandro Lupori <luporl@FreeBSD.org>2021-10-20 18:48:33 +0000
commitf83288645cd9726c24ca67292fbc3abb4eb65a36 (patch)
tree4e21abd0b10961bcdbc5c5a14fa604b196f952b3
parent2ff7c2cc4f28ab05caccb2936ba0d74c6734dd39 (diff)
downloadsrc-f83288645cd9726c24ca67292fbc3abb4eb65a36.tar.gz
src-f83288645cd9726c24ca67292fbc3abb4eb65a36.zip
powerpc64le: stand fixes
Fix boot1 and loader on PowerPC64 little-endian (LE). Due to endian issues, boot1 couldn't find the UFS boot partition and loader wasn't able to load the kernel. Most of the issues happened because boot1 and loader were BE binaries trying to access LE UFS partitions and because loader expects the kernel ELF image to use the same endian as itself. To fix these issues, boot1 and loader are now built as LE binaries on PPC64LE. To support this, the functions that call OpenFirmware were enhanced to correctly perform endian conversion on its input and output arguments and to change the CPU into BE mode before making the calls, as OpenFirmware always runs in BE. Besides that, some other small fixes were needed. Submitted by: bdragon (initial version) Reviewed by: alfredo, jhibbits Sponsored by: Instituto de Pesquisas Eldorado (eldorado.org.br) Differential Revision: https://reviews.freebsd.org/D32160
-rw-r--r--stand/common/load_elf.c18
-rw-r--r--stand/defs.mk8
-rw-r--r--stand/libofw/openfirm.c344
-rw-r--r--stand/libofw/openfirm.h7
-rw-r--r--stand/powerpc/Makefile6
-rw-r--r--stand/powerpc/boot1.chrp/boot1.c102
-rw-r--r--stand/powerpc/ofw/Makefile14
-rw-r--r--stand/powerpc/ofw/cas.c14
-rw-r--r--stand/powerpc/ofw/ldscript.powerpcle142
-rw-r--r--stand/powerpc/ofw/main.c37
-rw-r--r--stand/powerpc/ofw/ofwfdt.c7
-rw-r--r--stand/powerpc/ofw/trampolineLE.S71
12 files changed, 525 insertions, 245 deletions
diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
index c163b50c9737..a213b34970f0 100644
--- a/stand/common/load_elf.c
+++ b/stand/common/load_elf.c
@@ -750,13 +750,6 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off)
}
#endif
size = shdr[i].sh_size;
-#if defined(__powerpc__)
- #if __ELF_WORD_SIZE == 64
- size = htobe64(size);
- #else
- size = htobe32(size);
- #endif
-#endif
archsw.arch_copyin(&size, lastaddr, sizeof(size));
lastaddr += sizeof(size);
@@ -802,17 +795,6 @@ __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off)
printf("]");
#endif
-#if defined(__powerpc__)
- /* On PowerPC we always need to provide BE data to the kernel */
- #if __ELF_WORD_SIZE == 64
- ssym = htobe64((uint64_t)ssym);
- esym = htobe64((uint64_t)esym);
- #else
- ssym = htobe32((uint32_t)ssym);
- esym = htobe32((uint32_t)esym);
- #endif
-#endif
-
file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);
diff --git a/stand/defs.mk b/stand/defs.mk
index aee4636985ac..765fd046a879 100644
--- a/stand/defs.mk
+++ b/stand/defs.mk
@@ -120,10 +120,12 @@ CFLAGS+= -DLOADER_DISK_SUPPORT
# Machine specific flags for all builds here
-# Ensure PowerPC64 and PowerPC64LE boot loaders are compiled as 32 bit
-# and in big endian.
-.if ${MACHINE_ARCH:Mpowerpc64*} != ""
+# Ensure PowerPC64 and PowerPC64LE boot loaders are compiled as 32 bit.
+# PowerPC64LE boot loaders are 32-bit little-endian.
+.if ${MACHINE_ARCH} == "powerpc64"
CFLAGS+= -m32 -mcpu=powerpc -mbig-endian
+.elif ${MACHINE_ARCH} == "powerpc64le"
+CFLAGS+= -m32 -mcpu=powerpc -mlittle-endian
.endif
# For amd64, there's a bit of mixed bag. Some of the tree (i386, lib*32) is
diff --git a/stand/libofw/openfirm.c b/stand/libofw/openfirm.c
index 0b4198d281fd..b2b89581ae70 100644
--- a/stand/libofw/openfirm.c
+++ b/stand/libofw/openfirm.c
@@ -58,6 +58,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/endian.h>
+
#include <machine/stdarg.h>
#include <stand.h>
@@ -71,6 +73,13 @@ ihandle_t mmu;
ihandle_t memory;
int real_mode = 0;
+#define IN(x) htobe32((cell_t)x)
+#define OUT(x) be32toh(x)
+#define SETUP(a, b, c, d) \
+ a.name = IN( (b) ); \
+ a.nargs = IN( (c) ); \
+ a.nreturns = IN( (d) );
+
/* Initialiser */
void
@@ -117,16 +126,13 @@ OF_test(char *name)
cell_t nreturns;
cell_t service;
cell_t missing;
- } args = {
- (cell_t)"test",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "test", 1, 1);
- args.service = (cell_t)name;
+ args.service = IN(name);
if (openfirmware(&args) == -1)
return (-1);
- return (args.missing);
+ return (OUT(args.missing));
}
/* Return firmware millisecond count. */
@@ -138,14 +144,11 @@ OF_milliseconds()
cell_t nargs;
cell_t nreturns;
cell_t ms;
- } args = {
- (cell_t)"milliseconds",
- 0,
- 1,
- };
+ } args = {};
+ SETUP(args, "milliseconds", 0, 1);
openfirmware(&args);
- return (args.ms);
+ return (OUT(args.ms));
}
/*
@@ -162,11 +165,8 @@ OF_peer(phandle_t node)
cell_t nreturns;
cell_t node;
cell_t next;
- } args = {
- (cell_t)"peer",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "peer", 1, 1);
args.node = node;
if (openfirmware(&args) == -1)
@@ -184,11 +184,8 @@ OF_child(phandle_t node)
cell_t nreturns;
cell_t node;
cell_t child;
- } args = {
- (cell_t)"child",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "child", 1, 1);
args.node = node;
if (openfirmware(&args) == -1)
@@ -206,11 +203,8 @@ OF_parent(phandle_t node)
cell_t nreturns;
cell_t node;
cell_t parent;
- } args = {
- (cell_t)"parent",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "parent", 1, 1);
args.node = node;
if (openfirmware(&args) == -1)
@@ -228,11 +222,8 @@ OF_instance_to_package(ihandle_t instance)
cell_t nreturns;
cell_t instance;
cell_t package;
- } args = {
- (cell_t)"instance-to-package",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "instance-to-package", 1, 1);
args.instance = instance;
if (openfirmware(&args) == -1)
@@ -251,17 +242,14 @@ OF_getproplen(phandle_t package, const char *propname)
cell_t package;
cell_t propname;
cell_t proplen;
- } args = {
- (cell_t)"getproplen",
- 2,
- 1,
- };
+ } args = {};
+ SETUP(args, "getproplen", 2, 1);
args.package = package;
- args.propname = (cell_t)propname;
+ args.propname = IN(propname);
if (openfirmware(&args) == -1)
return (-1);
- return (args.proplen);
+ return (OUT(args.proplen));
}
/* Get the value of a property of a package. */
@@ -277,19 +265,31 @@ OF_getprop(phandle_t package, const char *propname, void *buf, int buflen)
cell_t buf;
cell_t buflen;
cell_t size;
- } args = {
- (cell_t)"getprop",
- 4,
- 1,
- };
+ } args = {};
+ SETUP(args, "getprop", 4, 1);
args.package = package;
- args.propname = (cell_t)propname;
- args.buf = (cell_t)buf;
- args.buflen = buflen;
+ args.propname = IN(propname);
+ args.buf = IN(buf);
+ args.buflen = IN(buflen);
if (openfirmware(&args) == -1)
return (-1);
- return (args.size);
+ return (OUT(args.size));
+}
+
+/* Decode a binary property from a package. */
+int
+OF_getencprop(phandle_t package, const char *propname, cell_t *buf, int buflen)
+{
+ int retval, i;
+ retval = OF_getprop(package, propname, buf, buflen);
+ if (retval == -1)
+ return (retval);
+
+ for (i = 0; i < buflen/4; i++)
+ buf[i] = be32toh((uint32_t)buf[i]);
+
+ return (retval);
}
/* Get the next property of a package. */
@@ -304,18 +304,15 @@ OF_nextprop(phandle_t package, const char *previous, char *buf)
cell_t previous;
cell_t buf;
cell_t flag;
- } args = {
- (cell_t)"nextprop",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "nextprop", 3, 1);
args.package = package;
- args.previous = (cell_t)previous;
- args.buf = (cell_t)buf;
+ args.previous = IN(previous);
+ args.buf = IN(buf);
if (openfirmware(&args) == -1)
return (-1);
- return (args.flag);
+ return (OUT(args.flag));
}
/* Set the value of a property of a package. */
@@ -332,19 +329,16 @@ OF_setprop(phandle_t package, const char *propname, void *buf, int len)
cell_t buf;
cell_t len;
cell_t size;
- } args = {
- (cell_t)"setprop",
- 4,
- 1,
- };
+ } args = {};
+ SETUP(args, "setprop", 4, 1);
args.package = package;
- args.propname = (cell_t)propname;
- args.buf = (cell_t)buf;
- args.len = len;
+ args.propname = IN(propname);
+ args.buf = IN(buf);
+ args.len = IN(len);
if (openfirmware(&args) == -1)
return (-1);
- return (args.size);
+ return (OUT(args.size));
}
/* Convert a device specifier to a fully qualified pathname. */
@@ -359,18 +353,15 @@ OF_canon(const char *device, char *buf, int len)
cell_t buf;
cell_t len;
cell_t size;
- } args = {
- (cell_t)"canon",
- 3,
- 1,
- };
-
- args.device = (cell_t)device;
- args.buf = (cell_t)buf;
- args.len = len;
+ } args = {};
+ SETUP(args, "canon", 3, 1);
+
+ args.device = IN(device);
+ args.buf = IN(buf);
+ args.len = IN(len);
if (openfirmware(&args) == -1)
return (-1);
- return (args.size);
+ return (OUT(args.size));
}
/* Return a package handle for the specified device. */
@@ -383,13 +374,10 @@ OF_finddevice(const char *device)
cell_t nreturns;
cell_t device;
cell_t package;
- } args = {
- (cell_t)"finddevice",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "finddevice", 1, 1);
- args.device = (cell_t)device;
+ args.device = IN(device);
if (openfirmware(&args) == -1)
return (-1);
return (args.package);
@@ -407,18 +395,15 @@ OF_instance_to_path(ihandle_t instance, char *buf, int len)
cell_t buf;
cell_t len;
cell_t size;
- } args = {
- (cell_t)"instance-to-path",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "instance-to-path", 3, 1);
args.instance = instance;
- args.buf = (cell_t)buf;
- args.len = len;
+ args.buf = IN(buf);
+ args.len = IN(len);
if (openfirmware(&args) == -1)
return (-1);
- return (args.size);
+ return (OUT(args.size));
}
/* Return the fully qualified pathname corresponding to a package. */
@@ -433,18 +418,15 @@ OF_package_to_path(phandle_t package, char *buf, int len)
cell_t buf;
cell_t len;
cell_t size;
- } args = {
- (cell_t)"package-to-path",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "package-to-path", 3, 1);
args.package = package;
- args.buf = (cell_t)buf;
- args.len = len;
+ args.buf = IN(buf);
+ args.len = IN(len);
if (openfirmware(&args) == -1)
return (-1);
- return (args.size);
+ return (OUT(args.size));
}
/* Call the method in the scope of a given instance. */
@@ -459,30 +441,26 @@ OF_call_method(char *method, ihandle_t instance, int nargs, int nreturns, ...)
cell_t method;
cell_t instance;
cell_t args_n_results[12];
- } args = {
- (cell_t)"call-method",
- 2,
- 1,
- };
+ } args = {};
+ SETUP(args, "call-method", nargs + 2, nreturns + 1);
cell_t *cp;
int n;
if (nargs > 6)
return (-1);
- args.nargs = nargs + 2;
- args.nreturns = nreturns + 1;
- args.method = (cell_t)method;
+ args.method = IN(method);
args.instance = instance;
va_start(ap, nreturns);
for (cp = (cell_t *)(args.args_n_results + (n = nargs)); --n >= 0;)
- *--cp = va_arg(ap, cell_t);
+ *--cp = IN(va_arg(ap, cell_t));
if (openfirmware(&args) == -1)
return (-1);
if (args.args_n_results[nargs])
- return (args.args_n_results[nargs]);
- for (cp = (cell_t *)(args.args_n_results + nargs + (n = args.nreturns));
- --n > 0;)
- *va_arg(ap, cell_t *) = *--cp;
+ return (OUT(args.args_n_results[nargs]));
+ /* XXX what if ihandles or phandles are returned */
+ for (cp = (cell_t *)(args.args_n_results + nargs +
+ (n = be32toh(args.nreturns))); --n > 0;)
+ *va_arg(ap, cell_t *) = OUT(*--cp);
va_end(ap);
return (0);
}
@@ -501,13 +479,10 @@ OF_open(char *device)
cell_t nreturns;
cell_t device;
cell_t instance;
- } args = {
- (cell_t)"open",
- 1,
- 1,
- };
+ } args = {};
+ SETUP(args, "open", 1, 1);
- args.device = (cell_t)device;
+ args.device = IN(device);
if (openfirmware(&args) == -1 || args.instance == 0) {
return (-1);
}
@@ -523,10 +498,8 @@ OF_close(ihandle_t instance)
cell_t nargs;
cell_t nreturns;
cell_t instance;
- } args = {
- (cell_t)"close",
- 1,
- };
+ } args = {};
+ SETUP(args, "close", 1, 0);
args.instance = instance;
openfirmware(&args);
@@ -544,19 +517,16 @@ OF_read(ihandle_t instance, void *addr, int len)
cell_t addr;
cell_t len;
cell_t actual;
- } args = {
- (cell_t)"read",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "read", 3, 1);
args.instance = instance;
- args.addr = (cell_t)addr;
- args.len = len;
+ args.addr = IN(addr);
+ args.len = IN(len);
#if defined(OPENFIRM_DEBUG)
printf("OF_read: called with instance=%08x, addr=%p, len=%d\n",
- args.instance, args.addr, args.len);
+ instance, addr, len);
#endif
if (openfirmware(&args) == -1)
@@ -564,10 +534,10 @@ OF_read(ihandle_t instance, void *addr, int len)
#if defined(OPENFIRM_DEBUG)
printf("OF_read: returning instance=%d, addr=%p, len=%d, actual=%d\n",
- args.instance, args.addr, args.len, args.actual);
+ args.instance, OUT(args.addr), OUT(args.len), OUT(args.actual));
#endif
- return (args.actual);
+ return (OUT(args.actual));
}
/* Write to an instance. */
@@ -582,18 +552,15 @@ OF_write(ihandle_t instance, void *addr, int len)
cell_t addr;
cell_t len;
cell_t actual;
- } args = {
- (cell_t)"write",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "write", 3, 1);
args.instance = instance;
- args.addr = (cell_t)addr;
- args.len = len;
+ args.addr = IN(addr);
+ args.len = IN(len);
if (openfirmware(&args) == -1)
return (-1);
- return (args.actual);
+ return (OUT(args.actual));
}
/* Seek to a position. */
@@ -608,18 +575,15 @@ OF_seek(ihandle_t instance, uint64_t pos)
cell_t poshi;
cell_t poslo;
cell_t status;
- } args = {
- (cell_t)"seek",
- 3,
- 1,
- };
+ } args = {};
+ SETUP(args, "seek", 3, 1);
args.instance = instance;
- args.poshi = pos >> 32;
- args.poslo = pos;
+ args.poshi = IN(((uint64_t)pos >> 32));
+ args.poslo = IN(pos);
if (openfirmware(&args) == -1)
return (-1);
- return (args.status);
+ return (OUT(args.status));
}
/* Blocks. */
@@ -633,16 +597,13 @@ OF_blocks(ihandle_t instance)
cell_t instance;
cell_t result;
cell_t blocks;
- } args = {
- (cell_t)"#blocks",
- 2,
- 1,
- };
+ } args = {};
+ SETUP(args, "#blocks", 2, 1);
args.instance = instance;
if (openfirmware(&args) == -1)
return ((unsigned int)-1);
- return (args.blocks);
+ return (OUT(args.blocks));
}
/* Block size. */
@@ -656,16 +617,13 @@ OF_block_size(ihandle_t instance)
cell_t instance;
cell_t result;
cell_t size;
- } args = {
- (cell_t)"block-size",
- 2,
- 1,
- };
+ } args = {};
+ SETUP(args, "block-size", 2, 1);
args.instance = instance;
if (openfirmware(&args) == -1)
return (512);
- return (args.size);
+ return (OUT(args.size));
}
/*
@@ -684,18 +642,15 @@ OF_claim(void *virt, u_int size, u_int align)
cell_t size;
cell_t align;
cell_t baseaddr;
- } args = {
- (cell_t)"claim",
- 3,
- 1,
- };
-
- args.virt = (cell_t)virt;
- args.size = size;
- args.align = align;
+ } args = {};
+ SETUP(args, "claim", 3, 1);
+
+ args.virt = IN(virt);
+ args.size = IN(size);
+ args.align = IN(align);
if (openfirmware(&args) == -1)
return ((void *)-1);
- return ((void *)args.baseaddr);
+ return ((void *)OUT(args.baseaddr));
}
/* Release an area of memory. */
@@ -708,13 +663,11 @@ OF_release(void *virt, u_int size)
cell_t nreturns;
cell_t virt;
cell_t size;
- } args = {
- (cell_t)"release",
- 2,
- };
+ } args = {};
+ SETUP(args, "release", 2, 0);
- args.virt = (cell_t)virt;
- args.size = size;
+ args.virt = IN(virt);
+ args.size = IN(size);
openfirmware(&args);
}
@@ -731,12 +684,10 @@ OF_boot(char *bootspec)
cell_t nargs;
cell_t nreturns;
cell_t bootspec;
- } args = {
- (cell_t)"boot",
- 1,
- };
+ } args = {};
+ SETUP(args, "boot", 1, 0);
- args.bootspec = (cell_t)bootspec;
+ args.bootspec = IN(bootspec);
openfirmware(&args);
for (;;) /* just in case */
;
@@ -750,9 +701,8 @@ OF_enter()
cell_t name;
cell_t nargs;
cell_t nreturns;
- } args = {
- (cell_t)"enter",
- };
+ } args = {};
+ SETUP(args, "enter", 0, 0);
openfirmware(&args);
/* We may come back. */
@@ -766,9 +716,8 @@ OF_exit()
cell_t name;
cell_t nargs;
cell_t nreturns;
- } args = {
- (cell_t)"exit",
- };
+ } args = {};
+ SETUP(args, "exit", 0, 0);
openfirmware(&args);
for (;;) /* just in case */
@@ -782,9 +731,8 @@ OF_quiesce()
cell_t name;
cell_t nargs;
cell_t nreturns;
- } args = {
- (cell_t)"quiesce",
- };
+ } args = {};
+ SETUP(args, "quiesce", 0, 0);
openfirmware(&args);
}
@@ -803,16 +751,14 @@ OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len)
cell_t entry;
cell_t arg;
cell_t len;
- } args = {
- (cell_t)"chain",
- 5,
- };
-
- args.virt = (cell_t)virt;
- args.size = size;
- args.entry = (cell_t)entry;
- args.arg = (cell_t)arg;
- args.len = len;
+ } args = {};
+ SETUP(args, "chain", 5, 0);
+
+ args.virt = IN(virt);
+ args.size = IN(size);
+ args.entry = IN(entry);
+ args.arg = IN(arg);
+ args.len = IN(len);
openfirmware(&args);
}
#else
diff --git a/stand/libofw/openfirm.h b/stand/libofw/openfirm.h
index b83cf4b0b27f..0981dbf093eb 100644
--- a/stand/libofw/openfirm.h
+++ b/stand/libofw/openfirm.h
@@ -65,9 +65,9 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-typedef unsigned int ihandle_t;
-typedef unsigned int phandle_t;
-typedef unsigned long int cell_t;
+typedef uint32_t ihandle_t;
+typedef uint32_t phandle_t;
+typedef uint32_t cell_t;
extern int (*openfirmware)(void *);
extern phandle_t chosen;
@@ -91,6 +91,7 @@ phandle_t OF_parent(phandle_t);
phandle_t OF_instance_to_package(ihandle_t);
int OF_getproplen(phandle_t, const char *);
int OF_getprop(phandle_t, const char *, void *, int);
+int OF_getencprop(phandle_t, const char *, cell_t *, int);
int OF_nextprop(phandle_t, const char *, char *);
int OF_setprop(phandle_t, const char *, void *, int);
int OF_canon(const char *, char *, int);
diff --git a/stand/powerpc/Makefile b/stand/powerpc/Makefile
index 888fe0e97028..a16d3933ff7e 100644
--- a/stand/powerpc/Makefile
+++ b/stand/powerpc/Makefile
@@ -4,7 +4,11 @@ NO_OBJ=t
.include <bsd.init.mk>
-SUBDIR.yes= boot1.chrp ofw uboot
+SUBDIR.yes= boot1.chrp ofw
+
+.if "${MACHINE_ARCH}" != "powerpc64le"
+SUBDIR.${MK_FDT}+= uboot
+.endif
.if "${MACHINE_ARCH}" == "powerpc64"
SUBDIR.${MK_FDT}+= kboot
diff --git a/stand/powerpc/boot1.chrp/boot1.c b/stand/powerpc/boot1.chrp/boot1.c
index 4d152efe1a70..ed7c55d11d6f 100644
--- a/stand/powerpc/boot1.chrp/boot1.c
+++ b/stand/powerpc/boot1.chrp/boot1.c
@@ -20,6 +20,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/dirent.h>
+#include <sys/endian.h>
#include <machine/elf.h>
#include <machine/stdarg.h>
#include <machine/md_var.h>
@@ -82,11 +83,11 @@ static char *__ultoa(char *buf, u_long val, int base);
*/
typedef uint32_t ofwcell_t;
typedef uint32_t u_ofwh_t;
-typedef int (*ofwfp_t)(void *);
+typedef int (*ofwfp_t)(ofwcell_t *);
ofwfp_t ofw; /* the prom Open Firmware entry */
ofwh_t chosenh;
-void ofw_init(void *, int, int (*)(void *), char *, int);
+void ofw_init(void *, int, ofwfp_t, char *, int);
static ofwh_t ofw_finddevice(const char *);
static ofwh_t ofw_open(const char *);
static int ofw_close(ofwh_t);
@@ -101,6 +102,16 @@ static void ofw_exit(void) __dead2;
ofwh_t bootdevh;
ofwh_t stdinh, stdouth;
+/*
+ * Note about the entry point:
+ *
+ * For some odd reason, the first page of the load appears to have trouble
+ * when entering in LE. The first five instructions decode weirdly.
+ * I suspect it is some cache weirdness between the ELF headers and .text.
+ *
+ * Ensure we have a gap between the start of .text and the entry as a
+ * workaround.
+ */
__asm(" \n\
.data \n\
.align 4 \n\
@@ -108,6 +119,8 @@ stack: \n\
.space 16384 \n\
\n\
.text \n\
+ /* SLOF cache hack */ \n\
+ .space 4096 \n\
.globl _start \n\
_start: \n\
lis %r1,stack@ha \n\
@@ -117,18 +130,95 @@ _start: \n\
b ofw_init \n\
");
+ofwfp_t realofw;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+/*
+ * Minimal endianness-swap trampoline for LE.
+ */
+__attribute__((naked)) int
+ofwtramp(void *buf, ofwfp_t cb)
+{
+__asm(" \n\
+ mflr %r0 \n\
+ stw %r0, 4(%r1) \n\
+ stwu %r1, -16(%r1) \n\
+ stw %r30, 8(%r1) \n\
+ /* Save current MSR for restoration post-call. */ \n\
+ mfmsr %r30 \n\
+ mr %r5, %r30 \n\
+ /* Remove LE bit from MSR. */ \n\
+ clrrwi %r5, %r5, 1 \n\
+ mtsrr0 %r4 \n\
+ mtsrr1 %r5 \n\
+ bcl 20, 31, .+4 /* LOAD_LR_NIA */ \n\
+1: \n\
+ mflr %r4 \n\
+ addi %r4, %r4, (2f - 1b) \n\
+ mtlr %r4 \n\
+ /* Switch to BE and transfer control to OF entry */ \n\
+ rfid \n\
+2: \n\
+ /* Control is returned here, but in BE. */ \n\
+ .long 0x05009f42 /* LOAD_LR_NIA */\n\
+ /* 0: */\n\
+ .long 0xa603db7f /* mtsrr1 %r30 */\n\
+ .long 0xa602c87f /* mflr %r30 */\n\
+ .long 0x1400de3b /* addi %r30, %r30, (1f - 0b) */\n\
+ .long 0xa603da7f /* mtsrr0 %r30 */\n\
+ .long 0x2400004c /* rfid */\n\
+ /* 1: */\n\
+1: \n\
+ /* Back to normal. Tidy up for return. */ \n\
+ lwz %r30, 8(%r1) \n\
+ lwz %r0, 20(%r1) \n\
+ addi %r1, %r1, 16 \n\
+ mtlr %r0 \n\
+ blr \n\
+");
+}
+
+/*
+ * Little-endian OFW entrypoint replacement.
+ *
+ * We are doing all the byteswapping in one place here to save space.
+ * This means instance handles will be byteswapped as well.
+ */
+int
+call_ofw(ofwcell_t* buf)
+{
+ int ret, i, ncells;
+
+ ncells = 3 + buf[1] + buf[2];
+ for (i = 0; i < ncells; i++)
+ buf[i] = htobe32(buf[i]);
+
+ ret = (ofwtramp(buf, realofw));
+ for (i = 0; i < ncells; i++)
+ buf[i] = be32toh(buf[i]);
+ return (ret);
+}
+#endif
+
void
-ofw_init(void *vpd, int res, int (*openfirm)(void *), char *arg, int argl)
+ofw_init(void *vpd, int res, ofwfp_t openfirm, char *arg, int argl)
{
char *av[16];
char *p;
int ac;
- ofw = openfirm;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ realofw = openfirm;
+ ofw = call_ofw;
+#else
+ realofw = ofw = openfirm;
+#endif
chosenh = ofw_finddevice("/chosen");
ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
+ stdinh = be32toh(stdinh);
ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
+ stdouth = be32toh(stdouth);
ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs));
ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
@@ -537,8 +627,8 @@ load(const char *fname)
__syncicache(p, ph.p_memsz);
}
ofw_close(bootdev);
- (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0,
- ofw,NULL,0);
+ (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0,
+ realofw, NULL, 0);
}
static int
diff --git a/stand/powerpc/ofw/Makefile b/stand/powerpc/ofw/Makefile
index 7c2e97ebb382..03844301f094 100644
--- a/stand/powerpc/ofw/Makefile
+++ b/stand/powerpc/ofw/Makefile
@@ -28,11 +28,15 @@ CFLAGS.gfx_fb.c += -I${SRCTOP}/sys/teken
SRCS+= ofwfdt.c
.endif
-.if ${MACHINE_ARCH} == "powerpc64"
+.if ${MACHINE_ARCH:Mpowerpc64*} != ""
SRCS+= cas.c
CFLAGS+= -DCAS
.endif
+.if ${MACHINE_ARCH} == "powerpc64le"
+SRCS+= trampolineLE.S
+.endif
+
HELP_FILES= ${FDTSRC}/help.fdt
# Always add MI sources
@@ -44,7 +48,13 @@ HELP_FILES= ${FDTSRC}/help.fdt
RELOC?= 0x1C00000
CFLAGS+= -DRELOC=${RELOC} -g
-LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc
+LDFLAGS= -nostdlib -static
+
+.if ${MACHINE_ARCH} == "powerpc64le"
+LDFLAGS+= -T ${.CURDIR}/ldscript.powerpcle
+.else
+LDFLAGS+= -T ${.CURDIR}/ldscript.powerpc
+.endif
# Open Firmware standalone support library
LIBOFW= ${BOOTOBJ}/libofw/libofw.a
diff --git a/stand/powerpc/ofw/cas.c b/stand/powerpc/ofw/cas.c
index 6292e04794a8..0a12f31d1a1a 100644
--- a/stand/powerpc/ofw/cas.c
+++ b/stand/powerpc/ofw/cas.c
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
#include <openfirm.h>
#include <stand.h>
+#include <sys/endian.h>
+
/* #define CAS_DEBUG */
#ifdef CAS_DEBUG
#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
@@ -132,12 +134,12 @@ static struct ibm_arch_vec {
struct opt_vec5 vec5;
} __packed ibm_arch_vec = {
/* pvr_list */ {
- { PVR_CPU_MASK, PVR_CPU_P8 }, /* POWER8 */
- { PVR_CPU_MASK, PVR_CPU_P8E }, /* POWER8E */
- { PVR_CPU_MASK, PVR_CPU_P8NVL }, /* POWER8NVL */
- { PVR_CPU_MASK, PVR_CPU_P9 }, /* POWER9 */
- { PVR_ISA_MASK, PVR_ISA_207 }, /* All ISA 2.07 */
- { PVR_ISA_MASK, PVR_ISA_300 }, /* All ISA 3.00 */
+ { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8) },
+ { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8E) },
+ { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8NVL) },
+ { htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P9) },
+ { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_207) },
+ { htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_300) },
{ 0, 0xffffffffu } /* terminator */
},
4, /* num_opts (4 actually means 5 option vectors) */
diff --git a/stand/powerpc/ofw/ldscript.powerpcle b/stand/powerpc/ofw/ldscript.powerpcle
new file mode 100644
index 000000000000..813459b8c242
--- /dev/null
+++ b/stand/powerpc/ofw/ldscript.powerpcle
@@ -0,0 +1,142 @@
+/* $FreeBSD$ */
+
+OUTPUT_FORMAT("elf32-powerpcle-freebsd", "elf32-powerpcle-freebsd",
+ "elf32-powerpcle-freebsd")
+OUTPUT_ARCH(powerpcle:common)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib);
+PROVIDE (__stack = 0);
+PHDRS
+{
+ text PT_LOAD;
+}
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x02c00000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) } :text
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rela.init : { *(.rela.init) }
+ .rela.fini : { *(.rela.fini) }
+ .rela.bss : { *(.rela.bss) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.sdata : { *(.rela.sdata) }
+ .rela.sbss : { *(.rela.sbss) }
+ .rela.sdata2 : { *(.rela.sdata2) }
+ .rela.sbss2 : { *(.rela.sbss2) }
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0
+ _etext = .;
+ PROVIDE (etext = .);
+ .init : { *(.init) } =0
+ .fini : { *(.fini) } =0
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2) }
+ .sbss2 : { *(.sbss2) }
+ /* Adjust the address for the data segment to the next page up. */
+ . = ((. + 0x1000) & ~(0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .got1 : { *(.got1) }
+ .dynamic : { *(.dynamic) }
+ /* Put .ctors and .dtors next to the .got2 section, so that the pointers
+ get relocated with -mrelocatable. Also put in the .fixup pointers.
+ The current compiler no longer needs this, but keep it around for 2.7.2 */
+ PROVIDE (_GOT2_START_ = .);
+ .got2 : { *(.got2) }
+ PROVIDE (__CTOR_LIST__ = .);
+ .ctors : { *(.ctors) }
+ PROVIDE (__CTOR_END__ = .);
+ PROVIDE (__DTOR_LIST__ = .);
+ .dtors : { *(.dtors) }
+ PROVIDE (__DTOR_END__ = .);
+ PROVIDE (_FIXUP_START_ = .);
+ .fixup : { *(.fixup) }
+ PROVIDE (_FIXUP_END_ = .);
+ PROVIDE (_GOT2_END_ = .);
+ PROVIDE (_GOT_START_ = .);
+ .got : { *(.got) }
+ .got.plt : { *(.got.plt) }
+ PROVIDE (_GOT_END_ = .);
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ *(.sbss)
+ *(.scommon)
+ *(.dynsbss)
+ PROVIDE (__sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ PROVIDE (__bss_start = .);
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
+
diff --git a/stand/powerpc/ofw/main.c b/stand/powerpc/ofw/main.c
index 7b1ec384b6bd..81195d3f2444 100644
--- a/stand/powerpc/ofw/main.c
+++ b/stand/powerpc/ofw/main.c
@@ -28,11 +28,14 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/endian.h>
+
#include <stand.h>
#include "openfirm.h"
#include "libofw.h"
#include "bootstrap.h"
+#include <machine/asm.h>
#include <machine/psl.h>
struct arch_switch archsw; /* MI/MD interface boundary */
@@ -77,7 +80,7 @@ memsize(void)
memsz = 0;
memoryp = OF_instance_to_package(memory);
- sz = OF_getprop(memoryp, "reg", &reg, sizeof(reg));
+ sz = OF_getencprop(memoryp, "reg", &reg[0], sizeof(reg));
sz /= sizeof(reg[0]);
for (i = 0; i < sz; i += (acells + scells)) {
@@ -104,6 +107,26 @@ ppc64_autoload(void)
}
#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+/*
+ * In Little-endian, we cannot just branch to the client interface. Since
+ * the client interface is big endian, we have to rfid to it.
+ * Likewise, when execution resumes, we are in the wrong endianness so
+ * we must do a fixup before returning to the caller.
+ */
+static int (*openfirmware_entry)(void *);
+extern int openfirmware_trampoline(void *buf, int (*cb)(void *));
+
+/*
+ * Wrapper to pass the real entry point to our trampoline.
+ */
+static int
+openfirmware_docall(void *buf)
+{
+ return openfirmware_trampoline(buf, openfirmware_entry);
+}
+#endif
+
int
main(int (*openfirm)(void *))
{
@@ -117,13 +140,21 @@ main(int (*openfirm)(void *))
/*
* Initialise the Open Firmware routines by giving them the entry point.
*/
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /*
+ * Use a trampoline entry point for endian fixups.
+ */
+ openfirmware_entry = openfirm;
+ OF_init(openfirmware_docall);
+#else
OF_init(openfirm);
+#endif
root = OF_finddevice("/");
scells = acells = 1;
- OF_getprop(root, "#address-cells", &acells, sizeof(acells));
- OF_getprop(root, "#size-cells", &scells, sizeof(scells));
+ OF_getencprop(root, "#address-cells", &acells, sizeof(acells));
+ OF_getencprop(root, "#size-cells", &scells, sizeof(scells));
/*
* Initialise the heap as early as possible. Once this is done,
diff --git a/stand/powerpc/ofw/ofwfdt.c b/stand/powerpc/ofw/ofwfdt.c
index 92ae9f553134..ff1458ed2edf 100644
--- a/stand/powerpc/ofw/ofwfdt.c
+++ b/stand/powerpc/ofw/ofwfdt.c
@@ -112,7 +112,7 @@ ofwfdt_fixups(void *fdtp)
node = OF_finddevice("/rtas");
OF_package_to_path(node, path, sizeof(path));
- OF_getprop(node, "rtas-size", &len, sizeof(len));
+ OF_getencprop(node, "rtas-size", &len, sizeof(len));
/* Allocate memory */
rtasmem = OF_claim(0, len, 4096);
@@ -133,6 +133,7 @@ ofwfdt_fixups(void *fdtp)
sizeof(base));
/* Mark RTAS private data area reserved */
+ base = fdt32_to_cpu(base);
fdt_add_mem_rsv(fdtp, base, len);
} else {
/*
@@ -157,8 +158,7 @@ ofwfdt_fixups(void *fdtp)
for (i = 0; chosenprops[i] != NULL; i++) {
ihand = fdt_getprop(fdtp, offset, chosenprops[i], &len);
if (ihand != NULL && len == sizeof(*ihand)) {
- node = OF_instance_to_package(
- fdt32_to_cpu(*ihand));
+ node = OF_instance_to_package(*ihand);
if (OF_hasprop(node, "phandle"))
OF_getprop(node, "phandle", &node,
sizeof(node));
@@ -168,7 +168,6 @@ ofwfdt_fixups(void *fdtp)
else if (OF_hasprop(node, "ibm,phandle"))
OF_getprop(node, "ibm,phandle", &node,
sizeof(node));
- node = cpu_to_fdt32(node);
fdt_setprop(fdtp, offset, chosenprops[i], &node,
sizeof(node));
}
diff --git a/stand/powerpc/ofw/trampolineLE.S b/stand/powerpc/ofw/trampolineLE.S
new file mode 100644
index 000000000000..cd940fb3dfee
--- /dev/null
+++ b/stand/powerpc/ofw/trampolineLE.S
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Brandon Bergren <bdragon@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+/**
+ * int openfirmware_trampoline(void *buf, int (*cb)(void *));
+ */
+ASENTRY_NOPROF(openfirmware_trampoline)
+ mflr %r0
+ stw %r0, 4(%r1)
+ stwu %r1, -16(%r1)
+ stw %r30, 8(%r1)
+ /* Save current MSR for restoration post-call. */
+ mfmsr %r30
+ mr %r5, %r30
+ /* Remove LE bit from MSR. */
+ clrrwi %r5, %r5, 1
+ mtsrr0 %r4
+ mtsrr1 %r5
+ LOAD_LR_NIA
+1:
+ mflr %r4
+ addi %r4, %r4, (2f - 1b)
+ mtlr %r4
+ /* Switch to BE and transfer control to OF entry */
+ rfid
+2:
+ /* Control is returned here, but in BE. */
+ .long 0x05009f42 /* LOAD_LR_NIA */
+ /* 0: */
+ .long 0xa603db7f /* mtsrr1 %r30 */
+ .long 0xa602c87f /* mflr %r30 */
+ .long 0x1400de3b /* addi %r30, %r30, (1f - 0b) */
+ .long 0xa603da7f /* mtsrr0 %r30 */
+ .long 0x2400004c /* rfid */
+ /* 1: */
+1:
+ /* Back to normal. Tidy up for return. */
+ lwz %r30, 8(%r1)
+ lwz %r0, 20(%r1)
+ addi %r1, %r1, 16
+ mtlr %r0
+ blr
+ASEND(openfirmware_trampoline)