mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC 0/5] add ELF support
@ 2012-12-19 21:29 Antony Pavlov
  2012-12-19 21:29 ` [RFC 1/5] resource: add create_resource() helper function Antony Pavlov
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

This __PRELIMINARY__ patch series introduces ELF files support in barebox
for MIPS architecture, but there is no barriers to adapt it for other
architectures.
The two last patches make possible to start linux from barebox
on qemu's mips malta board.

ELF support uses a relocator and makes possible to load ELF files,
that use the same address ranges that uses barebox,
e.g. it make possible to start barebox from barebox.

ELF support is based on kexec (git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git),
but I have discovered some problems with it:
* kexec tryes to duplicate the functions of the existing
barebox image handling framework so IMHO there is no reasons to import
kexec to barebox "as is".
* kexec introduces own data structures for memory regions handling,
but we already have 'struct resource'. So we have no need to import
this part either.
* kexec has coding style errors.

ELF-loader use the following algorithm:
1. it loads entire ELF-file to RAM;
2. it parses the ELF header and choose the loadable segments; (from kexec);
3. it traverses the available memory banks and checks that ELF
segments can be loaded;
4. it collects all used by barebox memory ranges (thanks to
request_sdram_region() we can do it), in addition it collects all
memory ranges that WILL be used after ELF segments relocation. So we
can determine all unused in any case memory ranges.
5. The relocation code copy all ELF segments to the unused memory.
Also it copy to the unsed memory the table of ELF segments. At last it
copy the relocatable "reboot code" to the unused memory. The "reboot
code" is written in ASM it can be started from any address.
It takes the address of the table of ELF segments and ELF entry point.
The "reboot code" use table to relocate segments and transfer
the control to the ELF entry point. (this part is based on mips linux
kernel kexec part).

ISSUES

* the relocator is always used;
* some checks from original kexec are skipped;
* just now there is no cache support for MIPS, so there are many conversions to KSEG1;
* check_room_for_elf() uses virt_to_phys();
* unused memory region for storing relocated data segments is selected trivialy: it is the biggest unused region; but we must check the size of the region, and, if necesssary try to find next region;
* some naming cleenup is needed;
* MIPS: linux cmdline handling is missing.

USAGE

barebox:/ iomem
0x00000000 - 0xffffffff (size 0x00000000) iomem
  0x1e000000 - 0x1e3fffff (size 0x00400000) cfi_flash0
  0xa0000000 - 0xafffffff (size 0x10000000) ram0
    0xa17f9000 - 0xa17fffff (size 0x00007000) stack
    0xa1800000 - 0xa27fffff (size 0x01000000) malloc space
    0xa2800000 - 0xa2d4e464 (size 0x0054e465) barebox
    0xa2d50114 - 0xa2d56bb0 (size 0x00006a9d) bss
  0xb80003f8 - 0xb80003ff (size 0x00000008) ns16550_serial0
barebox:/ bootm env/vmlinux.qemu-malta-be
handler failed with No error
barebox:/ iomem
0x00000000 - 0xffffffff (size 0x00000000) iomem
  0x1e000000 - 0x1e3fffff (size 0x00400000) cfi_flash0
  0xa0000000 - 0xafffffff (size 0x10000000) ram0
    0xa17f9000 - 0xa17fffff (size 0x00007000) stack
    0xa1800000 - 0xa27fffff (size 0x01000000) malloc space
    0xa2800000 - 0xa2d4e464 (size 0x0054e465) barebox
    0xa2d50114 - 0xa2d56bb0 (size 0x00006a9d) bss
    0xa2d56bc0 - 0xa316f29b (size 0x004186dc) kexec relocatable segments
    0xa316f2a0 - 0xa316f307 (size 0x00000068) kexec relocator
    0xa316f310 - 0xa316f31f (size 0x00000010) kexec control segments
  0xb80003f8 - 0xb80003ff (size 0x00000008) ns16550_serial0
barebox:/ go 0xa316f2a0
## Starting application at 0xa316f2a0 ...
Linux version 2.6.38.8-malta (antony@luz) (gcc version 4.4.6 (crosstool-NG 1.13.0) ) #1 Fri Oct 28 14:45:36 MSK 2011
memsize not set in boot prom, set to default (32Mb)
Config serial console: console=ttyS0,38400n8r
bootconsole [early0] enabled
CPU revision is: 00019300 (MIPS 24Kc)
FPU revision is: 00000000
...


[RFC 1/5] resource: add create_resource() helper function
[RFC 2/5] bootm: add very basic ELF support (stolen from kexec)
[RFC 3/5] MIPS: add ELF support
[RFC 4/5] MIPS: qemu-malta: add board label
[RFC 5/5] MIPS: qemu-malta: add YAMON-style GT64120 memory map

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 1/5] resource: add create_resource() helper function
  2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
@ 2012-12-19 21:29 ` Antony Pavlov
  2012-12-19 21:29 ` [RFC 2/5] bootm: add very basic ELF support (stolen from kexec) Antony Pavlov
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 common/resource.c      |   15 +++++++++++++++
 include/linux/ioport.h |    2 ++
 2 files changed, 17 insertions(+)

diff --git a/common/resource.c b/common/resource.c
index ea6abe8..a493e70 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -113,3 +113,18 @@ struct resource *request_iomem_region(const char *name,
 {
 	return request_region(&iomem_resource, name, start, end);
 }
+
+struct resource *create_resource(const char *name,
+	resource_size_t start, resource_size_t end)
+{
+	struct resource *t;
+
+	t = xzalloc(sizeof *t);
+	INIT_LIST_HEAD(&t->children);
+	t->parent = NULL;
+	t->name = xstrdup(name);
+	t->start = start;
+	t->end = end;
+
+	return t;
+}
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 6d6cd68..0a282ae 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -128,6 +128,8 @@ struct resource *request_region(struct resource *parent,
 		resource_size_t size);
 
 int release_region(struct resource *res);
+struct resource *create_resource(const char *name,
+	resource_size_t start, resource_size_t end);
 
 extern struct resource iomem_resource;
 
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 2/5] bootm: add very basic ELF support (stolen from kexec)
  2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
  2012-12-19 21:29 ` [RFC 1/5] resource: add create_resource() helper function Antony Pavlov
