* [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 = ¤t_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 = ¤t_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 = ¤t_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
* 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
* [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
* 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
* [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
* 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
* [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