mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [RFC 00/10] MIPS: use kexec to load ELF linux images
@ 2014-04-15  7:38 Antony Pavlov
  2014-04-15  7:38 ` [RFC 01/10] MIPS: add initial cache support for R4000-class CPUs Antony Pavlov
                   ` (9 more replies)
  0 siblings, 10 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

This patchseries introduces a suitable way for loading
ELF linux kernel images on MIPS. You can load normal vmlinux
images or compressed self-extractable vmlinuz images.

The patchseries and additional patch with pre-compiled demo
vmlinuz image for MIPS Malta can be obtained here:
  https://github.com/frantony/barebox/tree/next.mips-malta-elf-linux.20140415

Linux kernel ELF images use KSEG1 ("cached") addresses, so
this series adds elementary code for MIPS cache support.

The code for actual ELF loading and relocation is imported
from
  kexec-tools (git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools)
and
  linux kernel (http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git).

There is no common standard on passing cmdline to the linux kernel
on MIPS machines. We have to realize it's own cmdline passing
routine for every MIPS machine supported by barebox.
The 'MIPS: mach-malta: add kexec-capable reboot()' patch
demonstrates how to do it for MIPS Malta.

To separate common ELF handling routines and machine-specific
cmdline handling the 'reboot()' call is introduced.
The common code checks and loads ELF-file, the MIPS-specific
code arrange ELF segments in the appropriate memory places
and tunes relocator; next reboot() is called and machine-specific
code works on cmdline passing.

Antony Pavlov (10):
  MIPS: add initial cache support for R4000-class CPUs
  MIPS: introduce arch_shutdown()
  MIPS: use arch_shutdown() for flushing caches
  MIPS: add virt_to_phys() and phys_to_virt()
  resource: add create_resource() helper function
  import initial kexec stuff
  filetype: add ELF type
  bootm: add kexec ELF support
  MIPS: add ELF support
  MIPS: mach-malta: add kexec-capable reboot()

 arch/mips/Kconfig                |   1 +
 arch/mips/include/asm/cache.h    |   8 +
 arch/mips/include/asm/cacheops.h |  27 ++
 arch/mips/include/asm/common.h   |   2 +-
 arch/mips/include/asm/elf.h      |   8 +-
 arch/mips/include/asm/io.h       |  42 +++
 arch/mips/lib/Makefile           |   5 +
 arch/mips/lib/c-r4k.c            |  89 ++++++
 arch/mips/lib/dma.c              |  27 ++
 arch/mips/lib/kexec-mips.c       | 170 +++++++++++
 arch/mips/lib/relocate_kernel.S  |  80 +++++
 arch/mips/lib/shutdown.c         |  12 +
 arch/mips/mach-malta/Makefile    |   1 +
 arch/mips/mach-malta/reboot.c    | 104 +++++++
 commands/Kconfig                 |   7 +
 common/Kconfig                   |   3 +
 common/filetype.c                |   5 +
 common/resource.c                |  15 +
 include/filetype.h               |   1 +
 include/linux/ioport.h           |   2 +
 include/linux/reboot.h           |  14 +
 lib/Makefile                     |   1 +
 lib/kexec/Makefile               |   4 +
 lib/kexec/kexec-bootm-elf.c      |  36 +++
 lib/kexec/kexec-elf-exec.c       |  82 +++++
 lib/kexec/kexec-elf.c            | 639 +++++++++++++++++++++++++++++++++++++++
 lib/kexec/kexec-elf.h            |  86 ++++++
 lib/kexec/kexec.c                | 149 +++++++++
 lib/kexec/kexec.h                |  89 ++++++
 29 files changed, 1707 insertions(+), 2 deletions(-)
 create mode 100644 arch/mips/include/asm/cache.h
 create mode 100644 arch/mips/include/asm/cacheops.h
 create mode 100644 arch/mips/lib/dma.c
 create mode 100644 arch/mips/lib/kexec-mips.c
 create mode 100644 arch/mips/lib/relocate_kernel.S
 create mode 100644 arch/mips/lib/shutdown.c
 create mode 100644 arch/mips/mach-malta/reboot.c
 create mode 100644 include/linux/reboot.h
 create mode 100644 lib/kexec/Makefile
 create mode 100644 lib/kexec/kexec-bootm-elf.c
 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

-- 
1.9.0


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

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

* [RFC 01/10] MIPS: add initial cache support for R4000-class CPUs
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 02/10] MIPS: introduce arch_shutdown() Antony Pavlov
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

This commit adds very elementary U-Boot style flush/clean
cache generic routines for R4000-class CPUs.

This patch lacks of initial cache initialization code
as it's may be CPU-dependent.

Here is Linux-Barebox cache routines correspondance:

  Linux                         Barebox
    _dma_cache_wback_inv    =>    dma_flush_range
    _dma_cache_wback        =>    dma_clean_range
    _dma_cache_inv          =>    dma_inv_range

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/include/asm/cache.h    |  8 ++++
 arch/mips/include/asm/cacheops.h | 27 ++++++++++++
 arch/mips/include/asm/io.h       |  7 ++++
 arch/mips/lib/Makefile           |  1 +
 arch/mips/lib/c-r4k.c            | 89 ++++++++++++++++++++++++++++++++++++++++
 arch/mips/lib/dma.c              | 27 ++++++++++++
 6 files changed, 159 insertions(+)
 create mode 100644 arch/mips/include/asm/cache.h
 create mode 100644 arch/mips/include/asm/cacheops.h
 create mode 100644 arch/mips/lib/dma.c