@ 2012-12-19 21:29 ` Antony Pavlov
  2012-12-21  7:49   ` Sascha Hauer
  2012-12-19 21:29 ` [RFC 3/5] MIPS: add ELF support Antony Pavlov
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 commands/Kconfig           |    7 +
 common/Kconfig             |    3 +
 common/filetype.c          |    5 +
 include/filetype.h         |    1 +
 lib/Makefile               |    1 +
 lib/kexec/Makefile         |    1 +
 lib/kexec/kexec-elf-exec.c |   85 ++++
 lib/kexec/kexec-elf.c      |  927 ++++++++++++++++++++++++++++++++++++++++++++
 lib/kexec/kexec-elf.h      |  110 ++++++
 lib/kexec/kexec.c          |  251 ++++++++++++
 lib/kexec/kexec.h          |  117 ++++++
 11 files changed, 1508 insertions(+)
 create mode 100644 lib/kexec/Makefile
 create mode 100644 lib/kexec/kexec-elf-exec.c
 create mode 100644 lib/kexec/kexec-elf.c
 create mode 100644 lib/kexec/kexec-elf.h
 create mode 100644 lib/kexec/kexec.c
 create mode 100644 lib/kexec/kexec.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 75ebfb8..aef4ecb 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -403,6 +403,13 @@ config CMD_BOOTM_AIMAGE
 	help
 	  Support using Android Images.
 
+config KEXEC
+	bool
+	prompt "bootm ELF image support"
+	depends on CMD_BOOTM && HAS_KEXEC
+	help
+	  Support using ELF Images.
+
 config CMD_UIMAGE
 	select UIMAGE
 	tristate
diff --git a/common/Kconfig b/common/Kconfig
index b60b78b..84cfc70 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -674,3 +674,6 @@ endmenu
 
 config HAS_DEBUG_LL
 	bool
+
+config HAS_KEXEC
+	bool
diff --git a/common/filetype.c b/common/filetype.c
index 748e364..1bb9cac 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -23,6 +23,7 @@
 #include <fs.h>
 #include <malloc.h>
 #include <errno.h>
+#include <elf.h>
 
 struct filetype_str {
 	const char *name;	/* human readable filetype */
@@ -48,6 +49,7 @@ static const struct filetype_str filetype_str[] = {
 	[filetype_bmp] = { "BMP image", "bmp" },
 	[filetype_png] = { "PNG image", "png" },
 	[filetype_ext] = { "ext filesystem", "ext" },
+	[filetype_elf] = { "ELF", "elf" },
 };
 
 const char *file_type_to_string(enum filetype f)
@@ -169,6 +171,9 @@ enum filetype file_detect_type(void *_buf, size_t bufsize)
 	if (buf16[512 + 28] == le16_to_cpu(0xef53))
 		return filetype_ext;
 
+	if (strncmp(buf8, ELFMAG, 4) == 0)
+		return filetype_elf;
+
 	return filetype_unknown;
 }
 
diff --git a/include/filetype.h b/include/filetype.h
index 91139db..d7c9c03 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -23,6 +23,7 @@ enum filetype {
 	filetype_bmp,
 	filetype_png,
 	filetype_ext,
+	filetype_elf,
 	filetype_max,
 };
 
diff --git a/lib/Makefile b/lib/Makefile
index 635d52e..10465ee 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_QSORT)	+= qsort.o
 obj-y			+= gui/
 obj-$(CONFIG_XYMODEM)	+= xymodem.o
 obj-y			+= unlink-recursive.o
+obj-$(CONFIG_KEXEC)	+= kexec/
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
new file mode 100644
index 0000000..6d84858
--- /dev/null
+++ b/lib/kexec/Makefile
@@ -0,0 +1 @@
+obj-y	+= kexec-elf-exec.o kexec-elf.o kexec.o
diff --git a/lib/kexec/kexec-elf-exec.c b/lib/kexec/kexec-elf-exec.c
new file mode 100644
index 0000000..39c55be
--- /dev/null
+++ b/lib/kexec/kexec-elf-exec.c
@@ -0,0 +1,85 @@
+#include <linux/types.h>
+#include <common.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <elf.h>
+#include "kexec.h"
+#include "kexec-elf.h"
+
+int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+				uint32_t flags)
+{
+	struct mem_phdr *phdr, *end_phdr;
+	int result;
+
+	result = build_elf_info(buf, len, ehdr, flags);
+	if (result < 0) {
+		return result;
+	}
+
+	if (ehdr->e_type != ET_EXEC) {
+		printf("Not ELF type ET_EXEC\n");
+		return -1;
+	}
+
+	if (!ehdr->e_phdr) {
+		printf("No ELF program header\n");
+		return -1;
+	}
+
+	end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
+	for (phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
+		/* Kexec does not support loading interpreters.
+		 * In addition this check keeps us from attempting
+		 * to kexec ordinay executables.
+		 */
+		if (phdr->p_type == PT_INTERP) {
+			printf("Requires an ELF interpreter\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
+{
+	int result;
+	size_t i;
+
+	if (!ehdr->e_phdr) {
+		printf("No program header?\n");
+		result = -1;
+		goto out;
+	}
+
+	/* Read in the PT_LOAD segments */
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		struct mem_phdr *phdr;
+		size_t size;
+
+		phdr = &ehdr->e_phdr[i];
+
+		if (phdr->p_type != PT_LOAD) {
+			continue;
+		}
+
+		size = phdr->p_filesz;
+
+		if (size > phdr->p_memsz) {
+			size = phdr->p_memsz;
+		}
+
+		add_segment(info,
+			phdr->p_data, size,
+			phdr->p_paddr, phdr->p_memsz);
+	}
+
+	result = 0;
+ out:
+	return result;
+}
diff --git a/lib/kexec/kexec-elf.c b/lib/kexec/kexec-elf.c
new file mode 100644
index 0000000..975d85f
--- /dev/null
+++ b/lib/kexec/kexec-elf.c
@@ -0,0 +1,927 @@
+#include <linux/types.h>
+#include <common.h>
+
+/* FIXME: virt_to_phys() */
+#include <asm/io.h>
+/* FIXME: need UINT64_MAX */
+//#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include "elf.h"
+#include "kexec.h"
+#include "kexec-elf.h"
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = le16_to_cpu(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = be16_to_cpu(value);
+	}
+
+	return value;
+}
+
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = le32_to_cpu(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = be32_to_cpu(value);
+	}
+
+	return value;
+}
+
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = le64_to_cpu(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = be64_to_cpu(value);
+	}
+
+	return value;
+}
+
+uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = cpu_to_le16(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = cpu_to_be16(value);
+	}
+
+	return value;
+}
+
+uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = cpu_to_le32(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = cpu_to_be32(value);
+	}
+
+	return value;
+}
+
+uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value)
+{
+	if (ehdr->ei_data == ELFDATA2LSB) {
+		value = cpu_to_le64(value);
+	} else if (ehdr->ei_data == ELFDATA2MSB) {
+		value = cpu_to_be64(value);
+	}
+
+	return value;
+}
+
+#define ELF32_MAX 0xffffffff
+#define ELF64_MAX 0xffffffffffffffff
+#if ELF64_MAX > ULONG_MAX
+#undef ELF64_MAX
+#define ELF64_MAX ULONG_MAX
+#endif
+
+unsigned long elf_max_addr(const struct mem_ehdr *ehdr)
+{
+	unsigned long max_addr = 0;
+
+	if (ehdr->ei_class == ELFCLASS32) {
+		max_addr = ELF32_MAX;
+	} else if (ehdr->ei_class == ELFCLASS64) {
+		max_addr = ELF64_MAX;
+	}
+
+	return max_addr;
+}
+
+static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+	Elf32_Ehdr lehdr;
+
+	if ((size_t)len < sizeof(lehdr)) {
+		printf("Buffer is too small to hold ELF header\n");
+		return -1;
+	}
+
+	memcpy(&lehdr, buf, sizeof(lehdr));
+	if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf32_Ehdr)) {
+		printf("Bad ELF header size\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
+		printf("ELF e_entry is too large\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
+		printf("ELF e_phoff is too large\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
+		printf("ELF e_shoff is too large\n");
+		return -1;
+	}
+
+	ehdr->e_type      = elf16_to_cpu(ehdr, lehdr.e_type);
+	ehdr->e_machine   = elf16_to_cpu(ehdr, lehdr.e_machine);
+	ehdr->e_version   = elf32_to_cpu(ehdr, lehdr.e_version);
+	ehdr->e_entry     = elf32_to_cpu(ehdr, lehdr.e_entry);
+	ehdr->e_phoff     = elf32_to_cpu(ehdr, lehdr.e_phoff);
+	ehdr->e_shoff     = elf32_to_cpu(ehdr, lehdr.e_shoff);
+	ehdr->e_flags     = elf32_to_cpu(ehdr, lehdr.e_flags);
+	ehdr->e_phnum     = elf16_to_cpu(ehdr, lehdr.e_phnum);
+	ehdr->e_shnum     = elf16_to_cpu(ehdr, lehdr.e_shnum);
+	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
+
+	if ((ehdr->e_phnum > 0) &&
+		(elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr)))
+	{
+		printf("ELF bad program header size\n");
+		return -1;
+	}
+
+	if ((ehdr->e_shnum > 0) &&
+		(elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf32_Shdr)))
+	{
+		printf("ELF bad section header size\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+	Elf64_Ehdr lehdr;
+
+	if ((size_t)len < sizeof(lehdr)) {
+		printf("Buffer is too small to hold ELF header\n");
+
+		return -1;
+	}
+
+	memcpy(&lehdr, buf, sizeof(lehdr));
+	if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf64_Ehdr)) {
+		printf("Bad ELF header size\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
+		printf("ELF e_entry is too large\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
+		printf("ELF e_phoff is too large\n");
+		return -1;
+	}
+
+	if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
+		printf("ELF e_shoff is too large\n");
+		return -1;
+	}
+
+	ehdr->e_type      = elf16_to_cpu(ehdr, lehdr.e_type);
+	ehdr->e_machine   = elf16_to_cpu(ehdr, lehdr.e_machine);
+	ehdr->e_version   = elf32_to_cpu(ehdr, lehdr.e_version);
+	ehdr->e_entry     = elf64_to_cpu(ehdr, lehdr.e_entry);
+	ehdr->e_phoff     = elf64_to_cpu(ehdr, lehdr.e_phoff);
+	ehdr->e_shoff     = elf64_to_cpu(ehdr, lehdr.e_shoff);
+	ehdr->e_flags     = elf32_to_cpu(ehdr, lehdr.e_flags);
+	ehdr->e_phnum     = elf16_to_cpu(ehdr, lehdr.e_phnum);
+	ehdr->e_shnum     = elf16_to_cpu(ehdr, lehdr.e_shnum);
+	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
+
+	if ((ehdr->e_phnum > 0) &&
+		(elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr)))
+	{
+		/* Invalid program header size */
+		printf("ELF bad program header size\n");
+		return -1;
+	}
+
+	if ((ehdr->e_shnum > 0) &&
+		(elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf64_Shdr)))
+	{
+		printf("Bad ELF section header size\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+	unsigned char e_ident[EI_NIDENT];
+	int result;
+
+	memset(ehdr, 0, sizeof(*ehdr));
+
+	if ((size_t)len < sizeof(e_ident)) {
+		printf("Buffer is too small to hold ELF e_ident\n");
+
+		return -1;
+	}
+
+	memcpy(e_ident, buf, sizeof(e_ident));
+
+	ehdr->ei_class   = e_ident[EI_CLASS];
+	ehdr->ei_data    = e_ident[EI_DATA];
+	if (	(ehdr->ei_class != ELFCLASS32) &&
+		(ehdr->ei_class != ELFCLASS64))
+	{
+		printf("Not a supported ELF class\n");
+		return -1;
+	}
+
+	if (	(ehdr->ei_data != ELFDATA2LSB) &&
+		(ehdr->ei_data != ELFDATA2MSB))
+	{
+		printf("Not a supported ELF data format\n");
+		return -1;
+	}
+
+	result = -1;
+	if (ehdr->ei_class == ELFCLASS32) {
+		result = build_mem_elf32_ehdr(buf, len, ehdr);
+	} else if (ehdr->ei_class == ELFCLASS64) {
+		result = build_mem_elf64_ehdr(buf, len, ehdr);
+	}
+
+	if (result < 0) {
+		return result;
+	}
+
+	if ((e_ident[EI_VERSION] != EV_CURRENT) ||
+		(ehdr->e_version != EV_CURRENT))
+	{
+		printf("Unknown ELF version\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int build_mem_elf32_phdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+	struct mem_phdr *phdr;
+	const char *pbuf;
+	Elf32_Phdr lphdr;
+
+	pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr));
+	phdr = &ehdr->e_phdr[idx];
+	memcpy(&lphdr, pbuf, sizeof(lphdr));
+
+	if (	(elf32_to_cpu(ehdr, lphdr.p_filesz) > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lphdr.p_memsz)  > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lphdr.p_offset) > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lphdr.p_paddr)  > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lphdr.p_vaddr)  > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lphdr.p_align)  > UINT32_MAX))
+	{
+		printf("Program segment size out of range\n");
+		return -1;
+	}
+
+	phdr->p_type   = elf32_to_cpu(ehdr, lphdr.p_type);
+	phdr->p_paddr  = elf32_to_cpu(ehdr, lphdr.p_paddr);
+	phdr->p_vaddr  = elf32_to_cpu(ehdr, lphdr.p_vaddr);
+	phdr->p_filesz = elf32_to_cpu(ehdr, lphdr.p_filesz);
+	phdr->p_memsz  = elf32_to_cpu(ehdr, lphdr.p_memsz);
+	phdr->p_offset = elf32_to_cpu(ehdr, lphdr.p_offset);
+	phdr->p_flags  = elf32_to_cpu(ehdr, lphdr.p_flags);
+	phdr->p_align  = elf32_to_cpu(ehdr, lphdr.p_align);
+
+	return 0;
+}
+
+static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+	struct mem_phdr *phdr;
+	const char *pbuf;
+	Elf64_Phdr lphdr;
+
+	pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr));
+	phdr = &ehdr->e_phdr[idx];
+	memcpy(&lphdr, pbuf, sizeof(lphdr));
+
+#if 0
+	if (	(elf64_to_cpu(ehdr, lphdr.p_filesz) > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lphdr.p_memsz)  > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lphdr.p_offset) > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lphdr.p_paddr)  > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lphdr.p_vaddr)  > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lphdr.p_align)  > UINT64_MAX))
+	{
+		printf("Program segment size out of range\n");
+		return -1;
+	}
+#endif
+
+	phdr->p_type   = elf32_to_cpu(ehdr, lphdr.p_type);
+	phdr->p_paddr  = elf64_to_cpu(ehdr, lphdr.p_paddr);
+	phdr->p_vaddr  = elf64_to_cpu(ehdr, lphdr.p_vaddr);
+	phdr->p_filesz = elf64_to_cpu(ehdr, lphdr.p_filesz);
+	phdr->p_memsz  = elf64_to_cpu(ehdr, lphdr.p_memsz);
+	phdr->p_offset = elf64_to_cpu(ehdr, lphdr.p_offset);
+	phdr->p_flags  = elf32_to_cpu(ehdr, lphdr.p_flags);
+	phdr->p_align  = elf64_to_cpu(ehdr, lphdr.p_align);
+
+	return 0;
+}
+
+static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr,
+				uint32_t flags)
+{
+	size_t phdr_size, mem_phdr_size, i;
+
+	/* e_phnum is at most 65535 so calculating
+	 * the size of the program header cannot overflow.
+	 */
+	/* Is the program header in the file buffer? */
+	phdr_size = 0;
+	if (ehdr->ei_class == ELFCLASS32) {
+		phdr_size = sizeof(Elf32_Phdr);
+	} else if (ehdr->ei_class == ELFCLASS64) {
+		phdr_size = sizeof(Elf64_Phdr);
+	} else {
+		printf("Invalid ei_class?\n");
+		return -1;
+	}
+	phdr_size *= ehdr->e_phnum;
+
+#if 0
+	if ((uintmax_t)(ehdr->e_phoff + phdr_size) > (uintmax_t)len) {
+		/* The program header did not fit in the file buffer */
+		printf("ELF program headers truncated"
+			" have %ju bytes need %ju bytes\n",
+			(uintmax_t)len,
+			(uintmax_t)(ehdr->e_phoff + phdr_size));
+		return -1;
+	}
+#endif
+
+	/* Allocate the e_phdr array */
+	mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum;
+	ehdr->e_phdr = xmalloc(mem_phdr_size);
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		struct mem_phdr *phdr;
+		int result;
+
+		result = -1;
+		if (ehdr->ei_class == ELFCLASS32) {
+			result = build_mem_elf32_phdr(buf, ehdr, i);
+
+		} else if (ehdr->ei_class == ELFCLASS64) {
+			result = build_mem_elf64_phdr(buf, ehdr, i);
+		}
+
+		if (result < 0) {
+			return result;
+		}
+
+		/* Check the program headers to be certain
+		 * they are safe to use.
+		 * Skip the check if ELF_SKIP_FILESZ_CHECK is set.
+		 */
+		phdr = &ehdr->e_phdr[i];
+#if 0
+		if (!(flags & ELF_SKIP_FILESZ_CHECK)
+			&& (uintmax_t)(phdr->p_offset + phdr->p_filesz) >
+			   (uintmax_t)len) {
+			/* The segment does not fit in the buffer */
+			printf("ELF segment not in file\n");
+			return -1;
+		}
+#endif
+		if ((phdr->p_paddr + phdr->p_memsz) < phdr->p_paddr) {
+			/* The memory address wraps */
+			printf("ELF address wrap around\n");
+			return -1;
+		}
+
+		/* Remember where the segment lives in the buffer */
+		phdr->p_data = buf + phdr->p_offset;
+	}
+
+	return 0;
+}
+
+static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+	struct mem_shdr *shdr;
+	const char *sbuf;
+	int size_ok;
+	Elf32_Shdr lshdr;
+
+	sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr));
+	shdr = &ehdr->e_shdr[idx];
+	memcpy(&lshdr, sbuf, sizeof(lshdr));
+
+	if (	(elf32_to_cpu(ehdr, lshdr.sh_flags)     > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lshdr.sh_addr)      > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lshdr.sh_offset)    > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lshdr.sh_size)      > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lshdr.sh_addralign) > UINT32_MAX) ||
+		(elf32_to_cpu(ehdr, lshdr.sh_entsize)   > UINT32_MAX))
+	{
+		printf("Program section size out of range\n");
+		return -1;
+	}
+
+	shdr->sh_name      = elf32_to_cpu(ehdr, lshdr.sh_name);
+	shdr->sh_type      = elf32_to_cpu(ehdr, lshdr.sh_type);
+	shdr->sh_flags     = elf32_to_cpu(ehdr, lshdr.sh_flags);
+	shdr->sh_addr      = elf32_to_cpu(ehdr, lshdr.sh_addr);
+	shdr->sh_offset    = elf32_to_cpu(ehdr, lshdr.sh_offset);
+	shdr->sh_size      = elf32_to_cpu(ehdr, lshdr.sh_size);
+	shdr->sh_link      = elf32_to_cpu(ehdr, lshdr.sh_link);
+	shdr->sh_info      = elf32_to_cpu(ehdr, lshdr.sh_info);
+	shdr->sh_addralign = elf32_to_cpu(ehdr, lshdr.sh_addralign);
+	shdr->sh_entsize   = elf32_to_cpu(ehdr, lshdr.sh_entsize);
+
+	/* Now verify sh_entsize */
+	size_ok = 0;
+	switch(shdr->sh_type) {
+	case SHT_SYMTAB:
+		size_ok = shdr->sh_entsize == sizeof(Elf32_Sym);
+		break;
+	case SHT_RELA:
+		size_ok = shdr->sh_entsize == sizeof(Elf32_Rela);
+		break;
+	case SHT_DYNAMIC:
+		size_ok = shdr->sh_entsize == sizeof(Elf32_Dyn);
+		break;
+	case SHT_REL:
+		size_ok = shdr->sh_entsize == sizeof(Elf32_Rel);
+		break;
+	case SHT_NOTE:
+	case SHT_NULL:
+	case SHT_PROGBITS:
+	case SHT_HASH:
+	case SHT_NOBITS:
+	default:
+		/* This is a section whose entsize requirements
+		 * I don't care about.  If I don't know about
+		 * the section I can't care about it's entsize
+		 * requirements.
+		 */
+		size_ok = 1;
+		break;
+	}
+
+	if (!size_ok) {
+		printf("Bad section header(%x) entsize: %lld\n",
+			shdr->sh_type, shdr->sh_entsize);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+	struct mem_shdr *shdr;
+	const char *sbuf;
+	int size_ok;
+	Elf64_Shdr lshdr;
+
+	sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr));
+	shdr = &ehdr->e_shdr[idx];
+	memcpy(&lshdr, sbuf, sizeof(lshdr));
+
+#if 0
+	if (	(elf64_to_cpu(ehdr, lshdr.sh_flags)     > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lshdr.sh_addr)      > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lshdr.sh_offset)    > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lshdr.sh_size)      > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lshdr.sh_addralign) > UINT64_MAX) ||
+		(elf64_to_cpu(ehdr, lshdr.sh_entsize)   > UINT64_MAX))
+	{
+		printf("Program section size out of range\n");
+		return -1;
+	}
+#endif
+
+	shdr->sh_name      = elf32_to_cpu(ehdr, lshdr.sh_name);
+	shdr->sh_type      = elf32_to_cpu(ehdr, lshdr.sh_type);
+	shdr->sh_flags     = elf64_to_cpu(ehdr, lshdr.sh_flags);
+	shdr->sh_addr      = elf64_to_cpu(ehdr, lshdr.sh_addr);
+	shdr->sh_offset    = elf64_to_cpu(ehdr, lshdr.sh_offset);
+	shdr->sh_size      = elf64_to_cpu(ehdr, lshdr.sh_size);
+	shdr->sh_link      = elf32_to_cpu(ehdr, lshdr.sh_link);
+	shdr->sh_info      = elf32_to_cpu(ehdr, lshdr.sh_info);
+	shdr->sh_addralign = elf64_to_cpu(ehdr, lshdr.sh_addralign);
+	shdr->sh_entsize   = elf64_to_cpu(ehdr, lshdr.sh_entsize);
+
+	/* Now verify sh_entsize */
+	size_ok = 0;
+	switch(shdr->sh_type) {
+	case SHT_SYMTAB:
+		size_ok = shdr->sh_entsize == sizeof(Elf64_Sym);
+		break;
+	case SHT_RELA:
+		size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
+		break;
+	case SHT_DYNAMIC:
+		size_ok = shdr->sh_entsize == sizeof(Elf64_Dyn);
+		break;
+	case SHT_REL:
+		size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
+		break;
+	case SHT_NOTE:
+	case SHT_NULL:
+	case SHT_PROGBITS:
+	case SHT_HASH:
+	case SHT_NOBITS:
+	default:
+		/* This is a section whose entsize requirements
+		 * I don't care about.  If I don't know about
+		 * the section I can't care about it's entsize
+		 * requirements.
+		 */
+		size_ok = 1;
+		break;
+	}
+
+	if (!size_ok) {
+		printf("Bad section header(%x) entsize: %lld\n",
+			shdr->sh_type, shdr->sh_entsize);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr,
+				uint32_t flags)
+{
+	size_t shdr_size, mem_shdr_size, i;
+
+	/* e_shnum is at most 65536 so calculating
+	 * the size of the section header cannot overflow.
+	 */
+	/* Is the program header in the file buffer? */
+	shdr_size = 0;
+	if (ehdr->ei_class == ELFCLASS32) {
+		shdr_size = sizeof(Elf32_Shdr);
+	} else if (ehdr->ei_class == ELFCLASS64) {
+		shdr_size = sizeof(Elf64_Shdr);
+	} else {
+		printf("Invalid ei_class?\n");
+		return -1;
+	}
+	shdr_size *= ehdr->e_shnum;
+#if 0
+	if ((uintmax_t)(ehdr->e_shoff + shdr_size) > (uintmax_t)len) {
+		/* The section header did not fit in the file buffer */
+		printf("ELF section header does not fit in file\n");
+		return -1;
+	}
+#endif
+
+	/* Allocate the e_shdr array */
+	mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum;
+	ehdr->e_shdr = xmalloc(mem_shdr_size);
+
+	for (i = 0; i < ehdr->e_shnum; i++) {
+		struct mem_shdr *shdr;
+		int result;
+
+		result = -1;
+		if (ehdr->ei_class == ELFCLASS32) {
+			result = build_mem_elf32_shdr(buf, ehdr, i);
+		} else if (ehdr->ei_class == ELFCLASS64) {
+			result = build_mem_elf64_shdr(buf, ehdr, i);
+		}
+
+		if (result < 0) {
+			return result;
+		}
+
+		/* Check the section headers to be certain
+		 * they are safe to use.
+		 * Skip the check if ELF_SKIP_FILESZ_CHECK is set.
+		 */
+		shdr = &ehdr->e_shdr[i];
+#if 0
+		if (!(flags & ELF_SKIP_FILESZ_CHECK)
+			&& (shdr->sh_type != SHT_NOBITS)
+			&& (uintmax_t)(shdr->sh_offset + shdr->sh_size) >
+			   (uintmax_t)len) {
+			/* The section does not fit in the buffer */
+			printf("ELF section %zd not in file\n", i);
+			return -1;
+		}
+#endif
+
+		if ((shdr->sh_addr + shdr->sh_size) < shdr->sh_addr) {
+			printf("ELF address wrap around\n");
+			return -1;
+		}
+
+		/* Remember where the section lives in the buffer */
+		shdr->sh_data = (unsigned char *)(buf + shdr->sh_offset);
+	}
+
+	return 0;
+}
+
+static void read_nhdr(const struct mem_ehdr *ehdr,
+	ElfNN_Nhdr *hdr, const unsigned char *note)
+{
+	memcpy(hdr, note, sizeof(*hdr));
+	hdr->n_namesz = elf32_to_cpu(ehdr, hdr->n_namesz);
+	hdr->n_descsz = elf32_to_cpu(ehdr, hdr->n_descsz);
+	hdr->n_type   = elf32_to_cpu(ehdr, hdr->n_type);
+}
+
+static int build_mem_notes(struct mem_ehdr *ehdr)
+{
+	const unsigned char *note_start, *note_end, *note;
+	size_t note_size, i;
+
+	/* First find the note segment or section */
+	note_start = note_end = NULL;
+
+	for (i = 0; !note_start && (i < ehdr->e_phnum); i++) {
+		struct mem_phdr *phdr = &ehdr->e_phdr[i];
+		/*
+		 * binutils <= 2.17 has a bug where it can create the
+		 * PT_NOTE segment with an offset of 0. Therefore
+		 * check p_offset > 0.
+		 *
+		 * See: http://sourceware.org/bugzilla/show_bug.cgi?id=594
+		 */
+		if (phdr->p_type == PT_NOTE && phdr->p_offset) {
+			note_start = (unsigned char *)phdr->p_data;
+			note_end = note_start + phdr->p_filesz;
+		}
+	}
+
+	for (i = 0; !note_start && (i < ehdr->e_shnum); i++) {
+		struct mem_shdr *shdr = &ehdr->e_shdr[i];
+		if (shdr->sh_type == SHT_NOTE) {
+			note_start = shdr->sh_data;
+			note_end = note_start + shdr->sh_size;
+		}
+	}
+
+	if (!note_start) {
+		return 0;
+	}
+
+	/* Walk through and count the notes */
+	ehdr->e_notenum = 0;
+	for (note = note_start; note < note_end; note+= note_size) {
+		ElfNN_Nhdr hdr;
+		read_nhdr(ehdr, &hdr, note);
+		note_size  = sizeof(hdr);
+		note_size += (hdr.n_namesz + 3) & ~3;
+		note_size += (hdr.n_descsz + 3) & ~3;
+		ehdr->e_notenum += 1;
+	}
+
+	/* Now walk and normalize the notes */
+	ehdr->e_note = xmalloc(sizeof(*ehdr->e_note) * ehdr->e_notenum);
+	for (i = 0, note = note_start; note < note_end; note+= note_size, i++) {
+		const unsigned char *name, *desc;
+		ElfNN_Nhdr hdr;
+		read_nhdr(ehdr, &hdr, note);
+		note_size  = sizeof(hdr);
+		name       = note + note_size;
+		note_size += (hdr.n_namesz + 3) & ~3;
+		desc       = note + note_size;
+		note_size += (hdr.n_descsz + 3) & ~3;
+
+		if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) {
+			/* If note name string is not null terminated, just
+			 * warn user about it and continue processing. This
+			 * allows us to parse /proc/kcore on older kernels
+			 * where /proc/kcore elf notes were not null
+			 * terminated. It has been fixed in 2.6.19.
+			 */
+			printf("Warning: Elf Note name is not null "
+					"terminated\n");
+		}
+		ehdr->e_note[i].n_type = hdr.n_type;
+		ehdr->e_note[i].n_name = (char *)name;
+		ehdr->e_note[i].n_desc = desc;
+		ehdr->e_note[i].n_descsz = hdr.n_descsz;
+
+	}
+
+	return 0;
+}
+
+void free_elf_info(struct mem_ehdr *ehdr)
+{
+	free(ehdr->e_phdr);
+	free(ehdr->e_shdr);
+	memset(ehdr, 0, sizeof(*ehdr));
+}
+
+int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+			uint32_t flags)
+{
+	int result;
+
+	result = build_mem_ehdr(buf, len, ehdr);
+	if (result < 0) {
+		return result;
+	}
+
+	if ((ehdr->e_phoff > 0) && (ehdr->e_phnum > 0)) {
+		result = build_mem_phdrs(buf, len, ehdr, flags);
+		if (result < 0) {
+			free_elf_info(ehdr);
+			return result;
+		}
+	}
+
+	if ((ehdr->e_shoff > 0) && (ehdr->e_shnum > 0)) {
+		result = build_mem_shdrs(buf, len, ehdr, flags);
+		if (result < 0) {
+			free_elf_info(ehdr);
+			return result;
+		}
+	}
+
+	result = build_mem_notes(ehdr);
+	if (result < 0) {
+		free_elf_info(ehdr);
+		return result;
+	}
+
+	return 0;
+}
+
+int check_room_for_elf(struct list_head *elf_segments)
+{
+	struct memory_bank *bank;
+	struct resource *res, *r;
+
+	list_for_each_entry(r, elf_segments, sibling) {
+		int got_bank;
+
+		got_bank = 0;
+		for_each_memory_bank(bank) {
+			resource_size_t start, end;
+
+			res = bank->res;
+
+			/* FIXME: no need in the future? */
+			start = virt_to_phys(res->start);
+			end = virt_to_phys(res->end);
+
+			if ((start <= r->start) && (end >= r->end)) {
+				got_bank = 1;
+				break;
+			}
+		}
+
+		if (!got_bank)
+			return -1;
+	}
+
+	return 0;
+}
+
+/* sort by size */
+static int compare(struct list_head *a, struct list_head *b)
+{
+	struct resource *ra = (struct resource *)list_entry(a, struct resource, sibling);
+	struct resource *rb = (struct resource *)list_entry(b, struct resource, sibling);
+	resource_size_t sa, sb;
+
+	sa = ra->end - ra->start;
+	sb = rb->end - rb->start;
+
+	if (sa > sb)
+		return -1;
+	if (sa < sb)
+		return 1;
+	return 0;
+}
+
+void list_add_used_region(struct list_head *new, struct list_head *head)
+{
+	struct list_head *pos, *insert = head;
+	struct resource *rb =
+		(struct resource *)list_entry(new, struct resource, sibling);
+	struct list_head *n;
+
+	/* rb --- new region */
+	list_for_each_safe(pos, n, head) {
+		struct resource *ra = (struct resource *)list_entry(pos, struct resource, sibling);
+
+		if (((rb->end >= ra->start) && (rb->end <= ra->end))
+			|| ((rb->start >= ra->start) && (rb->start <= ra->end))
+			|| ((rb->start >= ra->start) && (rb->end <= ra->end))
+			|| ((ra->start >= rb->start) && (ra->end <= rb->end))
+			|| (ra->start == rb->end + 1)
+			|| (rb->start == ra->end + 1)) {
+			rb->start = min(ra->start, rb->start);
+			rb->end = max(ra->end, rb->end);
+			rb->name = "join";
+			list_del(pos);
+		}
+	}
+
+	list_for_each(pos, head) {
+		struct resource *ra = (struct resource *)list_entry(pos, struct resource, sibling);
+
+		if (ra->start < rb->start)
+			continue;
+
+		insert = pos;
+		break;
+	}
+
+	list_add_tail(new, insert);
+}
+
+resource_size_t dcheck_res(struct list_head *elf_segments)
+{
+	struct memory_bank *bank;
+	struct resource *res, *r, *t;
+
+	LIST_HEAD(elf_relocate_banks);
+	LIST_HEAD(elf_relocate_banks_size_sorted);
+	LIST_HEAD(used_regions);
+
+	for_each_memory_bank(bank) {
+		res = bank->res;
+
+		list_for_each_entry(r, &res->children, sibling) {
+			resource_size_t start, end;
+
+			/* FIXME: no need in the future */
+			start = virt_to_phys(r->start);
+			end = virt_to_phys(r->end);
+
+			t = create_resource(r->name, start, end);
+			list_add_used_region(&t->sibling, &used_regions);
+		}
+	}
+
+	list_for_each_entry(r, elf_segments, sibling) {
+		t = create_resource(r->name, r->start, r->end);
+		list_add_used_region(&t->sibling, &used_regions);
+	}
+
+	for_each_memory_bank(bank) {
+		resource_size_t start;
+
+		res = bank->res;
+		res = create_resource("tmp", virt_to_phys(res->start),
+				virt_to_phys(res->end));
+		start = res->start;
+
+		list_for_each_entry(r, &used_regions, sibling) {
+			if (res->start > r->end)
+				continue;
+
+			if (res->end < r->start)
+				continue;
+
+			if (r->start - start) {
+				struct resource *t;
+
+				t = create_resource("ELF buffer", start, r->start - 1);
+				list_add_used_region(&t->sibling, &elf_relocate_banks);
+			}
+			start = r->end + 1;
+		}
+
+		if (res->end - start) {
+			struct resource *t;
+
+			t = create_resource("ELF buffer", start, res->end);
+			list_add_used_region(&t->sibling, &elf_relocate_banks);
+		}
+	}
+
+	list_for_each_entry(r, &elf_relocate_banks, sibling) {
+		struct resource *t;
+
+		t = create_resource("ELF buffer", r->start, r->end);
+		list_add_sort(&t->sibling,
+			&elf_relocate_banks_size_sorted, compare);
+	}
+
+	r = list_first_entry(&elf_relocate_banks_size_sorted, struct resource, sibling);
+
+	/* FIXME */
+	return r->start;
+}
diff --git a/lib/kexec/kexec-elf.h b/lib/kexec/kexec-elf.h
new file mode 100644
index 0000000..1e1c9e2
--- /dev/null
+++ b/lib/kexec/kexec-elf.h
@@ -0,0 +1,110 @@
+#ifndef KEXEC_ELF_H
+#define KEXEC_ELF_H
+
+struct kexec_info;
+
+struct mem_ehdr {
+	unsigned ei_class;
+	unsigned ei_data;
+	unsigned e_type;
+	unsigned e_machine;
+	unsigned e_version;
+	unsigned e_flags;
+	unsigned e_phnum;
+	unsigned e_shnum;
+	unsigned e_shstrndx;
+	unsigned long long e_entry;
+	unsigned long long e_phoff;
+	unsigned long long e_shoff;
+	unsigned e_notenum;
+	struct mem_phdr *e_phdr;
+	struct mem_shdr *e_shdr;
+	struct mem_note *e_note;
+	unsigned long rel_addr, rel_size;
+};
+
+struct mem_phdr {
+	unsigned long long p_paddr;
+	unsigned long long p_vaddr;
+	unsigned long long p_filesz;
+	unsigned long long p_memsz;
+	unsigned long long p_offset;
+	const char *p_data;
+	unsigned p_type;
+	unsigned p_flags;
+	unsigned long long p_align;
+};
+
+struct mem_shdr {
+	unsigned sh_name;
+	unsigned sh_type;
+	unsigned long long sh_flags;
+	unsigned long long sh_addr;
+	unsigned long long sh_offset;
+	unsigned long long sh_size;
+	unsigned sh_link;
+	unsigned sh_info;
+	unsigned long long sh_addralign;
+	unsigned long long sh_entsize;
+	const unsigned char *sh_data;
+};
+
+struct mem_sym {
+	unsigned long st_name;   /* Symbol name (string tbl index) */
+	unsigned char st_info;   /* No defined meaning, 0 */
+	unsigned char st_other;  /* Symbol type and binding */
+	unsigned st_shndx;  /* Section index */
+	unsigned long long st_value;  /* Symbol value */
+	unsigned long long st_size;   /* Symbol size */
+};
+
+struct  mem_rela {
+	unsigned long long r_offset;
+	unsigned r_sym;
+	unsigned r_type;
+	unsigned long long r_addend;
+};
+
+struct mem_note {
+	unsigned n_type;
+	unsigned n_descsz;
+	const char *n_name;
+	const void *n_desc;
+};
+
+/* The definition of an ELF note does not vary depending
+ * on ELFCLASS.
+ */
+typedef struct
+{
+	uint32_t n_namesz;		/* Length of the note's name.  */
+	uint32_t n_descsz;		/* Length of the note's descriptor.  */
+	uint32_t n_type;		/* Type of the note.  */
+} ElfNN_Nhdr;
+
+/* Misc flags */
+
+#define ELF_SKIP_FILESZ_CHECK		0x00000001
+
+extern void free_elf_info(struct mem_ehdr *ehdr);
+extern int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+				uint32_t flags);
+extern int build_elf_exec_info(const char *buf, off_t len,
+				struct mem_ehdr *ehdr, uint32_t flags);
+
+extern int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info);
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value);
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value);
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value);
+
+uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value);
+uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value);
+uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value);
+
+unsigned long elf_max_addr(const struct mem_ehdr *ehdr);
+int check_room_for_elf(struct list_head *elf_segments);
+resource_size_t dcheck_res(struct list_head *elf_segments);
+void list_add_used_region(struct list_head *new, struct list_head *head);
+
+#endif /* KEXEC_ELF_H */
diff --git a/lib/kexec/kexec.c b/lib/kexec/kexec.c
new file mode 100644
index 0000000..f9e94b2
--- /dev/null
+++ b/lib/kexec/kexec.c
@@ -0,0 +1,251 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005  Eric Biederman (ebiederm@xmission.com)
+ *
+ * Modified (2007-05-15) by Francesco Chiechi to rudely handle mips platform
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <linux/types.h>
+#include <common.h>
+
+#include <asm/io.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/stat.h>
+#include <fcntl.h>
+
+#include "config.h"
+
+#include "kexec.h"
+#include "kexec-elf.h"
+#include <fs.h>
+
+void die(char *fmt, ...)
+{
+}
+
+static void print_segments(struct kexec_info *info)
+{
+	int i;
+
+	printf("nr_segments = %d\n", info->nr_segments);
+	for (i = 0; i < info->nr_segments; i++) {
+		printf("segment[%d].buf   = %p\n", i,
+			info->segment[i].buf);
+		printf("segment[%d].bufsz = %zx\n", i,
+			info->segment[i].bufsz);
+		printf("segment[%d].mem   = %p\n", i,
+			info->segment[i].mem);
+		printf("segment[%d].memsz = %zx\n", i,
+			info->segment[i].memsz);
+	}
+}
+
+int sort_segments(struct kexec_info *info)
+{
+	int i, j;
+	void *end;
+
+	/* Do a stupid insertion sort... */
+	for (i = 0; i < info->nr_segments; i++) {
+		int tidx;
+		struct kexec_segment temp;
+		tidx = i;
+		for (j = i +1; j < info->nr_segments; j++) {
+			if (info->segment[j].mem < info->segment[tidx].mem) {
+				tidx = j;
+			}
+		}
+		if (tidx != i) {
+			temp = info->segment[tidx];
+			info->segment[tidx] = info->segment[i];
+			info->segment[i] = temp;
+		}
+	}
+	/* Now see if any of the segments overlap */
+	end = 0;
+	for (i = 0; i < info->nr_segments; i++) {
+		if (end > info->segment[i].mem) {
+			printf("Overlapping memory segments at %p\n",
+				end);
+			return -1;
+		}
+		end = ((char *)info->segment[i].mem) + info->segment[i].memsz;
+	}
+	return 0;
+}
+
+void add_segment_phys_virt(struct kexec_info *info,
+	const void *buf, size_t bufsz,
+	unsigned long base, size_t memsz, int phys)
+{
+	size_t size;
+	int pagesize;
+
+	if (bufsz > memsz) {
+		bufsz = memsz;
+	}
+
+	/* Forget empty segments */
+	if (memsz == 0) {
+		return;
+	}
+
+	/* Round memsz up to a multiple of pagesize */
+	//pagesize = getpagesize();
+	pagesize = 4096;
+	memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1);
+
+	/* Verify base is pagesize aligned.
+	 * Finding a way to cope with this problem
+	 * is important but for now error so at least
+	 * we are not surprised by the code doing the wrong
+	 * thing.
+	 */
+	if (base & (pagesize -1)) {
+		die("Base address: %lx is not page aligned\n", base);
+	}
+
+	if (phys)
+		base = virt_to_phys((void *)base);
+
+	size = (info->nr_segments + 1) * sizeof(info->segment[0]);
+	info->segment = xrealloc(info->segment, size);
+	info->segment[info->nr_segments].buf   = buf;
+	info->segment[info->nr_segments].bufsz = bufsz;
+	info->segment[info->nr_segments].mem   = (void *)base;
+	info->segment[info->nr_segments].memsz = memsz;
+	info->nr_segments++;
+	if (info->nr_segments > KEXEC_MAX_SEGMENTS) {
+		printf("Warning: kernel segment limit reached. "
+			"This will likely fail\n");
+	}
+}
+
+/*
+ *	Load the new kernel
+ */
+static int my_load(char *kernel, unsigned long kexec_flags)
+{
+	char *kernel_buf;
+	off_t kernel_size;
+	int i = 0;
+	int result;
+	struct kexec_info info;
+	long native_arch;
+
+	memset(&info, 0, sizeof(info));
+	info.segment = NULL;
+	info.nr_segments = 0;
+	info.entry = NULL;
+	info.kexec_flags = kexec_flags;
+
+	result = 0;
+	/* slurp in the input kernel */
+	/* FIXME: add a decompresion routines insted of read_file() */
+	kernel_buf = read_file(kernel, &kernel_size);
+
+	for (i = 0; i < kexec_file_types; i++) {
+		if (kexec_file_type[i].probe(kernel_buf, kernel_size) >= 0)
+			break;
+	}
+
+	if (i == kexec_file_types) {
+		printf("Cannot determine the file type "
+				"of %s\n", kernel);
+		return -1;
+	}
+
+	/* Figure out our native architecture before load */
+#if 0
+	native_arch = physical_arch();
+	if (native_arch < 0) {
+		return -1;
+	}
+#endif
+	native_arch = 0;
+	info.kexec_flags |= native_arch;
+
+	result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
+	if (result < 0) {
+		switch (result) {
+		case EFAILED:
+		default:
+			printf("Cannot load %s\n", kernel);
+			break;
+		}
+		return result;
+	}
+
+	/* Verify all of the segments load to a valid location in memory */
+
+	/* Sort the segments and verify we don't have overlaps */
+	if (sort_segments(&info) < 0) {
+		return -1;
+	}
+
+	result = kexec_load(info.entry,
+		info.nr_segments, info.segment, info.kexec_flags);
+
+	if (result != 0) {
+		/* The load failed, print some debugging information */
+		printf("kexec_load failed: %s\n",
+			strerror(errno));
+		printf("entry       = %p flags = %lx\n",
+			info.entry, info.kexec_flags);
+		print_segments(&info);
+	}
+
+	return result;
+}
+
+#include <boot.h>
+#include <init.h>
+#include <binfmt.h>
+
+static int do_bootm_elf(struct image_data *data)
+{
+	my_load(data->os_file, 0);
+
+	/* unreachable(); */
+	return 0;
+}
+
+static struct image_handler elf_handler = {
+	.name = "ELF",
+	.bootm = do_bootm_elf,
+	.filetype = filetype_elf,
+};
+
+static struct binfmt_hook binfmt_elf_hook = {
+	.type = filetype_elf,
+	.exec = "bootm",
+};
+
+static int elf_register_image_handler(void)
+{
+	register_image_handler(&elf_handler);
+	binfmt_register(&binfmt_elf_hook);
+
+	return 0;
+}
+late_initcall(elf_register_image_handler);
diff --git a/lib/kexec/kexec.h b/lib/kexec/kexec.h
new file mode 100644
index 0000000..74e7ca9
--- /dev/null
+++ b/lib/kexec/kexec.h
@@ -0,0 +1,117 @@
+#ifndef KEXEC_H
+#define KEXEC_H
+
+#include <common.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <asm/byteorder.h>
+
+#include "kexec-elf.h"
+
+/*
+ * Document some of the reasons why crashdump may fail, so we can give
+ * better error messages
+ */
+#define EFAILED		-1	/* default error code */
+
+struct kexec_segment {
+	const void *buf;
+	size_t bufsz;
+	const void *mem;
+	size_t memsz;
+};
+
+struct kexec_info {
+	struct kexec_segment *segment;
+	int nr_segments;
+	void *entry;
+	struct mem_ehdr rhdr;
+	unsigned long kexec_flags;
+};
+
+struct arch_map_entry {
+	const char *machine;
+	unsigned long arch;
+};
+
+extern const struct arch_map_entry arches[];
+long physical_arch(void);
+
+int sort_segments(struct kexec_info *info);
+typedef int (probe_t)(const char *kernel_buf, off_t kernel_size);
+typedef int (load_t )(//int argc, char **argv,
+	const char *kernel_buf, off_t kernel_size,
+	struct kexec_info *info);
+struct kexec_file_type {
+	const char *name;
+	probe_t *probe;
+	load_t  *load;
+};
+
+extern struct kexec_file_type kexec_file_type[];
+extern int kexec_file_types;
+
+extern void die(char *fmt, ...);
+extern void add_segment(struct kexec_info *info,
+	const void *buf, size_t bufsz, unsigned long base, size_t memsz);
+extern void add_segment_phys_virt(struct kexec_info *info,
+	const void *buf, size_t bufsz, unsigned long base, size_t memsz,
+	int phys);
+extern unsigned long add_buffer(struct kexec_info *info,
+	const void *buf, unsigned long bufsz, unsigned long memsz,
+	unsigned long buf_align, unsigned long buf_min, unsigned long buf_max,
+	int buf_end);
+extern unsigned long add_buffer_phys_virt(struct kexec_info *info,
+	const void *buf, unsigned long bufsz, unsigned long memsz,
+	unsigned long buf_align, unsigned long buf_min, unsigned long buf_max,
+	int buf_end, int phys);
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+/* These values match the ELF architecture values.
+ * Unless there is a good reason that should continue to be the case.
+ */
+#define KEXEC_ARCH_DEFAULT ( 0 << 16)
+#define KEXEC_ARCH_386     ( 3 << 16)
+#define KEXEC_ARCH_X86_64  (62 << 16)
+#define KEXEC_ARCH_PPC     (20 << 16)
+#define KEXEC_ARCH_PPC64   (21 << 16)
+#define KEXEC_ARCH_IA_64   (50 << 16)
+#define KEXEC_ARCH_ARM     (40 << 16)
+#define KEXEC_ARCH_S390    (22 << 16)
+#define KEXEC_ARCH_SH      (42 << 16)
+#define KEXEC_ARCH_MIPS_LE (10 << 16)
+#define KEXEC_ARCH_MIPS    ( 8 << 16)
+#define KEXEC_ARCH_CRIS    (76 << 16)
+
+#define KEXEC_MAX_SEGMENTS 16
+
+#endif /* KEXEC_H */
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 3/5] MIPS: add ELF support
  2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
  2012-12-19 21:29 ` [RFC 1/5] resource: add create_resource() helper function Antony Pavlov
  2012-12-19 21:29 ` [RFC 2/5] bootm: add very basic ELF support (stolen from kexec) Antony Pavlov
@ 2012-12-19 21:29 ` Antony Pavlov
  2012-12-21  7:53   ` Sascha Hauer
  2012-12-19 21:29 ` [RFC 4/5] MIPS: qemu-malta: add board label Antony Pavlov
  2012-12-19 21:29 ` [RFC 5/5] MIPS: qemu-malta: add YAMON-style GT64120 memory map Antony Pavlov
  4 siblings, 1 reply; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/Kconfig               |    1 +
 arch/mips/include/asm/elf.h     |    8 ++-
 arch/mips/include/asm/io.h      |   18 ++++++
 arch/mips/lib/Makefile          |    3 +
 arch/mips/lib/kexec-elf-mips.c  |   83 +++++++++++++++++++++++++
 arch/mips/lib/kexec-mips.c      |  130 +++++++++++++++++++++++++++++++++++++++
 arch/mips/lib/kexec-mips.h      |   24 ++++++++
 arch/mips/lib/relocate_kernel.S |   77 +++++++++++++++++++++++
 8 files changed, 343 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/lib/kexec-elf-mips.c
 create mode 100644 arch/mips/lib/kexec-mips.c
 create mode 100644 arch/mips/lib/kexec-mips.h
 create mode 100644 arch/mips/lib/relocate_kernel.S

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 947edcf..3a7f775 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -6,6 +6,7 @@ config MIPS
 	select HAS_KALLSYMS
 	select HAVE_CONFIGURABLE_MEMORY_LAYOUT
 	select HAVE_CONFIGURABLE_TEXT_BASE
+	select HAS_KEXEC
 	default y
 
 config SYS_SUPPORTS_BIG_ENDIAN
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index b8b8219..bf974f5 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -17,12 +17,18 @@
 
 #ifndef ELF_ARCH
 
+/* Legal values for e_machine (architecture).  */
+
+#define EM_MIPS		 8		/* MIPS R3000 big-endian */
+#define EM_MIPS_RS4_BE	10		/* MIPS R4000 big-endian */
+
 #ifdef CONFIG_32BIT
 
 /*
  * This is used to ensure we don't load something for the wrong architecture.
  */
-#define elf_check_arch(hdr)						\
+#define elf_check_arch(x) ((x)->e_machine == EM_MIPS)
+
 /*
  * These are used to set parameters in the core dumps.
  */
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 4100e1e..f22ef6f 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -14,6 +14,24 @@
 #include <asm/types.h>
 #include <asm/byteorder.h>
 
+/*
+ *     virt_to_phys    -       map virtual addresses to physical
+ *     @address: address to remap
+ *
+ *     The returned physical address is the physical (CPU) mapping for
+ *     the memory address given. It is only valid to use this function on
+ *     addresses directly mapped or allocated via kmalloc.
+ *
+ *     This function does not give bus mappings for DMA transfers. In
+ *     almost all conceivable cases a device driver should not be using
+ *     this function
+ */
+static inline unsigned long virt_to_phys(volatile const void *address)
+{
+	//return (unsigned long)address - PAGE_OFFSET + PHYS_OFFSET;
+	return (unsigned long)address & 0x1fffffff;
+}
+
 /*****************************************************************************/
 /*
  * readX/writeX() are used to access memory mapped devices. On some
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index a31046b..fa16748 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -13,3 +13,6 @@ obj-$(CONFIG_CPU_MIPS64) += c-r4k.o
 
 obj-$(CONFIG_CMD_MIPS_CPUINFO) += cpuinfo.o
 obj-$(CONFIG_CMD_BOOTM)	+= bootm.o
+
+obj-$(CONFIG_KEXEC) += kexec-mips.o kexec-elf-mips.o
+obj-$(CONFIG_KEXEC) += relocate_kernel.o
diff --git a/arch/mips/lib/kexec-elf-mips.c b/arch/mips/lib/kexec-elf-mips.c
new file mode 100644
index 0000000..2065428
--- /dev/null
+++ b/arch/mips/lib/kexec-elf-mips.c
@@ -0,0 +1,83 @@
+/*
+ * kexec-elf-mips.c - kexec Elf loader for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-elf-ppc.c
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <fcntl.h>
+#include <elf.h>
+#include "../../../lib/kexec/kexec.h"
+#include "../../../lib/kexec/kexec-elf.h"
+#include "kexec-mips.h"
+
+int elf_mips_probe(const char *buf, off_t len)
+{
+	struct mem_ehdr ehdr;
+	int result;
+
+	result = build_elf_exec_info(buf, len, &ehdr, 0);
+	if (result < 0) {
+		goto out;
+	}
+
+	/* Verify the architecuture specific bits */
+	if (ehdr.e_machine != EM_MIPS) {
+		/* for a different architecture */
+		printf("Not for this architecture.\n");
+		result = -1;
+		goto out;
+	}
+	result = 0;
+
+ out:
+	free_elf_info(&ehdr);
+
+	return result;
+}
+
+int elf_mips_load(const char *buf, off_t len, struct kexec_info *info)
+{
+	struct mem_ehdr ehdr;
+	int result;
+	size_t i;
+
+	result = build_elf_exec_info(buf, len, &ehdr, 0);
+	if (result < 0) {
+		printf("ELF exec parse failed\n");
+		goto out;
+	}
+
+	/* Read in the PT_LOAD segments and remove CKSEG0 mask from address */
+	for (i = 0; i < ehdr.e_phnum; i++) {
+		struct mem_phdr *phdr;
+		phdr = &ehdr.e_phdr[i];
+		if (phdr->p_type == PT_LOAD) {
+			phdr->p_paddr = virt_to_phys(phdr->p_paddr);
+		}
+	}
+
+	/* Load the ELF data */
+	result = elf_exec_load(&ehdr, info);
+	if (result < 0) {
+		printf("ELF exec load failed\n");
+		goto out;
+	}
+
+	info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+out:
+	return result;
+}
diff --git a/arch/mips/lib/kexec-mips.c b/arch/mips/lib/kexec-mips.c
new file mode 100644
index 0000000..d0c98dc
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.c
@@ -0,0 +1,130 @@
+/*
+ * kexec-mips.c - kexec for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-mips.c
+ * Copyright (C) 2004, 2005 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <memory.h>
+#include "../../../lib/kexec/kexec.h"
+#include "kexec-mips.h"
+
+struct kexec_file_type kexec_file_type[] = {
+	{"elf-mips", elf_mips_probe, elf_mips_load },
+};
+int kexec_file_types = sizeof(kexec_file_type) / sizeof(kexec_file_type[0]);
+
+#ifdef __mips64
+struct arch_options_t arch_options = {
+	.core_header_type = CORE_TYPE_ELF64
+};
+#endif
+
+const struct arch_map_entry arches[] = {
+	/* For compatibility with older patches
+	 * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_MIPS here.
+	 */
+	{ "mips", KEXEC_ARCH_MIPS },
+	{ "mips64", KEXEC_ARCH_MIPS },
+	{ NULL, 0 },
+};
+
+/*
+ * add_segment() should convert base to a physical address on mips,
+ * while the default is just to work with base as is */
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+		 unsigned long base, size_t memsz)
+{
+	add_segment_phys_virt(info, buf, bufsz, virt_to_phys(base), memsz, 1);
+}
+
+typedef void (*noretfun_t)(long, long, long, long) __attribute__((noreturn));
+
+/* relocator parameters */
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_segments;
+extern unsigned long kexec_nr_segments;
+
+long kexec_load(void *entry, unsigned long nr_segments,
+		struct kexec_segment *segments, unsigned long flags)
+{
+	int i;
+	struct resource *elf;
+	resource_size_t start;
+	unsigned long reboot_code_buffer;
+	LIST_HEAD(elf_segments);
+
+	for (i = 0; i < nr_segments; i++) {
+		elf = create_resource("elf segment",
+			segments[i].mem,
+			segments[i].mem + segments[i].memsz - 1);
+
+		list_add_used_region(&elf->sibling, &elf_segments);
+	}
+
+	if (check_room_for_elf(&elf_segments)) {
+		printf("ELF can't be loaded!\n");
+		return 0;
+	}
+
+	/* FIXME: common code? */
+	start = dcheck_res(&elf_segments);
+
+	/* FIXME: we don't support cache just now su use KSEG1 */
+	start = CKSEG1ADDR(start);
+
+	/* relocate_new_kernel() copy by register (4 or 8 bytes)
+	   so start address must be aligned to 4/8 */
+	start = (start + 15) & 0xfffffff0;
+
+	for (i = 0; i < nr_segments; i++) {
+		segments[i].buf = (void *)(CKSEG1ADDR(segments[i].buf));
+		segments[i].mem = (void *)(CKSEG1ADDR(segments[i].mem));
+		memcpy(start, segments[i].buf, segments[i].bufsz);
+		request_sdram_region("kexec relocatable segments",
+			(unsigned long)start,
+			(unsigned long)segments[i].bufsz);
+
+		/* FIXME */
+		/* relocate_new_kernel() copy by register (4 or 8 bytes)
+		   so bufsz must be aligned to 4/8 */
+		segments[i].bufsz = (segments[i].bufsz + 15) & 0xfffffff0;
+		start = start + segments[i].bufsz;
+	}
+
+	start = (start + 15) & 0xfffffff0;
+
+	extern u32 relocate_new_kernel;
+	extern u32 relocate_new_kernel_size;
+	reboot_code_buffer = start;
+
+	memcpy(reboot_code_buffer, &relocate_new_kernel, relocate_new_kernel_size);
+	request_sdram_region("kexec relocator",
+		(unsigned long)reboot_code_buffer,
+		(unsigned long)relocate_new_kernel_size);
+
+	start = reboot_code_buffer + relocate_new_kernel_size;
+	start = (start + 15) & 0xfffffff0;
+
+	kexec_start_address = CKSEG1ADDR(entry);
+	kexec_segments = start;
+	kexec_nr_segments = nr_segments;
+
+	memcpy(start, segments, nr_segments * sizeof(*segments));
+	request_sdram_region("kexec control segments",
+		(unsigned long)start,
+		(unsigned long)nr_segments * sizeof(*segments));
+
+	return 0;
+}
diff --git a/arch/mips/lib/kexec-mips.h b/arch/mips/lib/kexec-mips.h
new file mode 100644
index 0000000..af1c529
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.h
@@ -0,0 +1,24 @@
+#ifndef KEXEC_MIPS_H
+#define KEXEC_MIPS_H
+
+#define MAX_MEMORY_RANGES  64
+
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+extern unsigned char setup_simple_start[];
+extern uint32_t setup_simple_size;
+
+extern struct {
+	uint32_t spr8;
+	uint32_t spr9;
+} setup_simple_regs;
+
+int elf_mips_probe(const char *buf, off_t len);
+int elf_mips_load(/*int argc, char **argv,*/ const char *buf, off_t len,
+	struct kexec_info *info);
+
+struct arch_options_t {
+	int      core_header_type;
+};
+
+#endif /* KEXEC_MIPS_H */
diff --git a/arch/mips/lib/relocate_kernel.S b/arch/mips/lib/relocate_kernel.S
new file mode 100644
index 0000000..25db034
--- /dev/null
+++ b/arch/mips/lib/relocate_kernel.S
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * based on relocate_kernel.S for kexec
+ * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
+ *
+ * This file is part of barebox.
+ * See file CREDITS for list of people who contributed to this project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+
+LEAF(relocate_new_kernel)
+	PTR_L		s0, kexec_segments
+	PTR_L		s1, kexec_nr_segments
+	PTR_L		s2, kexec_start_address
+
+process_segment:
+	PTR_L		s4, (s0) /* buf */
+	PTR_L		s5, SZREG (s0) /* bufsz */
+	PTR_L		s6, 2*SZREG (s0) /* mem */
+
+copy_segment:
+	/* copy segment word by word */
+	REG_L		s7, (s4)
+	REG_S		s7, (s6)
+	PTR_ADD		s4, s4, SZREG
+	PTR_ADD		s6, s6, SZREG
+	LONG_SUB	s5, s5, 1
+	bne		s5, zero, copy_segment
+
+	LONG_SUB	s1, s1, 1
+	beq		s1, zero, done
+
+	PTR_ADD		s0, s0, 4*SZREG
+
+	b		process_segment
+
+done:
+	/* jump to kexec_start_address */
+	j		s2
+	END(relocate_new_kernel)
+
+kexec_start_address:
+	EXPORT(kexec_start_address)
+	PTR		0x0
+	.size		kexec_start_address, PTRSIZE
+
+kexec_segments:
+	EXPORT(kexec_segments)
+	PTR		0x0
+	.size		kexec_segments, PTRSIZE
+
+kexec_nr_segments:
+	EXPORT(kexec_nr_segments)
+	PTR		0x0
+	.size		kexec_nr_segments, PTRSIZE
+
+relocate_new_kernel_end:
+
+relocate_new_kernel_size:
+	EXPORT(relocate_new_kernel_size)
+	PTR		relocate_new_kernel_end - relocate_new_kernel
+	.size		relocate_new_kernel_size, PTRSIZE
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 4/5] MIPS: qemu-malta: add board label
  2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
                   ` (2 preceding siblings ...)
  2012-12-19 21:29 ` [RFC 3/5] MIPS: add ELF support Antony Pavlov
@ 2012-12-19 21:29 ` Antony Pavlov
  2012-12-19 21:29 ` [RFC 5/5] MIPS: qemu-malta: add YAMON-style GT64120 memory map Antony Pavlov
  4 siblings, 0 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

Put special Board ID at the start of bootrom (as qemu does).

The linux kernel use this board ID to determine
the MIPS board family and version, so we need this
Board ID to run linux kernel successfully.

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 .../boards/qemu-malta/include/board/board_pbl_start.h    |   14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h b/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
index 5b06770..a6c55c7 100644
--- a/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
+++ b/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
@@ -23,6 +23,20 @@
 	.set	push
 	.set	noreorder
 
+	b       __start
+	 nop
+
+	/*
+	   MIPS_REVISION_REG located at 0x1fc00010
+	   see the MIPS_REVISION_CORID macro in linux kernel sources
+	   set up it to 0x420 (Malta Board with CoreLV) as qemu does
+	*/
+	.org    0x10
+	.word   0x00000420
+
+	.align 4
+__start:
+
 	mips_disable_interrupts
 
 	/* cpu specific setup ... */
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 5/5] MIPS: qemu-malta: add YAMON-style GT64120 memory map
  2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
                   ` (3 preceding siblings ...)
  2012-12-19 21:29 ` [RFC 4/5] MIPS: qemu-malta: add board label Antony Pavlov
@ 2012-12-19 21:29 ` Antony Pavlov
  4 siblings, 0 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-19 21:29 UTC (permalink / raw)
  To: barebox

