@@ -152,6 +152,6 @@ struct elf_info {
unsigned long elfoffset;
};
int parse_elf64(void *hdr, struct elf_info *info);
-int parse_elf32(void *hdr, struct elf_info *info);
+int parse_elf32(void *hdr, struct elf_info *info, int *little_endian);
#endif /* _PPC_BOOT_ELF_H_ */
@@ -14,6 +14,7 @@
#include "page.h"
#include "string.h"
#include "stdio.h"
+#include "swab.h"
int parse_elf64(void *hdr, struct elf_info *info)
{
@@ -47,7 +48,35 @@ int parse_elf64(void *hdr, struct elf_info *info)
return 1;
}
-int parse_elf32(void *hdr, struct elf_info *info)
+void byteswap_elf32(Elf32_Ehdr *elf32) {
+ Elf32_Phdr *elf32ph;
+
+ swab16s(&elf32->e_type);
+ swab16s(&elf32->e_machine);
+ swab32s(&elf32->e_version);
+ swab32s(&elf32->e_entry);
+ swab32s(&elf32->e_phoff);
+ swab32s(&elf32->e_shoff);
+ swab32s(&elf32->e_flags);
+ swab16s(&elf32->e_ehsize);
+ swab16s(&elf32->e_phentsize);
+ swab16s(&elf32->e_phnum);
+ swab16s(&elf32->e_shentsize);
+ swab16s(&elf32->e_shnum);
+ swab16s(&elf32->e_shstrndx);
+
+ elf32ph = (Elf32_Phdr *) ((unsigned long)elf32 + elf32->e_phoff);
+ swab32s(&elf32ph->p_type);
+ swab32s(&elf32ph->p_offset);
+ swab32s(&elf32ph->p_vaddr);
+ swab32s(&elf32ph->p_paddr);
+ swab32s(&elf32ph->p_filesz);
+ swab32s(&elf32ph->p_memsz);
+ swab32s(&elf32ph->p_flags);
+ swab32s(&elf32ph->p_align);
+}
+
+int parse_elf32(void *hdr, struct elf_info *info, int *little_endian)
{
Elf32_Ehdr *elf32 = hdr;
Elf32_Phdr *elf32ph;
@@ -57,9 +86,20 @@ int parse_elf32(void *hdr, struct elf_info *info)
elf32->e_ident[EI_MAG1] == ELFMAG1 &&
elf32->e_ident[EI_MAG2] == ELFMAG2 &&
elf32->e_ident[EI_MAG3] == ELFMAG3 &&
- elf32->e_ident[EI_CLASS] == ELFCLASS32 &&
- elf32->e_ident[EI_DATA] == ELFDATA2MSB &&
- (elf32->e_type == ET_EXEC ||
+ elf32->e_ident[EI_CLASS] == ELFCLASS32))
+ return 0;
+ switch(elf32->e_ident[EI_DATA]) {
+ case ELFDATA2MSB:
+ *little_endian = 0;
+ break;
+ case ELFDATA2LSB:
+ *little_endian = 1;
+ byteswap_elf32(elf32);
+ break;
+ default:
+ return 0;
+ }
+ if (!((elf32->e_type == ET_EXEC ||
elf32->e_type == ET_DYN) &&
elf32->e_machine == EM_PPC))
return 0;
@@ -27,7 +27,7 @@ struct addr_range {
#undef DEBUG
-static struct addr_range prep_kernel(void)
+static struct addr_range prep_kernel(int *little_endian)
{
char elfheader[256];
void *vmlinuz_addr = _vmlinux_start;
@@ -40,8 +40,10 @@ static struct addr_range prep_kernel(void)
gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
- if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
+ if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei, little_endian))
fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
+ if (*little_endian && !platform_ops.le_kentry)
+ fatal("Little Endian kernel unsupported on this platform!");
if (platform_ops.image_hdr)
platform_ops.image_hdr(elfheader);
@@ -166,8 +168,10 @@ void start(void)
{
struct addr_range vmlinux, initrd;
kernel_entry_t kentry;
+ le_kernel_entry_t le_kentry;
unsigned long ft_addr = 0;
void *chosen;
+ int little_endian = 0;
/* Do this first, because malloc() could clobber the loader's
* command line. Only use the loader command line if a
@@ -189,7 +193,7 @@ void start(void)
if (!chosen)
chosen = create_node(NULL, "chosen");
- vmlinux = prep_kernel();
+ vmlinux = prep_kernel(&little_endian);
initrd = prep_initrd(vmlinux, chosen,
loader_info.initrd_addr, loader_info.initrd_size);
prep_cmdline(chosen);
@@ -206,11 +210,21 @@ void start(void)
console_ops.close();
kentry = (kernel_entry_t) vmlinux.addr;
+ le_kentry = (le_kernel_entry_t)platform_ops.le_kentry;
if (ft_addr)
- kentry(ft_addr, 0, NULL);
+ if (little_endian)
+ le_kentry(ft_addr, 0, NULL, kentry);
+ else
+ kentry(ft_addr, 0, NULL);
else
- kentry((unsigned long)initrd.addr, initrd.size,
- loader_info.promptr);
+ if (little_endian)
+ le_kentry((unsigned long)initrd.addr, initrd.size,
+ loader_info.promptr, kentry);
+ else
+ kentry((unsigned long)initrd.addr, initrd.size,
+ loader_info.promptr);
+
+ kentry(ft_addr, 0, NULL);
/* console closed so printf in fatal below may not work */
fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r");
@@ -20,6 +20,8 @@
#define MAX_PROP_LEN 256 /* What should this be? */
typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
+typedef void (*le_kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5,
+ kernel_entry_t kentry);
/* Platform specific operations */
struct platform_ops {
@@ -30,6 +32,7 @@ struct platform_ops {
void * (*realloc)(void *ptr, unsigned long size);
void (*exit)(void);
void * (*vmlinux_alloc)(unsigned long size);
+ le_kernel_entry_t le_kentry;
};
extern struct platform_ops platform_ops;
@@ -519,6 +519,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
void *vmlinuz_addr = _vmlinux_start;
unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
char elfheader[256];
+ int little_endian;
if (dt_size <= 0) /* No fdt */
exit();
@@ -533,7 +534,9 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
gunzip_start(&gzstate, vmlinuz_addr, vmlinuz_size);
gunzip_exactly(&gzstate, elfheader, sizeof(elfheader));
- if (!parse_elf32(elfheader, &ei))
+ if (!parse_elf32(elfheader, &ei, &little_endian))
+ exit();
+ if (little_endian)
exit();
heap_start = (char *)(ei.memsize + ei.elfoffset); /* end of kernel*/
new file mode 100644
@@ -0,0 +1,26 @@
+#ifndef _SWAB_H_
+#define _SWAB_H_
+
+#include "types.h"
+
+static __inline__ void st_le16(volatile u16 *addr, const u16 val)
+{
+ __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
+}
+
+static inline void swab16s(u16 *addr)
+{
+ st_le16(addr, *addr);
+}
+
+static __inline__ void st_le32(volatile u32 *addr, const u32 val)
+{
+ __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
+}
+
+static inline void swab32s(u32 *addr)
+{
+ st_le32(addr, *addr);
+}
+
+#endif