diff --git a/arch/mips/include/asm/cache.h b/arch/mips/include/asm/cache.h
new file mode 100644
index 0000000..2f807d5
--- /dev/null
+++ b/arch/mips/include/asm/cache.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_MIPS_CACHE_H
+#define _ASM_MIPS_CACHE_H
+
+void flush_cache_all(void);
+void flush_dcache_all(void);
+void flush_icache_all(void);
+
+#endif /* _ASM_MIPS_CACHE_H */
diff --git a/arch/mips/include/asm/cacheops.h b/arch/mips/include/asm/cacheops.h
new file mode 100644
index 0000000..4844a67
--- /dev/null
+++ b/arch/mips/include/asm/cacheops.h
@@ -0,0 +1,27 @@
+/*
+ * Cache operations for the cache instruction.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * (C) Copyright 1996, 97, 99, 2002, 03 Ralf Baechle
+ * (C) Copyright 1999 Silicon Graphics, Inc.
+ */
+#ifndef	__ASM_CACHEOPS_H
+#define	__ASM_CACHEOPS_H
+
+/*
+ * Cache Operations available on all MIPS processors with R4000-style caches
+ */
+#define Index_Invalidate_I      0x00
+#define Index_Writeback_Inv_D   0x01
+#define Index_Load_Tag_I	0x04
+#define Index_Load_Tag_D	0x05
+#define Index_Store_Tag_I	0x08
+#define Index_Store_Tag_D	0x09
+#define Hit_Invalidate_I	0x10
+#define Hit_Invalidate_D	0x11
+#define Hit_Writeback_Inv_D	0x15
+
+#endif	/* __ASM_CACHEOPS_H */
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 4832be6..ff66ea5 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -14,6 +14,13 @@
 #include <asm/types.h>
 #include <asm/byteorder.h>
 
+void *dma_alloc_coherent(size_t size);
+void dma_free_coherent(void *mem, size_t size);
+
+void dma_clean_range(unsigned long, unsigned long);
+void dma_flush_range(unsigned long, unsigned long);
+void dma_inv_range(unsigned long, unsigned long);
+
 #define	IO_SPACE_LIMIT	0
 
 /*****************************************************************************/
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 71c4f6b..f4aee2e 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -6,6 +6,7 @@ obj-y += ashrdi3.o
 obj-y += cpu-probe.o
 obj-y += traps.o
 obj-y += genex.o
+obj-y += dma.o
 
 obj-$(CONFIG_CPU_MIPS32) += c-r4k.o
 obj-$(CONFIG_CPU_MIPS64) += c-r4k.o
diff --git a/arch/mips/lib/c-r4k.c b/arch/mips/lib/c-r4k.c
index 01b8665..ff81141 100644
--- a/arch/mips/lib/c-r4k.c
+++ b/arch/mips/lib/c-r4k.c
@@ -10,10 +10,99 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/mipsregs.h>
+#include <asm/cache.h>
+#include <asm/cacheops.h>
 #include <asm/cpu.h>
 #include <asm/cpu-info.h>
 #include <asm/bitops.h>
 
+#define cache_op(op,addr)						\
+	__asm__ __volatile__(						\
+	"	.set	push					\n"	\
+	"	.set	noreorder				\n"	\
+	"	.set	mips3\n\t				\n"	\
+	"	cache	%0, %1					\n"	\
+	"	.set	pop					\n"	\
+	:								\
+	: "i" (op), "R" (*(unsigned char *)(addr)))
+
+void flush_cache_all(void)
+{
+	struct cpuinfo_mips *c = &current_cpu_data;
+	unsigned long lsize;
+	unsigned long addr;
+	unsigned long aend;
+	unsigned int icache_size, dcache_size;
+
+	dcache_size = c->dcache.waysize * c->dcache.ways;
+	lsize = c->dcache.linesz;
+	aend = (KSEG0 + dcache_size - 1) & ~(lsize - 1);
+	for (addr = KSEG0; addr <= aend; addr += lsize) {
+		cache_op(Index_Writeback_Inv_D, addr);
+	}
+
+	icache_size = c->icache.waysize * c->icache.ways;
+	lsize = c->icache.linesz;
+	aend = (KSEG0 + icache_size - 1) & ~(lsize - 1);
+	for (addr = KSEG0; addr <= aend; addr += lsize) {
+		cache_op(Index_Invalidate_I, addr);
+	}
+
+	/* secondatory cache skipped */
+}
+
+void flush_dcache_all(void)
+{
+	flush_cache_all();
+}
+
+void flush_icache_all(void)
+{
+	flush_cache_all();
+}
+
+void dma_clean_range(unsigned long start, unsigned long end)
+{
+	struct cpuinfo_mips *c = &current_cpu_data;
+	unsigned long lsize = c->dcache.linesz;
+	unsigned long addr = start & ~(lsize - 1);
+	unsigned long aend = (end - 1) & ~(lsize - 1);
+
+	for (; addr <= aend; addr += lsize)
+		cache_op(Hit_Invalidate_D, addr);
+
+	/* secondatory cache skipped */
+}
+
+void dma_flush_range(unsigned long start, unsigned long end)
+{
+	struct cpuinfo_mips *c = &current_cpu_data;
+	unsigned long lsize = c->dcache.linesz;
+	unsigned long addr = start & ~(lsize - 1);
+	unsigned long aend = (end - 1) & ~(lsize - 1);
+
+	for (; addr <= aend; addr += lsize) {
+		cache_op(Hit_Writeback_Inv_D, addr);
+	}
+
+	/* secondatory cache skipped */
+}
+
+/*
+ *	r4k_dma_inv_range(start,end)
+ *
+ *	Invalidate the data cache within the specified region; we will
+ *	be performing a DMA operation in this region and we want to
+ *	purge old data in the cache.
+ *
+ *	- start   - virtual start address of region
+ *	- end     - virtual end address of region
+ */
+void dma_inv_range(unsigned long start, unsigned long end)
+{
+	dma_clean_range(start, end);
+}
+
 void r4k_cache_init(void);
 
 static void probe_pcache(void)