YAMON-style GT64120 memory map make move UART to the new position.

We need YAMON-style GT64120 memory for running Linux kernel from
barebox.

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 .../qemu-malta/include/board/board_pbl_start.h     |   42 ++++++++++++++++++++
 arch/mips/mach-malta/include/mach/hardware.h       |    2 +-
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h b/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
index a6c55c7..d993c2f 100644
--- a/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
+++ b/arch/mips/boards/qemu-malta/include/board/board_pbl_start.h
@@ -19,6 +19,15 @@
 
 #include <asm/pbl_macros.h>
 
+#define GT_PCI0IOLD_OFS		0x048
+#define GT_PCI0IOHD_OFS		0x050
+#define GT_PCI0M0LD_OFS		0x058
+#define GT_PCI0M0HD_OFS		0x060
+#define GT_ISD_OFS		0x068
+
+#define GT_PCI0M1LD_OFS		0x080
+#define GT_PCI0M1HD_OFS		0x088
+
 	.macro	board_pbl_start
 	.set	push
 	.set	noreorder
@@ -39,6 +48,39 @@ __start:
 
 	mips_disable_interrupts
 
+	/*
+	 * Load BAR registers of GT64120 as done by YAMON
+	 *
+	 * based on write_bootloader() in qemu.git/hw/mips_malta.c
+	 * see GT64120 manual and qemu.git/hw/gt64xxx.c for details
+	 *
+	 * This is big-endian version of code!
+	 */
+
+	/* move GT64120 registers to 0x1be00000 */
+	li	t1, 0xb4000000
+	li	t0, 0xdf000000
+	sw	t0, GT_ISD_OFS(t1)
+
+	/* setup MEM-to-PCI0 mapping */
+	li	t1, 0xbbe00000
+
+	/* setup PCI0 io window to 0x18000000-0x181fffff */
+	li	t0, 0xc0000000
+	sw	t0, GT_PCI0IOLD_OFS(t1)
+	li	t0, 0x40000000
+	sw	t0, GT_PCI0IOHD_OFS(t1)
+
+	/* setup PCI0 mem windows */
+	li	t0, 0x80000000
+	sw	t0, GT_PCI0M0LD_OFS(t1)
+	li	t0, 0x3f000000
+	sw	t0, GT_PCI0M0HD_OFS(t1)
+	li	t0, 0xc1000000
+	sw	t0, GT_PCI0M1LD_OFS(t1)
+	li	t0, 0x5e000000
+	sw	t0, GT_PCI0M1HD_OFS(t1)
+
 	/* cpu specific setup ... */
 	/* ... absent */
 
diff --git a/arch/mips/mach-malta/include/mach/hardware.h b/arch/mips/mach-malta/include/mach/hardware.h
index 5e8e276..f6294b2 100644
--- a/arch/mips/mach-malta/include/mach/hardware.h
+++ b/arch/mips/mach-malta/include/mach/hardware.h
@@ -18,7 +18,7 @@
 #ifndef __INCLUDE_ARCH_HARDWARE_H__
 #define   __INCLUDE_ARCH_HARDWARE_H__
 
-#define DEBUG_LL_UART_ADDR	0xb00003f8
+#define DEBUG_LL_UART_ADDR	0xb80003f8
 #define DEBUG_LL_UART_SHIFT	0
 
 /*
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC 2/5] bootm: add very basic ELF support (stolen from kexec)
  2012-12-19 21:29 ` [RFC 2/5] bootm: add very basic ELF support (stolen from kexec) Antony Pavlov
@ 2012-12-21  7:49   ` Sascha Hauer
       [not found]     ` <CAA4bVAGk02KAHJH6mOfzoa0-5C=P7_7ADEMtg9jXPSk-Fr5UVw@mail.gmail.com>
  0 siblings, 1 reply; 10+ messages in thread
From: Sascha Hauer @ 2012-12-21  7:49 UTC (permalink / raw)
  To: Antony Pavlov; +Cc: barebox

On Thu, Dec 20, 2012 at 01:29:52AM +0400, Antony Pavlov wrote:
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
>  commands/Kconfig           |    7 +
>  common/Kconfig             |    3 +
>  common/filetype.c          |    5 +
>  include/filetype.h         |    1 +
>  lib/Makefile               |    1 +
>  lib/kexec/Makefile         |    1 +
>  lib/kexec/kexec-elf-exec.c |   85 ++++
>  lib/kexec/kexec-elf.c      |  927 ++++++++++++++++++++++++++++++++++++++++++++
>  lib/kexec/kexec-elf.h      |  110 ++++++
>  lib/kexec/kexec.c          |  251 ++++++++++++
>  lib/kexec/kexec.h          |  117 ++++++
>  11 files changed, 1508 insertions(+)
>  create mode 100644 lib/kexec/Makefile
>  create mode 100644 lib/kexec/kexec-elf-exec.c
>  create mode 100644 lib/kexec/kexec-elf.c
>  create mode 100644 lib/kexec/kexec-elf.h
>  create mode 100644 lib/kexec/kexec.c
>  create mode 100644 lib/kexec/kexec.h
> 
> diff --git a/commands/Kconfig b/commands/Kconfig
> index 75ebfb8..aef4ecb 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -403,6 +403,13 @@ config CMD_BOOTM_AIMAGE
>  	help
>  	  Support using Android Images.
>  
> +config KEXEC
> +	bool
> +	prompt "bootm ELF image support"
> +	depends on CMD_BOOTM && HAS_KEXEC
> +	help
> +	  Support using ELF Images.
> +
>  config CMD_UIMAGE
>  	select UIMAGE
>  	tristate
> diff --git a/common/Kconfig b/common/Kconfig
> index b60b78b..84cfc70 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -674,3 +674,6 @@ endmenu
>  
>  config HAS_DEBUG_LL
>  	bool
> +
> +config HAS_KEXEC
> +	bool
> diff --git a/common/filetype.c b/common/filetype.c
> index 748e364..1bb9cac 100644
> --- a/common/filetype.c
> +++ b/common/filetype.c
> @@ -23,6 +23,7 @@
>  #include <fs.h>
>  #include <malloc.h>
>  #include <errno.h>
> +#include <elf.h>
>  
>  struct filetype_str {
>  	const char *name;	/* human readable filetype */
> @@ -48,6 +49,7 @@ static const struct filetype_str filetype_str[] = {
>  	[filetype_bmp] = { "BMP image", "bmp" },
>  	[filetype_png] = { "PNG image", "png" },
>  	[filetype_ext] = { "ext filesystem", "ext" },
> +	[filetype_elf] = { "ELF", "elf" },
>  };
>  
>  const char *file_type_to_string(enum filetype f)
> @@ -169,6 +171,9 @@ enum filetype file_detect_type(void *_buf, size_t bufsize)
>  	if (buf16[512 + 28] == le16_to_cpu(0xef53))
>  		return filetype_ext;
>  
> +	if (strncmp(buf8, ELFMAG, 4) == 0)
> +		return filetype_elf;
> +