diff --git a/arch/mips/lib/dma.c b/arch/mips/lib/dma.c
new file mode 100644
index 0000000..845ffe8
--- /dev/null
+++ b/arch/mips/lib/dma.c
@@ -0,0 +1,27 @@
+#include <common.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <malloc.h>
+
+static inline void __iomem *ioremap_nocache(phys_t offset, unsigned long size)
+{
+	return (void __iomem *) (unsigned long)CKSEG1ADDR(offset);
+}
+
+void *dma_alloc_coherent(size_t size)
+{
+	void *ret;
+
+	ret = xmemalign(PAGE_SIZE, size);
+
+	dma_inv_range((unsigned long)ret, (unsigned long)ret + size);
+
+	ret = ioremap_nocache((phys_t)ret, size);
+
+	return ret;
+}
+
+void dma_free_coherent(void *mem, size_t size)
+{
+	free(mem);
+}
-- 
1.9.0


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

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

* [RFC 02/10] MIPS: introduce arch_shutdown()
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
  2014-04-15  7:38 ` [RFC 01/10] MIPS: add initial cache support for R4000-class CPUs Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 03/10] MIPS: use arch_shutdown() for flushing caches Antony Pavlov
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/include/asm/common.h |  2 +-
 arch/mips/lib/Makefile         |  1 +
 arch/mips/lib/shutdown.c       | 10 ++++++++++
 3 files changed, 12 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/lib/shutdown.c

diff --git a/arch/mips/include/asm/common.h b/arch/mips/include/asm/common.h
index 2f5419f..ae0d805 100644
--- a/arch/mips/include/asm/common.h
+++ b/arch/mips/include/asm/common.h
@@ -20,6 +20,6 @@
 #ifndef _ASM_MIPS_COMMON_H_
 #define _ASM_MIPS_COMMON_H_
 
-/* nothing special yet */
+#define ARCH_SHUTDOWN
 
 #endif /* _ASM_MIPS_COMMON_H_ */
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index f4aee2e..c4e2214 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -7,6 +7,7 @@ obj-y += cpu-probe.o
 obj-y += traps.o
 obj-y += genex.o
 obj-y += dma.o
+obj-y += shutdown.o
 
 obj-$(CONFIG_CPU_MIPS32) += c-r4k.o
 obj-$(CONFIG_CPU_MIPS64) += c-r4k.o
diff --git a/arch/mips/lib/shutdown.c b/arch/mips/lib/shutdown.c
new file mode 100644
index 0000000..6feec9b
--- /dev/null
+++ b/arch/mips/lib/shutdown.c
@@ -0,0 +1,10 @@
+/**
+ * This function is called by shutdown_barebox to get a clean
+ * memory/cache state.
+ */
+#include <common.h>
+
+void arch_shutdown(void)
+{
+}
+EXPORT_SYMBOL(arch_shutdown);
-- 
1.9.0


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

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

* [RFC 03/10] MIPS: use arch_shutdown() for flushing caches
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
  2014-04-15  7:38 ` [RFC 01/10] MIPS: add initial cache support for R4000-class CPUs Antony Pavlov
  2014-04-15  7:38 ` [RFC 02/10] MIPS: introduce arch_shutdown() Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-23  8:43   ` Sascha Hauer
  2014-04-15  7:38 ` [RFC 04/10] MIPS: add virt_to_phys() and phys_to_virt() Antony Pavlov
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/lib/shutdown.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/mips/lib/shutdown.c b/arch/mips/lib/shutdown.c
index 6feec9b..09651a7 100644
--- a/arch/mips/lib/shutdown.c
+++ b/arch/mips/lib/shutdown.c
@@ -3,8 +3,10 @@
  * memory/cache state.
  */
 #include <common.h>
+#include <asm/cache.h>
 
 void arch_shutdown(void)
 {
+	flush_cache_all();
 }
 EXPORT_SYMBOL(arch_shutdown);
-- 
1.9.0


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

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

* [RFC 04/10] MIPS: add virt_to_phys() and phys_to_virt()
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (2 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 03/10] MIPS: use arch_shutdown() for flushing caches Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 05/10] resource: add create_resource() helper function Antony Pavlov
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/include/asm/io.h | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index ff66ea5..dcda857 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -12,6 +12,7 @@
 
 #include <linux/compiler.h>
 #include <asm/types.h>
+#include <asm/addrspace.h>
 #include <asm/byteorder.h>
 
 void *dma_alloc_coherent(size_t size);
@@ -21,6 +22,40 @@ void dma_clean_range(unsigned long, unsigned long);
 void dma_flush_range(unsigned long, unsigned long);
 void dma_inv_range(unsigned long, unsigned long);
 
+/*
+ *     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(const void *address)
+{
+	return (unsigned long)address & 0x3fffffff;
+}
+
+/*
+ *     phys_to_virt    -       map physical address to virtual
+ *     @address: address to remap
+ *
+ *     The returned virtual address is a current CPU mapping for
+ *     the memory address given. It is only valid to use this function on
+ *     addresses that have a kernel mapping
+ *
+ *     This function does not handle bus mappings for DMA transfers. In
+ *     almost all conceivable cases a device driver should not be using
+ *     this function
+ */
+static inline void *phys_to_virt(unsigned long address)
+{
+	return (void *)(KSEG0 | (address & 0x3fffffff));
+}
+
 #define	IO_SPACE_LIMIT	0
 
 /*****************************************************************************/
-- 
1.9.0


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

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

* [RFC 05/10] resource: add create_resource() helper function
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (3 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 04/10] MIPS: add virt_to_phys() and phys_to_virt() Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-23  8:46   ` Sascha Hauer
  2014-04-15  7:38 ` [RFC 06/10] import initial kexec stuff Antony Pavlov
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 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 fe4680e..b6c7c87 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -143,3 +143,18 @@ struct resource *request_ioport_region(const char *name,
 {
 	return __request_region(&ioport_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 d1b2f55..bce9b62 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -145,6 +145,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;
 extern struct resource ioport_resource;
-- 
1.9.0


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

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

* [RFC 06/10] import initial kexec stuff
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (4 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 05/10] resource: add create_resource() helper function Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 07/10] filetype: add ELF type Antony Pavlov
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 commands/Kconfig           |   7 +
 common/Kconfig             |   3 +
 lib/Makefile               |   1 +
 lib/kexec/Makefile         |   3 +
 lib/kexec/kexec-elf-exec.c |  82 ++++++
 lib/kexec/kexec-elf.c      | 639 +++++++++++++++++++++++++++++++++++++++++++++
 lib/kexec/kexec-elf.h      |  86 ++++++
 lib/kexec/kexec.c          | 149 +++++++++++
 lib/kexec/kexec.h          |  89 +++++++
 9 files changed, 1059 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 3e384f1..80e6263 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -484,6 +484,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 0031cc8..8a028b1 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -847,3 +847,6 @@ endmenu
 
 config HAS_DEBUG_LL
 	bool
+
+config HAS_KEXEC
+	bool
diff --git a/lib/Makefile b/lib/Makefile
index e8769a9..15b9182 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -44,3 +44,4 @@ obj-y			+= gui/
 obj-$(CONFIG_XYMODEM)	+= xymodem.o
 obj-y			+= unlink-recursive.o
 obj-$(CONFIG_STMP_DEVICE) += stmp-device.o
+obj-$(CONFIG_KEXEC)	+= kexec/
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
new file mode 100644
index 0000000..8febef1
--- /dev/null
+++ b/lib/kexec/Makefile
@@ -0,0 +1,3 @@
+obj-y	+= kexec.o
+obj-y	+= kexec-elf.o
+obj-y	+= kexec-elf-exec.o
diff --git a/lib/kexec/kexec-elf-exec.c b/lib/kexec/kexec-elf-exec.c
new file mode 100644
index 0000000..46c157c
--- /dev/null
+++ b/lib/kexec/kexec-elf-exec.c
@@ -0,0 +1,82 @@
+#include <common.h>
+#include <string.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..4e1528f
--- /dev/null
+++ b/lib/kexec/kexec-elf.c
@@ -0,0 +1,639 @@
+#include <common.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <asm/io.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;
+}
+
+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_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);
+	}
+
+	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_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;
+
+	/* 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);
+
+		}
+
+		if (result < 0) {
+			return result;
+		}
+
+		/* Check the program headers to be certain
+		 * they are safe to use.
+		 */
+		phdr = &ehdr->e_phdr[i];
+		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_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;
+
+	/* 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);
+		}
+
+		if (result < 0) {
+			return result;
+		}
+
+		/* Check the section headers to be certain
+		 * they are safe to use.
+		 */
+		shdr = &ehdr->e_shdr[i];
+		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;
+
+			start = virt_to_phys((void *)res->start);
+			end = virt_to_phys((void *)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) {
+			t = create_resource("tmp",
+				virt_to_phys((void *)r->start),
+				virt_to_phys((void *)r->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((void *)res->start),
+				virt_to_phys((void *)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..cf4be78
--- /dev/null
+++ b/lib/kexec/kexec-elf.h
@@ -0,0 +1,86 @@
+#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_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;
+
+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);
+
+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..0bb8b39
--- /dev/null
+++ b/lib/kexec/kexec.c
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <common.h>
+#include <fs.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <asm/io.h>
+
+#include "kexec.h"
+#include "kexec-elf.h"
+
+static 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 = 4096;
+	memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1);
+
+	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
+ */
+int kexec_load_file(char *kernel, unsigned long kexec_flags)
+{
+	char *kernel_buf;
+	off_t kernel_size;
+	int i = 0;
+	int result;
+	struct kexec_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.segment = NULL;
+	info.nr_segments = 0;
+	info.entry = NULL;
+	info.kexec_flags = kexec_flags;
+
+	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;
+	}
+
+	result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
+	if (result < 0) {
+		printf("Cannot load %s\n", kernel);
+		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);
+
+	return result;
+}
diff --git a/lib/kexec/kexec.h b/lib/kexec/kexec.h
new file mode 100644
index 0000000..381c1e4
--- /dev/null
+++ b/lib/kexec/kexec.h
@@ -0,0 +1,89 @@
+#ifndef KEXEC_H
+#define KEXEC_H
+
+#include "kexec-elf.h"
+
+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;
+	unsigned long kexec_flags;
+};
+
+typedef int (probe_t)(const char *kernel_buf, off_t kernel_size);
+typedef int (load_t)(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 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 long kexec_load(void *entry, unsigned long nr_segments,
+                        struct kexec_segment *segments, unsigned long flags);
+extern int kexec_load_file(char *kernel, unsigned long kexec_flags);
+
+/* 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.9.0


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

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

* [RFC 07/10] filetype: add ELF type
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (5 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 06/10] import initial kexec stuff Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 08/10] bootm: add kexec ELF support Antony Pavlov
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 common/filetype.c  | 5 +++++
 include/filetype.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/common/filetype.c b/common/filetype.c
index 0b5da30..dacfa38 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -24,6 +24,7 @@
 #include <malloc.h>
 #include <errno.h>
 #include <envfs.h>
+#include <elf.h>
 
 struct filetype_str {
 	const char *name;	/* human readable filetype */
@@ -53,6 +54,7 @@ static const struct filetype_str filetype_str[] = {
 	[filetype_gpt] = { "GUID Partition Table", "gpt" },
 	[filetype_bpk] = { "Binary PacKage", "bpk" },
 	[filetype_barebox_env] = { "barebox environment file", "bbenv" },
+	[filetype_elf] = { "ELF", "elf" },
 };
 
 const char *file_type_to_string(enum filetype f)
@@ -246,6 +248,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
 	if (bufsize >= 1536 && 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 c20a4f9..c4f776f 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -30,6 +30,7 @@ enum filetype {
 	filetype_ubifs,
 	filetype_bpk,
 	filetype_barebox_env,
+	filetype_elf,
 	filetype_max,
 };
 
-- 
1.9.0


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

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

* [RFC 08/10] bootm: add kexec ELF support
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (6 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 07/10] filetype: add ELF type Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-23  9:15   ` Sascha Hauer
  2014-04-15  7:38 ` [RFC 09/10] MIPS: add " Antony Pavlov
  2014-04-15  7:38 ` [RFC 10/10] MIPS: mach-malta: add kexec-capable reboot() Antony Pavlov
  9 siblings, 1 reply; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Also introduce reboot() for starting already loaded