The filetypes are sorted by size in this function. I think this should
go some positions up.

> +/*
> + *	Load the new kernel
> + */
> +static int my_load(char *kernel, unsigned long kexec_flags)
> +{
> +	char *kernel_buf;
> +	off_t kernel_size;
> +	int i = 0;
> +	int result;
> +	struct kexec_info info;
> +	long native_arch;
> +
> +	memset(&info, 0, sizeof(info));
> +	info.segment = NULL;
> +	info.nr_segments = 0;
> +	info.entry = NULL;
> +	info.kexec_flags = kexec_flags;
> +
> +	result = 0;
> +	/* slurp in the input kernel */
> +	/* FIXME: add a decompresion routines insted of read_file() */
> +	kernel_buf = read_file(kernel, &kernel_size);
> +
> +	for (i = 0; i < kexec_file_types; i++) {
> +		if (kexec_file_type[i].probe(kernel_buf, kernel_size) >= 0)
> +			break;
> +	}
> +
> +	if (i == kexec_file_types) {
> +		printf("Cannot determine the file type "
> +				"of %s\n", kernel);
> +		return -1;
> +	}
> +
> +	/* Figure out our native architecture before load */
> +#if 0
> +	native_arch = physical_arch();
> +	if (native_arch < 0) {
> +		return -1;
> +	}
> +#endif
> +	native_arch = 0;
> +	info.kexec_flags |= native_arch;
> +
> +	result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
> +	if (result < 0) {
> +		switch (result) {
> +		case EFAILED:
> +		default:
> +			printf("Cannot load %s\n", kernel);
> +			break;
> +		}
> +		return result;
> +	}
> +
> +	/* Verify all of the segments load to a valid location in memory */
> +
> +	/* Sort the segments and verify we don't have overlaps */
> +	if (sort_segments(&info) < 0) {
> +		return -1;
> +	}
> +
> +	result = kexec_load(info.entry,
> +		info.nr_segments, info.segment, info.kexec_flags);
> +
> +	if (result != 0) {
> +		/* The load failed, print some debugging information */
> +		printf("kexec_load failed: %s\n",
> +			strerror(errno));
> +		printf("entry       = %p flags = %lx\n",
> +			info.entry, info.kexec_flags);
> +		print_segments(&info);
> +	}
> +
> +	return result;
> +}
> +
> +#include <boot.h>
> +#include <init.h>
> +#include <binfmt.h>
> +
> +static int do_bootm_elf(struct image_data *data)
> +{
> +	my_load(data->os_file, 0);
> +
> +	/* unreachable(); */

This is not unreachable. my_load can return errors. Unfortunately they
are all -1 (which would be nice to have fixed)

Sascha


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC 3/5] MIPS: add ELF support
  2012-12-19 21:29 ` [RFC 3/5] MIPS: add ELF support Antony Pavlov
@ 2012-12-21  7:53   ` Sascha Hauer
       [not found]     ` <CAA4bVAGki1VfbFXNRpMj+-pcM70W_yZvMCFhDGgbpKAXE6Taag@mail.gmail.com>
  0 siblings, 1 reply; 10+ messages in thread
From: Sascha Hauer @ 2012-12-21  7:53 UTC (permalink / raw)
  To: Antony Pavlov; +Cc: barebox

On Thu, Dec 20, 2012 at 01:29:53AM +0400, Antony Pavlov wrote:
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
>  arch/mips/Kconfig               |    1 +
>  arch/mips/include/asm/elf.h     |    8 ++-
>  arch/mips/include/asm/io.h      |   18 ++++++
>  arch/mips/lib/Makefile          |    3 +
>  arch/mips/lib/kexec-elf-mips.c  |   83 +++++++++++++++++++++++++
>  arch/mips/lib/kexec-mips.c      |  130 +++++++++++++++++++++++++++++++++++++++
>  arch/mips/lib/kexec-mips.h      |   24 ++++++++
>  arch/mips/lib/relocate_kernel.S |   77 +++++++++++++++++++++++
>  8 files changed, 343 insertions(+), 1 deletion(-)
>  create mode 100644 arch/mips/lib/kexec-elf-mips.c
>  create mode 100644 arch/mips/lib/kexec-mips.c
>  create mode 100644 arch/mips/lib/kexec-mips.h
>  create mode 100644 arch/mips/lib/relocate_kernel.S
> 
> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
> index 947edcf..3a7f775 100644
> --- a/arch/mips/Kconfig
> +++ b/arch/mips/Kconfig
> @@ -6,6 +6,7 @@ config MIPS
>  	select HAS_KALLSYMS
>  	select HAVE_CONFIGURABLE_MEMORY_LAYOUT
>  	select HAVE_CONFIGURABLE_TEXT_BASE
> +	select HAS_KEXEC
>  	default y
>  
>  config SYS_SUPPORTS_BIG_ENDIAN
> diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
> index b8b8219..bf974f5 100644
> --- a/arch/mips/include/asm/elf.h
> +++ b/arch/mips/include/asm/elf.h
> @@ -17,12 +17,18 @@
>  
>  #ifndef ELF_ARCH
>  
> +/* Legal values for e_machine (architecture).  */
> +
> +#define EM_MIPS		 8		/* MIPS R3000 big-endian */
> +#define EM_MIPS_RS4_BE	10		/* MIPS R4000 big-endian */
> +
>  #ifdef CONFIG_32BIT
>  
>  /*
>   * This is used to ensure we don't load something for the wrong architecture.
>   */
> -#define elf_check_arch(hdr)						\
> +#define elf_check_arch(x) ((x)->e_machine == EM_MIPS)
> +
>  /*
>   * These are used to set parameters in the core dumps.
>   */
> diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
> index 4100e1e..f22ef6f 100644
> --- a/arch/mips/include/asm/io.h
> +++ b/arch/mips/include/asm/io.h
> @@ -14,6 +14,24 @@
>  #include <asm/types.h>
>  #include <asm/byteorder.h>
>  
> +/*
> + *     virt_to_phys    -       map virtual addresses to physical
> + *     @address: address to remap
> + *
> + *     The returned physical address is the physical (CPU) mapping for
> + *     the memory address given. It is only valid to use this function on
> + *     addresses directly mapped or allocated via kmalloc.
> + *
> + *     This function does not give bus mappings for DMA transfers. In
> + *     almost all conceivable cases a device driver should not be using
> + *     this function
> + */
> +static inline unsigned long virt_to_phys(volatile const void *address)
> +{
> +	//return (unsigned long)address - PAGE_OFFSET + PHYS_OFFSET;
> +	return (unsigned long)address & 0x1fffffff;

Why this? I would assume that you have a 1:1 mapping on Mips?

> +
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <errno.h>
> +#include <asm/io.h>
> +#include <linux/types.h>
> +#include <fcntl.h>
> +#include <elf.h>
> +#include "../../../lib/kexec/kexec.h"
> +#include "../../../lib/kexec/kexec-elf.h"

So the they should be better in include/kexec/

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC 2/5] bootm: add very basic ELF support (stolen from kexec)
       [not found]     ` <CAA4bVAGk02KAHJH6mOfzoa0-5C=P7_7ADEMtg9jXPSk-Fr5UVw@mail.gmail.com>