via kexec ELF segments.

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 include/linux/reboot.h      | 14 ++++++++++++++
 lib/kexec/Makefile          |  1 +
 lib/kexec/kexec-bootm-elf.c | 36 ++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+)
 create mode 100644 include/linux/reboot.h
 create mode 100644 lib/kexec/kexec-bootm-elf.c

diff --git a/include/linux/reboot.h b/include/linux/reboot.h
new file mode 100644
index 0000000..454ed33
--- /dev/null
+++ b/include/linux/reboot.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * KEXEC       Restart system using a previously loaded Linux kernel
+ */
+
+#define	LINUX_REBOOT_CMD_KEXEC		0x45584543
+
+extern int reboot(int cmd);
+
+#endif /* _LINUX_REBOOT_H */
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
index 8febef1..2f3dc1d 100644
--- a/lib/kexec/Makefile
+++ b/lib/kexec/Makefile
@@ -1,3 +1,4 @@
 obj-y	+= kexec.o
 obj-y	+= kexec-elf.o
 obj-y	+= kexec-elf-exec.o
+obj-y	+= kexec-bootm-elf.o
diff --git a/lib/kexec/kexec-bootm-elf.c b/lib/kexec/kexec-bootm-elf.c
new file mode 100644
index 0000000..6c8071a
--- /dev/null
+++ b/lib/kexec/kexec-bootm-elf.c
@@ -0,0 +1,36 @@
+#include <boot.h>
+#include <init.h>
+#include <binfmt.h>
+#include <errno.h>
+#include <linux/reboot.h>
+
+#include "kexec.h"
+
+static int do_bootm_elf(struct image_data *data)
+{
+	kexec_load_file(data->os_file, 0);
+	setenv("global.bootm.image", data->os_file);
+	reboot(LINUX_REBOOT_CMD_KEXEC);
+
+	return -ERESTARTSYS;
+}
+
+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);
-- 
1.9.0


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

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

* [RFC 09/10] MIPS: add ELF support
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (7 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 08/10] bootm: add kexec ELF support Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  2014-04-15  7:38 ` [RFC 10/10] MIPS: mach-malta: add kexec-capable reboot() Antony Pavlov
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/include/asm/elf.h     |   8 +-
 arch/mips/lib/Makefile          |   3 +
 arch/mips/lib/kexec-mips.c      | 170 ++++++++++++++++++++++++++++++++++++++++
 arch/mips/lib/relocate_kernel.S |  80 +++++++++++++++++++
 4 files changed, 260 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/lib/kexec-mips.c
 create mode 100644 arch/mips/lib/relocate_kernel.S

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/lib/Makefile b/arch/mips/lib/Makefile
index c4e2214..ba43fa2 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -14,3 +14,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
+obj-$(CONFIG_KEXEC) += relocate_kernel.o
diff --git a/arch/mips/lib/kexec-mips.c b/arch/mips/lib/kexec-mips.c
new file mode 100644
index 0000000..61b1020
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.c
@@ -0,0 +1,170 @@
+/*
+ * 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 <elf.h>
+#include "../../../lib/kexec/kexec.h"
+
+static 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;
+}
+
+static 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((void *)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((void *)ehdr.e_entry);
+
+out:
+	return result;
+}
+
+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]);
+
+/*
+ * 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((void *)base), memsz, 1);
+}
+
+/* relocator parameters */
+extern unsigned long relocate_new_kernel;
+extern unsigned long relocate_new_kernel_size;
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_segments;
+extern unsigned long kexec_nr_segments;
+
+unsigned long reboot_code_buffer;
+
+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;
+	LIST_HEAD(elf_segments);
+
+	for (i = 0; i < nr_segments; i++) {
+		resource_size_t mem = (resource_size_t)segments[i].mem;
+
+		elf = create_resource("elf segment",
+			mem, 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;
+	}
+
+	start = dcheck_res(&elf_segments);
+
+	/* 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].mem = (void *)(phys_to_virt((unsigned long)segments[i].mem));
+		memcpy(phys_to_virt(start), segments[i].buf, segments[i].bufsz);
+		request_sdram_region("kexec relocatable segment",
+			(unsigned long)phys_to_virt(start),
+			(unsigned long)segments[i].bufsz);
+
+		/* 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;
+
+	reboot_code_buffer = start;
+
+	memcpy(phys_to_virt(start), &relocate_new_kernel,
+		relocate_new_kernel_size);
+	request_sdram_region("kexec relocator",
+		(unsigned long)phys_to_virt(start),
+		(unsigned long)relocate_new_kernel_size);
+
+	start = start + relocate_new_kernel_size;
+	start = (start + 15) & 0xfffffff0;
+
+	kexec_start_address = (unsigned long)phys_to_virt((unsigned long)entry);
+	kexec_segments = (unsigned long)phys_to_virt((unsigned long)start);
+	kexec_nr_segments = nr_segments;
+
+	memcpy(phys_to_virt(start), segments, nr_segments * sizeof(*segments));
+	request_sdram_region("kexec control segments",
+		(unsigned long)phys_to_virt(start),
+		(unsigned long)nr_segments * sizeof(*segments));
+
+	return 1;
+}
diff --git a/arch/mips/lib/relocate_kernel.S b/arch/mips/lib/relocate_kernel.S
new file mode 100644
index 0000000..5b38113
--- /dev/null
+++ b/arch/mips/lib/relocate_kernel.S
@@ -0,0 +1,80 @@
+/*
+ * 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)
+	.set	push
+	.set	reorder
+	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
+	.set	pop
-- 
1.9.0


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

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

* [RFC 10/10] MIPS: mach-malta: add kexec-capable reboot()
  2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
                   ` (8 preceding siblings ...)
  2014-04-15  7:38 ` [RFC 09/10] MIPS: add " Antony Pavlov
@ 2014-04-15  7:38 ` Antony Pavlov
  9 siblings, 0 replies; 14+ messages in thread