@ 2012-12-21 10:59       ` Antony Pavlov
  0 siblings, 0 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-21 10:59 UTC (permalink / raw)
  To: barebox

On 21 December 2012 11:49, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> On Thu, Dec 20, 2012 at 01:29:52AM +0400, Antony Pavlov wrote:
>> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
>> ---
>>  commands/Kconfig           |    7 +
>>  common/Kconfig             |    3 +
>>  common/filetype.c          |    5 +
>>  include/filetype.h         |    1 +
>>  lib/Makefile               |    1 +
>>  lib/kexec/Makefile         |    1 +
>>  lib/kexec/kexec-elf-exec.c |   85 ++++
>>  lib/kexec/kexec-elf.c      |  927 ++++++++++++++++++++++++++++++++++++++++++++
>>  lib/kexec/kexec-elf.h      |  110 ++++++
>>  lib/kexec/kexec.c          |  251 ++++++++++++
>>  lib/kexec/kexec.h          |  117 ++++++
>>  11 files changed, 1508 insertions(+)
>>  create mode 100644 lib/kexec/Makefile
>>  create mode 100644 lib/kexec/kexec-elf-exec.c
>>  create mode 100644 lib/kexec/kexec-elf.c
>>  create mode 100644 lib/kexec/kexec-elf.h
>>  create mode 100644 lib/kexec/kexec.c
>>  create mode 100644 lib/kexec/kexec.h
>>
>> diff --git a/commands/Kconfig b/commands/Kconfig
>> index 75ebfb8..aef4ecb 100644
>> --- a/commands/Kconfig
>> +++ b/commands/Kconfig
>> @@ -403,6 +403,13 @@ config CMD_BOOTM_AIMAGE
>>       help
>>         Support using Android Images.
>>
>> +config KEXEC
>> +     bool
>> +     prompt "bootm ELF image support"
>> +     depends on CMD_BOOTM && HAS_KEXEC
>> +     help
>> +       Support using ELF Images.
>> +
>>  config CMD_UIMAGE
>>       select UIMAGE
>>       tristate
>> diff --git a/common/Kconfig b/common/Kconfig
>> index b60b78b..84cfc70 100644
>> --- a/common/Kconfig
>> +++ b/common/Kconfig
>> @@ -674,3 +674,6 @@ endmenu
>>
>>  config HAS_DEBUG_LL
>>       bool
>> +
>> +config HAS_KEXEC
>> +     bool
>> diff --git a/common/filetype.c b/common/filetype.c
>> index 748e364..1bb9cac 100644
>> --- a/common/filetype.c
>> +++ b/common/filetype.c
>> @@ -23,6 +23,7 @@
>>  #include <fs.h>
>>  #include <malloc.h>
>>  #include <errno.h>
>> +#include <elf.h>
>>
>>  struct filetype_str {
>>       const char *name;       /* human readable filetype */
>> @@ -48,6 +49,7 @@ static const struct filetype_str filetype_str[] = {
>>       [filetype_bmp] = { "BMP image", "bmp" },
>>       [filetype_png] = { "PNG image", "png" },
>>       [filetype_ext] = { "ext filesystem", "ext" },
>> +     [filetype_elf] = { "ELF", "elf" },
>>  };
>>
>>  const char *file_type_to_string(enum filetype f)
>> @@ -169,6 +171,9 @@ enum filetype file_detect_type(void *_buf, size_t bufsize)
>>       if (buf16[512 + 28] == le16_to_cpu(0xef53))
>>               return filetype_ext;
>>
>> +     if (strncmp(buf8, ELFMAG, 4) == 0)
>> +             return filetype_elf;
>> +
>
> The filetypes are sorted by size in this function. I think this should
> go some positions up.
>
>> +/*
>> + *   Load the new kernel
>> + */
>> +static int my_load(char *kernel, unsigned long kexec_flags)
>> +{
>> +     char *kernel_buf;
>> +     off_t kernel_size;
>> +     int i = 0;
>> +     int result;
>> +     struct kexec_info info;
>> +     long native_arch;
>> +
>> +     memset(&info, 0, sizeof(info));
>> +     info.segment = NULL;
>> +     info.nr_segments = 0;
>> +     info.entry = NULL;
>> +     info.kexec_flags = kexec_flags;
>> +
>> +     result = 0;
>> +     /* slurp in the input kernel */
>> +     /* FIXME: add a decompresion routines insted of read_file() */
>> +     kernel_buf = read_file(kernel, &kernel_size);
>> +
>> +     for (i = 0; i < kexec_file_types; i++) {
>> +             if (kexec_file_type[i].probe(kernel_buf, kernel_size) >= 0)
>> +                     break;
>> +     }
>> +
>> +     if (i == kexec_file_types) {
>> +             printf("Cannot determine the file type "
>> +                             "of %s\n", kernel);
>> +             return -1;
>> +     }
>> +
>> +     /* Figure out our native architecture before load */
>> +#if 0
>> +     native_arch = physical_arch();
>> +     if (native_arch < 0) {
>> +             return -1;
>> +     }
>> +#endif
>> +     native_arch = 0;
>> +     info.kexec_flags |= native_arch;
>> +
>> +     result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
>> +     if (result < 0) {
>> +             switch (result) {
>> +             case EFAILED:
>> +             default:
>> +                     printf("Cannot load %s\n", kernel);
>> +                     break;
>> +             }
>> +             return result;
>> +     }
>> +
>> +     /* Verify all of the segments load to a valid location in memory */
>> +
>> +     /* Sort the segments and verify we don't have overlaps */
>> +     if (sort_segments(&info) < 0) {
>> +             return -1;
>> +     }
>> +
>> +     result = kexec_load(info.entry,
>> +             info.nr_segments, info.segment, info.kexec_flags);
>> +
>> +     if (result != 0) {
>> +             /* The load failed, print some debugging information */
>> +             printf("kexec_load failed: %s\n",
>> +                     strerror(errno));
>> +             printf("entry       = %p flags = %lx\n",
>> +                     info.entry, info.kexec_flags);
>> +             print_segments(&info);
>> +     }
>> +
>> +     return result;
>> +}
>> +
>> +#include <boot.h>
>> +#include <init.h>
>> +#include <binfmt.h>
>> +
>> +static int do_bootm_elf(struct image_data *data)
>> +{
>> +     my_load(data->os_file, 0);
>> +
>> +     /* unreachable(); */
>
> This is not unreachable. my_load can return errors. Unfortunately they
> are all -1 (which would be nice to have fixed)

I agree.

Just now I use debug mode then bootm loads elf to unused memory
regions for relocation and returns control to shell, so one can check
memory map via 'iomem'. See this example from my [RFC 0/5]:

barebox:/ bootm env/vmlinux.qemu-malta-be
handler failed with No error

barebox:/ iomem
0x00000000 - 0xffffffff (size 0x00000000) iomem
  0x1e000000 - 0x1e3fffff (size 0x00400000) cfi_flash0
  0xa0000000 - 0xafffffff (size 0x10000000) ram0
    0xa17f9000 - 0xa17fffff (size 0x00007000) stack
    0xa1800000 - 0xa27fffff (size 0x01000000) malloc space
    0xa2800000 - 0xa2d4e464 (size 0x0054e465) barebox
    0xa2d50114 - 0xa2d56bb0 (size 0x00006a9d) bss
    0xa2d56bc0 - 0xa316f29b (size 0x004186dc) kexec relocatable segments
    0xa316f2a0 - 0xa316f307 (size 0x00000068) kexec relocator
    0xa316f310 - 0xa316f31f (size 0x00000010) kexec control segments
  0xb80003f8 - 0xb80003ff (size 0x00000008) ns16550_serial0

barebox:/ go 0xa316f2a0

In this sample I use 'go' to jump to the start of the kexec relocator.

Of cause this behavour will be changed in the future.

>
> --
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |


-- 
Best regards,
  Antony Pavlov

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Fwd: [RFC 3/5] MIPS: add ELF support
       [not found]     ` <CAA4bVAGki1VfbFXNRpMj+-pcM70W_yZvMCFhDGgbpKAXE6Taag@mail.gmail.com>