From: Antony Pavlov @ 2014-04-15  7:38 UTC (permalink / raw)
  To: barebox

This patch is based on qemu.git/hw/mips/mips_malta.c code.

Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
 arch/mips/Kconfig             |   1 +
 arch/mips/mach-malta/Makefile |   1 +
 arch/mips/mach-malta/reboot.c | 104 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 106 insertions(+)
 create mode 100644 arch/mips/mach-malta/reboot.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 9a240b7..759dcc8 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -44,6 +44,7 @@ config MACH_MIPS_MALTA
 	select SYS_SUPPORTS_32BIT_KERNEL
 	select SYS_SUPPORTS_BIG_ENDIAN
 	select HAS_DEBUG_LL
+	select HAS_KEXEC
 
 config MACH_MIPS_AR231X
 	bool "Atheros ar231x-based boards"
diff --git a/arch/mips/mach-malta/Makefile b/arch/mips/mach-malta/Makefile
index f3cc668..ed740ef 100644
--- a/arch/mips/mach-malta/Makefile
+++ b/arch/mips/mach-malta/Makefile
@@ -1 +1,2 @@
 obj-y += reset.o
+obj-y += reboot.o
diff --git a/arch/mips/mach-malta/reboot.c b/arch/mips/mach-malta/reboot.c
new file mode 100644
index 0000000..17c7ff8
--- /dev/null
+++ b/arch/mips/mach-malta/reboot.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * 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 <common.h>
+#include <init.h>
+#include <memory.h>
+#include <asm/io.h>
+#include <linux/reboot.h>
+#include "../../../lib/kexec/kexec.h"
+#include <boot.h>
+
+#define ENVP_ADDR	0x80002000l
+#define ENVP_NB_ENTRIES	16
+#define ENVP_ENTRY_SIZE	256
+
+static int reserve_yamon_prom_env(void)
+{
+	request_sdram_region("yamon env",
+		(unsigned long)ENVP_ADDR,
+		ENVP_NB_ENTRIES * ENVP_ENTRY_SIZE);
+
+	return 0;
+}
+late_initcall(reserve_yamon_prom_env);
+
+static void prom_set(uint32_t *prom_buf, int index,
+			const char *string, ...)
+{
+	va_list ap;
+	int32_t table_addr;
+
+	if (index >= ENVP_NB_ENTRIES)
+		return;
+
+	if (string == NULL) {
+		prom_buf[index] = 0;
+		return;
+	}
+
+	table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+	prom_buf[index] = (ENVP_ADDR + table_addr);
+
+	va_start(ap, string);
+	vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+	va_end(ap);
+}
+
+static inline void yamon_jump(void *entry)
+{
+	int argnum;
+	void (*kernel)(int a0, int a1, int a2, int a3);
+	void *prom_buf;
+	long prom_size;
+	int prom_index = 0;
+
+	kernel = entry;
+	argnum = 2;
+
+	/* Setup prom parameters. */
+	prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+	prom_buf = (void *)ENVP_ADDR;
+
+	prom_set(prom_buf, prom_index++, "%s", getenv("global.bootm.image"));
+	prom_set(prom_buf, prom_index++, "%s", linux_bootargs_get());
+
+	prom_set(prom_buf, prom_index++, "memsize");
+	prom_set(prom_buf, prom_index++, "%i", 256 << 20);
+	prom_set(prom_buf, prom_index++, "modetty0");
+	prom_set(prom_buf, prom_index++, "38400n8r");
+	prom_set(prom_buf, prom_index++, NULL);
+
+	kernel(argnum,              /* number of arguments? */
+		ENVP_ADDR,
+		ENVP_ADDR + 8,
+		0x10000000      /* no matter */
+	);
+}
+
+int reboot(int cmd)
+{
+	if (cmd == LINUX_REBOOT_CMD_KEXEC) {
+		extern unsigned long reboot_code_buffer;
+
+		shutdown_barebox();
+		yamon_jump(phys_to_virt(reboot_code_buffer));
+	}
+
+	return -1;
+}
+EXPORT_SYMBOL(reboot);
-- 
1.9.0


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

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

* Re: [RFC 03/10] MIPS: use arch_shutdown() for flushing caches
  2014-04-15  7:38 ` [RFC 03/10] MIPS: use arch_shutdown() for flushing caches Antony Pavlov
@ 2014-04-23  8:43   ` Sascha Hauer
  0 siblings, 0 replies; 14+ messages in thread
From: Sascha Hauer @ 2014-04-23  8:43 UTC (permalink / raw)
  To: Antony Pavlov; +Cc: barebox

On Tue, Apr 15, 2014 at 11:38:27AM +0400, Antony Pavlov wrote:
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
>  arch/mips/lib/shutdown.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/mips/lib/shutdown.c b/arch/mips/lib/shutdown.c
> index 6feec9b..09651a7 100644
> --- a/arch/mips/lib/shutdown.c
> +++ b/arch/mips/lib/shutdown.c
> @@ -3,8 +3,10 @@
>   * memory/cache state.
>   */
>  #include <common.h>
> +#include <asm/cache.h>
>  
>  void arch_shutdown(void)
>  {
> +	flush_cache_all();
>  }
>  EXPORT_SYMBOL(arch_shutdown);

You could combine this with 2/10. Otherwise 2/10 leaves the question:
why is this needed?

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] 14+ messages in thread

* Re: [RFC 05/10] resource: add create_resource() helper function
  2014-04-15  7:38 ` [RFC 05/10] resource: add create_resource() helper function Antony Pavlov
@ 2014-04-23  8:46   ` Sascha Hauer
  0 siblings, 0 replies; 14+ messages in thread
From: Sascha Hauer @ 2014-04-23  8:46 UTC (permalink / raw)
  To: Antony Pavlov; +Cc: barebox

On Tue, Apr 15, 2014 at 11:38:29AM +0400, Antony Pavlov wrote:
> 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 fe4680e..b6c7c87 100644
> --- a/common/resource.c
> +++ b/common/resource.c
> @@ -143,3 +143,18 @@ struct resource *request_ioport_region(const char *name,
>  {
>  	return __request_region(&ioport_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;

How about calling init_resource() and skip the fields already initialized
there from this function? This makes sure new fields in struct resource
can be initialized in a single place.

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] 14+ messages in thread

* Re: [RFC 08/10] bootm: add kexec ELF support
  2014-04-15  7:38 ` [RFC 08/10] bootm: add kexec ELF support Antony Pavlov
@ 2014-04-23  9:15   ` Sascha Hauer
  0 siblings, 0 replies; 14+ messages in thread
From: Sascha Hauer @ 2014-04-23  9:15 UTC (permalink / raw)
  To: Antony Pavlov; +Cc: barebox

On Tue, Apr 15, 2014 at 11:38:32AM +0400, Antony Pavlov wrote:
> Also introduce reboot() for starting already loaded
> via kexec ELF segments.
> 
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
>  include/linux/reboot.h      | 14 ++++++++++++++
>  lib/kexec/Makefile          |  1 +
>  lib/kexec/kexec-bootm-elf.c | 36 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 51 insertions(+)
>  create mode 100644 include/linux/reboot.h
>  create mode 100644 lib/kexec/kexec-bootm-elf.c
> 
> diff --git a/include/linux/reboot.h b/include/linux/reboot.h
> new file mode 100644
> index 0000000..454ed33
> --- /dev/null
> +++ b/include/linux/reboot.h
> @@ -0,0 +1,14 @@
> +#ifndef _LINUX_REBOOT_H
> +#define _LINUX_REBOOT_H
> +
> +/*
> + * Commands accepted by the _reboot() system call.
> + *
> + * KEXEC       Restart system using a previously loaded Linux kernel
> + */
> +
> +#define	LINUX_REBOOT_CMD_KEXEC		0x45584543
> +
> +extern int reboot(int cmd);
> +
> +#endif /* _LINUX_REBOOT_H */
> diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
> index 8febef1..2f3dc1d 100644
> --- a/lib/kexec/Makefile
> +++ b/lib/kexec/Makefile
> @@ -1,3 +1,4 @@
>  obj-y	+= kexec.o
>  obj-y	+= kexec-elf.o
>  obj-y	+= kexec-elf-exec.o
> +obj-y	+= kexec-bootm-elf.o
> diff --git a/lib/kexec/kexec-bootm-elf.c b/lib/kexec/kexec-bootm-elf.c
> new file mode 100644
> index 0000000..6c8071a
> --- /dev/null
> +++ b/lib/kexec/kexec-bootm-elf.c
> @@ -0,0 +1,36 @@
> +#include <boot.h>
> +#include <init.h>
> +#include <binfmt.h>
> +#include <errno.h>
> +#include <linux/reboot.h>
> +
> +#include "kexec.h"
> +
> +static int do_bootm_elf(struct image_data *data)
> +{
> +	kexec_load_file(data->os_file, 0);
> +	setenv("global.bootm.image", data->os_file);
> +	reboot(LINUX_REBOOT_CMD_KEXEC);
> +
> +	return -ERESTARTSYS;
> +}
> +
> +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);

Instead of making this an initcall I suggest that you call this function
from Malta specific code along with a pointer to a kexec_execute_file
function. This makes sure the generic code does not have to call into
global architecture specific functions. Also the dataflow can be done
clearer. kexec_load_file() currently loads the image *somewhere* and
reboot() is expected to know where it is loaded. I imagine kexec_load_file
should return information about the loaded image which is then passed to
reboot().

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] 14+ messages in thread

end of thread, other threads:[~2014-04-23  9:16 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-15  7:38 [RFC 00/10] MIPS: use kexec to load ELF linux images Antony Pavlov
2014-04-15  7:38 ` [RFC 01/10] MIPS: add initial cache support for R4000-class CPUs Antony Pavlov
2014-04-15  7:38 ` [RFC 02/10] MIPS: introduce arch_shutdown() Antony Pavlov
2014-04-15  7:38 ` [RFC 03/10] MIPS: use arch_shutdown() for flushing caches Antony Pavlov
2014-04-23  8:43   ` Sascha Hauer
2014-04-15  7:38 ` [RFC 04/10] MIPS: add virt_to_phys() and phys_to_virt() Antony Pavlov
2014-04-15  7:38 ` [RFC 05/10] resource: add create_resource() helper function Antony Pavlov
2014-04-23  8:46   ` Sascha Hauer
2014-04-15  7:38 ` [RFC 06/10] import initial kexec stuff Antony Pavlov
2014-04-15  7:38 ` [RFC 07/10] filetype: add ELF type Antony Pavlov
2014-04-15  7:38 ` [RFC 08/10] bootm: add kexec ELF support Antony Pavlov
2014-04-23  9:15   ` Sascha Hauer
2014-04-15  7:38 ` [RFC 09/10] MIPS: add " Antony Pavlov
2014-04-15  7:38 ` [RFC 10/10] MIPS: mach-malta: add kexec-capable reboot() Antony Pavlov

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