@ 2012-12-21 11:00       ` Antony Pavlov
  0 siblings, 0 replies; 10+ messages in thread
From: Antony Pavlov @ 2012-12-21 11:00 UTC (permalink / raw)
  To: barebox

On 21 December 2012 11:53, Sascha Hauer <s.hauer@pengutronix.de> wrote:
> On Thu, Dec 20, 2012 at 01:29:53AM +0400, Antony Pavlov wrote:
>> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
>> ---
>>  arch/mips/Kconfig               |    1 +
>>  arch/mips/include/asm/elf.h     |    8 ++-
>>  arch/mips/include/asm/io.h      |   18 ++++++
>>  arch/mips/lib/Makefile          |    3 +
>>  arch/mips/lib/kexec-elf-mips.c  |   83 +++++++++++++++++++++++++
>>  arch/mips/lib/kexec-mips.c      |  130 +++++++++++++++++++++++++++++++++++++++
>>  arch/mips/lib/kexec-mips.h      |   24 ++++++++
>>  arch/mips/lib/relocate_kernel.S |   77 +++++++++++++++++++++++
>>  8 files changed, 343 insertions(+), 1 deletion(-)
>>  create mode 100644 arch/mips/lib/kexec-elf-mips.c
>>  create mode 100644 arch/mips/lib/kexec-mips.c
>>  create mode 100644 arch/mips/lib/kexec-mips.h
>>  create mode 100644 arch/mips/lib/relocate_kernel.S
>>
>> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
>> index 947edcf..3a7f775 100644
>> --- a/arch/mips/Kconfig
>> +++ b/arch/mips/Kconfig
>> @@ -6,6 +6,7 @@ config MIPS
>>       select HAS_KALLSYMS
>>       select HAVE_CONFIGURABLE_MEMORY_LAYOUT
>>       select HAVE_CONFIGURABLE_TEXT_BASE
>> +     select HAS_KEXEC
>>       default y
>>
>>  config SYS_SUPPORTS_BIG_ENDIAN
>> diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
>> index b8b8219..bf974f5 100644
>> --- a/arch/mips/include/asm/elf.h
>> +++ b/arch/mips/include/asm/elf.h
>> @@ -17,12 +17,18 @@
>>
>>  #ifndef ELF_ARCH
>>
>> +/* Legal values for e_machine (architecture).  */
>> +
>> +#define EM_MIPS               8              /* MIPS R3000 big-endian */
>> +#define EM_MIPS_RS4_BE       10              /* MIPS R4000 big-endian */
>> +
>>  #ifdef CONFIG_32BIT
>>
>>  /*
>>   * This is used to ensure we don't load something for the wrong architecture.
>>   */
>> -#define elf_check_arch(hdr)                                          \
>> +#define elf_check_arch(x) ((x)->e_machine == EM_MIPS)
>> +
>>  /*
>>   * These are used to set parameters in the core dumps.
>>   */
>> diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
>> index 4100e1e..f22ef6f 100644
>> --- a/arch/mips/include/asm/io.h
>> +++ b/arch/mips/include/asm/io.h
>> @@ -14,6 +14,24 @@
>>  #include <asm/types.h>
>>  #include <asm/byteorder.h>
>>
>> +/*
>> + *     virt_to_phys    -       map virtual addresses to physical
>> + *     @address: address to remap
>> + *
>> + *     The returned physical address is the physical (CPU) mapping for
>> + *     the memory address given. It is only valid to use this function on
>> + *     addresses directly mapped or allocated via kmalloc.
>> + *
>> + *     This function does not give bus mappings for DMA transfers. In
>> + *     almost all conceivable cases a device driver should not be using
>> + *     this function
>> + */
>> +static inline unsigned long virt_to_phys(volatile const void *address)
>> +{
>> +     //return (unsigned long)address - PAGE_OFFSET + PHYS_OFFSET;
>> +     return (unsigned long)address & 0x1fffffff;
>
> Why this? I would assume that you have a 1:1 mapping on Mips?

No.
On MIPS virtual address, used by programmer just always differ from
physical address.
Just now mips barebox work in KSEG1 (uncached) memory region
(0xa0000000 --- 0xc0000000), after cache support adding mips barebox
will work in KSEG0 (cached) memory region (0x80000000 --- 0xa0000000).
Both memory regions (KSEG1 and KSEG0) map to the same physical
addresses (0x00000000 --- 0x20000000).


>> +
>> +#define _GNU_SOURCE
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <stdlib.h>
>> +#include <errno.h>
>> +#include <asm/io.h>
>> +#include <linux/types.h>
>> +#include <fcntl.h>
>> +#include <elf.h>
>> +#include "../../../lib/kexec/kexec.h"
>> +#include "../../../lib/kexec/kexec-elf.h"
>
> So the they should be better in include/kexec/

ok

> Sascha
>
> --
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

--
Best regards,
  Antony Pavlov

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-12-21 11:00 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-19 21:29 [RFC 0/5] add ELF support Antony Pavlov
2012-12-19 21:29 ` [RFC 1/5] resource: add create_resource() helper function Antony Pavlov
2012-12-19 21:29 ` [RFC 2/5] bootm: add very basic ELF support (stolen from kexec) Antony Pavlov
2012-12-21  7:49   ` Sascha Hauer
     [not found]     ` <CAA4bVAGk02KAHJH6mOfzoa0-5C=P7_7ADEMtg9jXPSk-Fr5UVw@mail.gmail.com>
2012-12-21 10:59       ` Antony Pavlov
2012-12-19 21:29 ` [RFC 3/5] MIPS: add ELF support Antony Pavlov
2012-12-21  7:53   ` Sascha Hauer
     [not found]     ` <CAA4bVAGki1VfbFXNRpMj+-pcM70W_yZvMCFhDGgbpKAXE6Taag@mail.gmail.com>
2012-12-21 11:00       ` Fwd: " Antony Pavlov
2012-12-19 21:29 ` [RFC 4/5] MIPS: qemu-malta: add board label Antony Pavlov
2012-12-19 21:29 ` [RFC 5/5] MIPS: qemu-malta: add YAMON-style GT64120 memory map Antony Pavlov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox