* [WIP] add very basic ELF support on MIPS
@ 2012-07-20 8:47 Antony Pavlov
2012-07-20 8:47 ` [RFC 1/7] MIPS: move start.S code to .text_entry section Antony Pavlov
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
This patch series introduces the basic ELF files support
for bootm command on MIPS based on kexec.
The goal of this work is ELF linux kernel loading for MIPS.
The patches 1-3 used to introduce per-arch low-level init for MIPS.
The patch 4 adds the nessesary low-level initialisation for
qemu-malta suitable for Linux kernel.
Just now barebox for MIPS32 has no support for cache, so
this patch series tries to overcome this disadvantage in
patch 5.
The patch 6 introduce memory region for loading ELFs on qemu-malta.
At last the patch 7 introduces kexec infrastructure for ELF loading.
Current problems:
* there is no Linux kernel cmdline support;
* the code is not clean (e.g. there are some '#if 0' and
some unused declarations);
* barebox already has ELF support for modules, but
this patch series just has no integration with it.
This patch series and one additional huge patch with sample
Linux ELF image for malta can be found at
https://github.com/frantony/barebox/tree/next.mips-malta-elf-linux.20120720
[RFC 1/7] MIPS: move start.S code to .text_entry section
[RFC 2/7] MIPS: add the initial support for lowlevel init
[RFC 3/7] MIPS: malta: add bogus lowlevel init
[RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map
[RFC 5/7] MIPS: malta: make KSEG0 uncached
[RFC 6/7] MIPS: qemu-malta: add KSEG0 memory region
[RFC 7/7] bootm: add very basic ELF support on MIPS (stealed from
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 1/7] MIPS: move start.S code to .text_entry section
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 2/7] MIPS: add the initial support for lowlevel init Antony Pavlov
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
This commit make possible lowlevel initialisation code
in the text_bare_init section.
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/boot/start.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/mips/boot/start.S b/arch/mips/boot/start.S
index dd302fc..10f7691 100644
--- a/arch/mips/boot/start.S
+++ b/arch/mips/boot/start.S
@@ -50,7 +50,7 @@ _pc: addiu \rd, ra, \label - _pc # label is assumed to be
.set noreorder
.text
- .section ".text_bare_init"
+ .section ".text_entry"
.align 4
EXPORT(_start)
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 2/7] MIPS: add the initial support for lowlevel init
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
2012-07-20 8:47 ` [RFC 1/7] MIPS: move start.S code to .text_entry section Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 3/7] MIPS: malta: add bogus " Antony Pavlov
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
This commit introduce the initial support for
machine specific lowlevel initialization
(e.g. RAM controller or cache memory).
To return from mach_init_lowlevel() it uses
explicit mach_init_lowlevel_return label.
This saves ra register for using inside mach_init_lowlevel()
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/boot/start.S | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/mips/boot/start.S b/arch/mips/boot/start.S
index 10f7691..238e2c4 100644
--- a/arch/mips/boot/start.S
+++ b/arch/mips/boot/start.S
@@ -74,6 +74,14 @@ __start:
and k0, k1
mtc0 k0, CP0_STATUS
+#ifdef CONFIG_MACH_DO_LOWLEVEL_INIT
+ b mach_init_lowlevel
+ nop
+
+mach_init_lowlevel_return:
+EXPORT(mach_init_lowlevel_return)
+#endif
+
/* copy barebox to link location */
ADR a0, _start, t1 /* a0 <- pc-relative position of _start */
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 3/7] MIPS: malta: add bogus lowlevel init
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
2012-07-20 8:47 ` [RFC 1/7] MIPS: move start.S code to .text_entry section Antony Pavlov
2012-07-20 8:47 ` [RFC 2/7] MIPS: add the initial support for lowlevel init Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map Antony Pavlov
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/Kconfig | 1 +
arch/mips/mach-malta/Makefile | 1 +
arch/mips/mach-malta/lowlevel_init.S | 37 ++++++++++++++++++++++++++++++++++
3 files changed, 39 insertions(+)
create mode 100644 arch/mips/mach-malta/lowlevel_init.S
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 7a1eeac..92dad9f 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -36,6 +36,7 @@ config MACH_MIPS_MALTA
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select HAS_DEBUG_LL
+ select MACH_HAS_LOWLEVEL_INIT
config MACH_MIPS_BCM47XX
bool "Broadcom BCM47xx-based boards"
diff --git a/arch/mips/mach-malta/Makefile b/arch/mips/mach-malta/Makefile
index f3cc668..f4cb8f2 100644
--- a/arch/mips/mach-malta/Makefile
+++ b/arch/mips/mach-malta/Makefile
@@ -1 +1,2 @@
obj-y += reset.o
+obj-$(CONFIG_MACH_DO_LOWLEVEL_INIT) += lowlevel_init.o
diff --git a/arch/mips/mach-malta/lowlevel_init.S b/arch/mips/mach-malta/lowlevel_init.S
new file mode 100644
index 0000000..a34de74
--- /dev/null
+++ b/arch/mips/mach-malta/lowlevel_init.S
@@ -0,0 +1,37 @@
+/*
+ * Lowlevel init code for MIPS malta
+ *
+ * Copyright (C) 2012 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+
+ .section ".text_bare_init"
+
+LEAF(mach_init_lowlevel)
+
+ /* nothing special yet */
+
+ /* return */
+ b mach_init_lowlevel_return
+ nop
+END(mach_init_lowlevel)
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
` (2 preceding siblings ...)
2012-07-20 8:47 ` [RFC 3/7] MIPS: malta: add bogus " Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 10:01 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 5/7] MIPS: malta: make KSEG0 uncached Antony Pavlov
` (2 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
YAMON-style GT64120 memory map make move UART to the new position.
We need YAMON-style GT64120 memory for running Linux kernel from
barebox.
WARNING! WIP! We need add some more effor for compartability
with qemu embedded YAMON.
E.g. we need put special Board ID at the start of bootrom:
--- a/arch/mips/boot/start.S
+++ b/arch/mips/boot/start.S
@@ -59,6 +59,10 @@ EXPORT(_start)
nop
.org 0x10
+#if 1
+ /* Board ID = 0x420 (Malta Board with CoreLV) */
+ .word 0x00000420
+#endif
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/mach-malta/include/mach/hardware.h | 2 +-
arch/mips/mach-malta/lowlevel_init.S | 33 +++++++++++++++++++++++++-
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/arch/mips/mach-malta/include/mach/hardware.h b/arch/mips/mach-malta/include/mach/hardware.h
index ddeb1b7..fde8f22 100644
--- a/arch/mips/mach-malta/include/mach/hardware.h
+++ b/arch/mips/mach-malta/include/mach/hardware.h
@@ -21,7 +21,7 @@
#ifndef __INCLUDE_ARCH_HARDWARE_H__
#define __INCLUDE_ARCH_HARDWARE_H__
-#define DEBUG_LL_UART_ADDR 0xb00003f8
+#define DEBUG_LL_UART_ADDR 0xb80003f8
#define DEBUG_LL_UART_SHIFT 0
/*
diff --git a/arch/mips/mach-malta/lowlevel_init.S b/arch/mips/mach-malta/lowlevel_init.S
index a34de74..a0134f8 100644
--- a/arch/mips/mach-malta/lowlevel_init.S
+++ b/arch/mips/mach-malta/lowlevel_init.S
@@ -29,7 +29,38 @@
LEAF(mach_init_lowlevel)
- /* nothing special yet */
+ /*
+ * Load BAR registers of GT64120 as done by YAMON
+ *
+ * based on write_bootloader() in qemu.git/hw/mips_malta.c
+ * see GT64120 manual and qemu.git/hw/gt64xxx.c for details
+ *
+ * This is big-endian version of code!
+ */
+
+ /* move GT64120 registers to 0x1be00000 */
+ lui t1, 0xb400
+ lui t0, 0xdf00
+ sw t0, 104(t1)
+
+ /* setup MEM-to-PCI0 mapping */
+ lui t1, 0xbbe0
+
+ /* setup PCI0 io window to 18000000-181fffff */
+ lui t0, 0xc000
+ sw t0, 72(t1) /* GT_PCI0IOLD */
+ lui t0, 0x4000
+ sw t0, 80(t1) /* GT_PCI0IOHD */
+
+ /* setup PCI0 mem windows */
+ lui t0, 0x8000
+ sw t0, 88(t1) /* GT_PCI0M0LD */
+ lui t0, 0x3f00
+ sw t0, 96(t1) /* GT_PCI0M0HD */
+ lui t0, 0xc100
+ sw t0, 128(t1) /* GT_PCI0M1LD */
+ lui t0, 0x5e00
+ sw t0, 136(t1) /* GT_PCI0M1HD */
/* return */
b mach_init_lowlevel_return
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 5/7] MIPS: malta: make KSEG0 uncached
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
` (3 preceding siblings ...)
2012-07-20 8:47 ` [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 6/7] MIPS: qemu-malta: add KSEG0 memory region Antony Pavlov
2012-07-20 8:47 ` [RFC 7/7] bootm: add very basic ELF support on MIPS (stealed from kexec) Antony Pavlov
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
On MIPS as usual KSEG0 segment used for cached memory access,
and Linux kernel linked to KSEG0.
As just now we don't have cache memory support on MIPS,
we can change the KSEG0 mode (make it uncached) and
use it without any danger.
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/mach-malta/lowlevel_init.S | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/mips/mach-malta/lowlevel_init.S b/arch/mips/mach-malta/lowlevel_init.S
index a0134f8..368c2fe 100644
--- a/arch/mips/mach-malta/lowlevel_init.S
+++ b/arch/mips/mach-malta/lowlevel_init.S
@@ -62,6 +62,10 @@ LEAF(mach_init_lowlevel)
lui t0, 0x5e00
sw t0, 136(t1) /* GT_PCI0M1HD */
+ /* make KSEG0 uncached */
+ li t0, CONF_CM_UNCACHED
+ mtc0 t0, CP0_CONFIG
+
/* return */
b mach_init_lowlevel_return
nop
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 6/7] MIPS: qemu-malta: add KSEG0 memory region
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
` (4 preceding siblings ...)
2012-07-20 8:47 ` [RFC 5/7] MIPS: malta: make KSEG0 uncached Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 7/7] bootm: add very basic ELF support on MIPS (stealed from kexec) Antony Pavlov
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
As most of ELF files are linked to KSEG0,
we need declaration for memory region (KSEG0)
for loading ELF files.
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/boards/qemu-malta/init.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/mips/boards/qemu-malta/init.c b/arch/mips/boards/qemu-malta/init.c
index 0efc92a..1dd268f 100644
--- a/arch/mips/boards/qemu-malta/init.c
+++ b/arch/mips/boards/qemu-malta/init.c
@@ -34,6 +34,7 @@
static int malta_mem_init(void)
{
barebox_add_memory_bank("ram0", 0xa0000000, SZ_256M);
+ barebox_add_memory_bank("cram0", 0x80000000, SZ_256M);
return 0;
}
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC 7/7] bootm: add very basic ELF support on MIPS (stealed from kexec)
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
` (5 preceding siblings ...)
2012-07-20 8:47 ` [RFC 6/7] MIPS: qemu-malta: add KSEG0 memory region Antony Pavlov
@ 2012-07-20 8:47 ` Antony Pavlov
6 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 8:47 UTC (permalink / raw)
To: barebox
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/include/asm/elf.h | 8 +-
arch/mips/lib/Makefile | 2 +
arch/mips/lib/kexec-elf-mips.c | 85 +++++
arch/mips/lib/kexec-mips.c | 66 ++++
arch/mips/lib/kexec-mips.h | 26 ++
common/filetype.c | 4 +
include/filetype.h | 1 +
lib/Makefile | 1 +
lib/kexec/Makefile | 1 +
lib/kexec/kexec-elf-core.c | 30 ++
lib/kexec/kexec-elf-exec.c | 182 +++++++++
lib/kexec/kexec-elf.c | 796 ++++++++++++++++++++++++++++++++++++++++
lib/kexec/kexec-elf.h | 132 +++++++
lib/kexec/kexec.c | 299 +++++++++++++++
lib/kexec/kexec.h | 188 ++++++++++
lib/kexec/unused.h | 15 +
16 files changed, 1835 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/lib/kexec-elf-mips.c
create mode 100644 arch/mips/lib/kexec-mips.c
create mode 100644 arch/mips/lib/kexec-mips.h
create mode 100644 lib/kexec/Makefile
create mode 100644 lib/kexec/kexec-elf-core.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
create mode 100644 lib/kexec/unused.h
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 b99bb71..e520209 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_CPU_MIPS64) += c-r4k.o
obj-$(CONFIG_CMD_MIPS_CPUINFO) += cpuinfo.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
+
+obj-y += kexec-mips.o kexec-elf-mips.o
diff --git a/arch/mips/lib/kexec-elf-mips.c b/arch/mips/lib/kexec-elf-mips.c
new file mode 100644
index 0000000..68c0043
--- /dev/null
+++ b/arch/mips/lib/kexec-elf-mips.c
@@ -0,0 +1,85 @@
+/*
+ * kexec-elf-mips.c - kexec Elf loader for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-elf-ppc.c
+ * Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/types.h>
+#include <fcntl.h>
+#include <elf.h>
+#include "../../../lib/kexec/kexec.h"
+#include "../../../lib/kexec/kexec-elf.h"
+#include "kexec-mips.h"
+
+static const int probe_debug = 0;
+
+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 */
+ if (probe_debug) {
+ fprintf(stderr, "Not for this architecture.\n");
+ }
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+
+ return result;
+}
+
+int elf_mips_load(const char *buf, off_t len, struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ unsigned long cmdline_addr;
+ size_t i;
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0)
+ die("ELF exec parse failed\n");
+
+ /* 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) {
+ /* FIXME: skipped */
+// phdr->p_paddr = virt_to_phys(phdr->p_paddr);
+ }
+ }
+
+ /* Load the Elf data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0)
+ die("ELF exec load failed\n");
+
+ /* FIXME */
+ info->entry = ehdr.e_entry;
+#if 0
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+#endif
+ return 0;
+}
diff --git a/arch/mips/lib/kexec-mips.c b/arch/mips/lib/kexec-mips.c
new file mode 100644
index 0000000..0fdb988
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.c
@@ -0,0 +1,66 @@
+/*
+ * 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 "../../../lib/kexec/kexec.h"
+#include "kexec-mips.h"
+
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long UNUSED(kexec_flags))
+{
+ int memory_ranges = 0;
+
+ memory_range[memory_ranges].start = 0;
+ memory_range[memory_ranges].end = 256 * 1024 * 1024;
+ memory_range[memory_ranges].type = RANGE_RAM;
+ memory_ranges++;
+
+ *range = memory_range;
+ *ranges = memory_ranges;
+ return 0;
+}
+
+struct kexec_file_type kexec_file_type[] = {
+ {"elf-mips", elf_mips_probe, elf_mips_load, /*elf_mips_usage*/ NULL},
+};
+int kexec_file_types = sizeof(kexec_file_type) / sizeof(kexec_file_type[0]);
+
+#ifdef __mips64
+struct arch_options_t arch_options = {
+ .core_header_type = CORE_TYPE_ELF64
+};
+#endif
+
+const struct arch_map_entry arches[] = {
+ /* For compatibility with older patches
+ * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_MIPS here.
+ */
+ { "mips", KEXEC_ARCH_MIPS },
+ { "mips64", KEXEC_ARCH_MIPS },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
diff --git a/arch/mips/lib/kexec-mips.h b/arch/mips/lib/kexec-mips.h
new file mode 100644
index 0000000..474ba0a
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.h
@@ -0,0 +1,26 @@
+#ifndef KEXEC_MIPS_H
+#define KEXEC_MIPS_H
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+extern unsigned char setup_simple_start[];
+extern uint32_t setup_simple_size;
+
+extern struct {
+ uint32_t spr8;
+ uint32_t spr9;
+} setup_simple_regs;
+
+int elf_mips_probe(const char *buf, off_t len);
+int elf_mips_load(/*int argc, char **argv,*/ const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_mips_usage(void);
+
+struct arch_options_t {
+ int core_header_type;
+};
+
+#endif /* KEXEC_MIPS_H */
diff --git a/common/filetype.c b/common/filetype.c
index 1a5b82d..cdedb4d 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include <fs.h>
#include <malloc.h>
+#include <elf.h>
static const char *filetype_str[] = {
[filetype_unknown] = "unknown",
@@ -40,6 +41,7 @@ static const char *filetype_str[] = {
[filetype_aimage] = "Android boot image",
[filetype_sh] = "Bourne Shell",
[filetype_mips_barebox] = "MIPS barebox image",
+ [filetype_elf] = "ELF",
};
const char *file_type_to_string(enum filetype f)
@@ -81,6 +83,8 @@ enum filetype file_detect_type(void *_buf)
return filetype_aimage;
if (strncmp(buf8 + 0x10, "barebox", 7) == 0)
return filetype_mips_barebox;
+ if (strncmp(buf8, ELFMAG, 4) == 0)
+ return filetype_elf;
return filetype_unknown;
}
diff --git a/include/filetype.h b/include/filetype.h
index f5de8ed..0e84937 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -18,6 +18,7 @@ enum filetype {
filetype_aimage,
filetype_sh,
filetype_mips_barebox,
+ filetype_elf,
};
const char *file_type_to_string(enum filetype f);
diff --git a/lib/Makefile b/lib/Makefile
index 4e6b1ee..4e2c0b4 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -34,3 +34,4 @@ obj-$(CONFIG_UNCOMPRESS) += uncompress.o
obj-$(CONFIG_BCH) += bch.o
obj-$(CONFIG_BITREV) += bitrev.o
obj-$(CONFIG_QSORT) += qsort.o
+obj-y += kexec/
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
new file mode 100644
index 0000000..f60a22d
--- /dev/null
+++ b/lib/kexec/Makefile
@@ -0,0 +1 @@
+obj-y += kexec-elf-exec.o kexec-elf.o kexec-elf-core.o kexec.o
diff --git a/lib/kexec/kexec-elf-core.c b/lib/kexec/kexec-elf-core.c
new file mode 100644
index 0000000..6be8713
--- /dev/null
+++ b/lib/kexec/kexec-elf-core.c
@@ -0,0 +1,30 @@
+#include <common.h>
+#include <linux/types.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "elf.h"
+#include "kexec-elf.h"
+
+int build_elf_core_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ int result;
+ result = build_elf_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ return result;
+ }
+ if ((ehdr->e_type != ET_CORE)) {
+ /* not an ELF Core */
+ fprintf(stderr, "Not ELF type ET_CORE\n");
+ return -1;
+ }
+ if (!ehdr->e_phdr) {
+ /* No program header */
+ fprintf(stderr, "No ELF program header\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/lib/kexec/kexec-elf-exec.c b/lib/kexec/kexec-elf-exec.c
new file mode 100644
index 0000000..0d5d5e0
--- /dev/null
+++ b/lib/kexec/kexec-elf-exec.c
@@ -0,0 +1,182 @@
+#include <linux/types.h>
+#include <common.h>
+
+//#include <limits.h>
+//#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <elf.h>
+//#include <boot/elf_boot.h>
+#include "kexec.h"
+#include "kexec-elf.h"
+
+static const int probe_debug = 0;
+
+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) && (ehdr->e_type != ET_DYN) &&
+ (ehdr->e_type != ET_CORE)) {
+ /* not an ELF executable */
+ if (probe_debug) {
+ fprintf(stderr, "Not ELF type ET_EXEC or ET_DYN\n");
+ }
+ return -1;
+ }
+ if (!ehdr->e_phdr) {
+ /* No program header */
+ fprintf(stderr, "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) {
+ fprintf(stderr, "Requires an ELF interpreter\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
+{
+ unsigned long base;
+ int result;
+ size_t i;
+
+ if (!ehdr->e_phdr) {
+ fprintf(stderr, "No program header?\n");
+ result = -1;
+ goto out;
+ }
+
+ /* If I have a dynamic executable find it's size
+ * and then find a location for it in memory.
+ */
+ base = 0;
+ if (ehdr->e_type == ET_DYN) {
+ unsigned long first, last, align;
+ first = ULONG_MAX;
+ last = 0;
+ align = 0;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ unsigned long start, stop;
+ struct mem_phdr *phdr;
+ phdr = &ehdr->e_phdr[i];
+ if ((phdr->p_type != PT_LOAD) ||
+ (phdr->p_memsz == 0))
+ {
+ continue;
+ }
+ start = phdr->p_paddr;
+ stop = start + phdr->p_memsz;
+ if (first > start) {
+ first = start;
+ }
+ if (last < stop) {
+ last = stop;
+ }
+ if (align < phdr->p_align) {
+ align = phdr->p_align;
+ }
+ }
+ /* If I can't use the default paddr find a new
+ * hole for the dynamic executable.
+ */
+#if 0
+ if (!valid_memory_range(info, first, last)) {
+ unsigned long hole;
+ hole = locate_hole(info,
+ last - first + 1, align,
+ 0, elf_max_addr(ehdr), 1);
+ if (hole == ULONG_MAX) {
+ result = -1;
+ goto out;
+ }
+ /* Base is the value that when added
+ * to any virtual address in the file
+ * yields it's load virtual address.
+ */
+ base = hole - first;
+ }
+#endif
+ }
+
+ printf("reading in the PT_LOAD segments\n");
+ /* 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];
+ printf(" found segment %d p_type = %08x\n", i, phdr->p_type);
+ if (phdr->p_type != PT_LOAD) {
+ continue;
+ }
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz) {
+ size = phdr->p_memsz;
+ }
+
+ printf(" add_segment(phdr->p_data = %08x, size = %08x, phdr->p_paddr =%08x, base = %08x, phdr->p_memsz = %08x)\n",
+ phdr->p_data, size, phdr->p_paddr, base, phdr->p_memsz);
+#if 0
+ add_segment(info,
+ phdr->p_data, size,
+ phdr->p_paddr + base, phdr->p_memsz);
+#endif
+
+ {
+ struct resource *elf_resource;
+ resource_size_t start;
+
+ /* FIXME */
+ start = phdr->p_vaddr;
+ elf_resource = request_sdram_region("elf",
+ start, size);
+ if (!elf_resource) {
+ printf("unable to request SDRAM 0x%08x-0x%08x\n",
+ start, start + size - 1);
+ result = -1;
+ goto out;
+ }
+ memcpy(start, phdr->p_data, size);
+ }
+ }
+
+ /* Update entry point to reflect new load address*/
+ ehdr->e_entry += base;
+
+ result = 0;
+ out:
+ return result;
+}
+
+void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, uint32_t flags)
+{
+ int result;
+ /* Parse the Elf file */
+ result = build_elf_exec_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ die("ELF exec parse failed\n");
+ }
+
+ /* Load the Elf data */
+ result = elf_exec_load(ehdr, info);
+ if (result < 0) {
+ die("ELF exec load failed\n");
+ }
+}
diff --git a/lib/kexec/kexec-elf.c b/lib/kexec/kexec-elf.c
new file mode 100644
index 0000000..4459afa
--- /dev/null
+++ b/lib/kexec/kexec-elf.c
@@ -0,0 +1,796 @@
+#include <linux/types.h>
+#include <common.h>
+
+// FIXME: need UINT64_MAX
+//#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "elf.h"
+#include "kexec.h"
+#include "kexec-elf.h"
+
+static const int probe_debug = 0;
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le16_to_cpu(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be16_to_cpu(value);
+ }
+ return value;
+}
+
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le32_to_cpu(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be32_to_cpu(value);
+ }
+ return value;
+}
+
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le64_to_cpu(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be64_to_cpu(value);
+ }
+ return value;
+}
+
+uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = cpu_to_le16(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = cpu_to_be16(value);
+ }
+ return value;
+}
+
+uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = cpu_to_le32(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = cpu_to_be32(value);
+ }
+ return value;
+}
+
+uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = cpu_to_le64(value);
+ }
+ else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = cpu_to_be64(value);
+ }
+ return value;
+}
+
+#define ELF32_MAX 0xffffffff
+#define ELF64_MAX 0xffffffffffffffff
+#if ELF64_MAX > ULONG_MAX
+#undef ELF64_MAX
+#define ELF64_MAX ULONG_MAX
+#endif
+
+unsigned long elf_max_addr(const struct mem_ehdr *ehdr)
+{
+ unsigned long max_addr = 0;
+ if (ehdr->ei_class == ELFCLASS32) {
+ max_addr = ELF32_MAX;
+ }
+ else if (ehdr->ei_class == ELFCLASS64) {
+ max_addr = ELF64_MAX;
+ }
+ return max_addr;
+}
+static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+ Elf32_Ehdr lehdr;
+#if 0
+ if ((uintmax_t)len < (uintmax_t)sizeof(lehdr)) {
+ /* Buffer is to small to be an elf executable */
+ if (probe_debug) {
+ fprintf(stderr, "Buffer is to small to hold ELF header\n");
+ }
+ return -1;
+ }
+#endif
+ memcpy(&lehdr, buf, sizeof(lehdr));
+ if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf32_Ehdr)) {
+ /* Invalid Elf header size */
+ if (probe_debug) {
+ fprintf(stderr, "Bad ELF header size\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
+ /* entry is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_entry to large\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
+ /* phoff is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_phoff to large\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
+ /* shoff is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_shoff to 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)))
+ {
+ /* Invalid program header size */
+ if (probe_debug) {
+ fprintf(stderr, "ELF bad program header size\n");
+ }
+ return -1;
+ }
+ if ((ehdr->e_shnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf32_Shdr)))
+ {
+ /* Invalid section header size */
+ if (probe_debug) {
+ fprintf(stderr, "ELF bad section header size\n");
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+ Elf64_Ehdr lehdr;
+#if 0
+ if ((uintmax_t)len < (uintmax_t)sizeof(lehdr)) {
+ /* Buffer is to small to be an elf executable */
+ if (probe_debug) {
+ fprintf(stderr, "Buffer is to small to hold ELF header\n");
+ }
+ return -1;
+ }
+#endif
+ memcpy(&lehdr, buf, sizeof(lehdr));
+ if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf64_Ehdr)) {
+ /* Invalid Elf header size */
+ if (probe_debug) {
+ fprintf(stderr, "Bad ELF header size\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
+ /* entry is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_entry to large\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
+ /* phoff is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_phoff to large\n");
+ }
+ return -1;
+ }
+ if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
+ /* shoff is to large */
+ if (probe_debug) {
+ fprintf(stderr, "ELF e_shoff to large\n");
+ }
+ return -1;
+ }
+ ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type);
+ ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine);
+ ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version);
+ ehdr->e_entry = elf64_to_cpu(ehdr, lehdr.e_entry);
+ ehdr->e_phoff = elf64_to_cpu(ehdr, lehdr.e_phoff);
+ ehdr->e_shoff = elf64_to_cpu(ehdr, lehdr.e_shoff);
+ ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags);
+ ehdr->e_phnum = elf16_to_cpu(ehdr, lehdr.e_phnum);
+ ehdr->e_shnum = elf16_to_cpu(ehdr, lehdr.e_shnum);
+ ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
+
+ if ((ehdr->e_phnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr)))
+ {
+ /* Invalid program header size */
+ if (probe_debug) {
+ fprintf(stderr, "ELF bad program header size\n");
+ }
+ return -1;
+ }
+ if ((ehdr->e_shnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf64_Shdr)))
+ {
+ /* Invalid section header size */
+ if (probe_debug) {
+ fprintf(stderr, "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 0
+ if ((uintmax_t)len < (uintmax_t)sizeof(e_ident)) {
+ /* Buffer is to small to be an elf executable */
+ if (probe_debug) {
+ fprintf(stderr, "Buffer is to small to hold ELF e_ident\n");
+ }
+ return -1;
+ }
+#endif
+ memcpy(e_ident, buf, sizeof(e_ident));
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
+ /* No ELF header magic */
+ if (probe_debug) {
+ fprintf(stderr, "NO ELF header magic\n");
+ }
+ return -1;
+ }
+ ehdr->ei_class = e_ident[EI_CLASS];
+ ehdr->ei_data = e_ident[EI_DATA];
+ if ( (ehdr->ei_class != ELFCLASS32) &&
+ (ehdr->ei_class != ELFCLASS64))
+ {
+ /* Not a supported elf class */
+ if (probe_debug) {
+ fprintf(stderr, "Not a supported ELF class\n");
+ }
+ return -1;
+ }
+ if ( (ehdr->ei_data != ELFDATA2LSB) &&
+ (ehdr->ei_data != ELFDATA2MSB))
+ {
+ /* Not a supported elf data type */
+ if (probe_debug) {
+ fprintf(stderr, "Not a supported ELF data format\n");
+ }
+ return -1;
+ }
+
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_ehdr(buf, len, ehdr);
+ }
+ else if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_ehdr(buf, len, ehdr);
+ }
+ if (result < 0) {
+ return result;
+ }
+ if ((e_ident[EI_VERSION] != EV_CURRENT) ||
+ (ehdr->e_version != EV_CURRENT))
+ {
+ if (probe_debug) {
+ fprintf(stderr, "Unknown ELF version\n");
+ }
+ /* Unknwon elf version */
+ 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))
+ {
+ fprintf(stderr, "Program segment size out of range\n");
+ return -1;
+ }
+
+ phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type);
+ phdr->p_paddr = elf32_to_cpu(ehdr, lphdr.p_paddr);
+ phdr->p_vaddr = elf32_to_cpu(ehdr, lphdr.p_vaddr);
+ phdr->p_filesz = elf32_to_cpu(ehdr, lphdr.p_filesz);
+ phdr->p_memsz = elf32_to_cpu(ehdr, lphdr.p_memsz);
+ phdr->p_offset = elf32_to_cpu(ehdr, lphdr.p_offset);
+ phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags);
+ phdr->p_align = elf32_to_cpu(ehdr, lphdr.p_align);
+
+ return 0;
+}
+
+static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_phdr *phdr;
+ const char *pbuf;
+ Elf64_Phdr lphdr;
+ pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr));
+ phdr = &ehdr->e_phdr[idx];
+ memcpy(&lphdr, pbuf, sizeof(lphdr));
+
+#if 0
+ if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_memsz) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_offset) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_paddr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_vaddr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lphdr.p_align) > UINT64_MAX))
+ {
+ fprintf(stderr, "Program segment size out of range\n");
+ return -1;
+ }
+#endif
+
+ phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type);
+ phdr->p_paddr = elf64_to_cpu(ehdr, lphdr.p_paddr);
+ phdr->p_vaddr = elf64_to_cpu(ehdr, lphdr.p_vaddr);
+ phdr->p_filesz = elf64_to_cpu(ehdr, lphdr.p_filesz);
+ phdr->p_memsz = elf64_to_cpu(ehdr, lphdr.p_memsz);
+ phdr->p_offset = elf64_to_cpu(ehdr, lphdr.p_offset);
+ phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags);
+ phdr->p_align = elf64_to_cpu(ehdr, lphdr.p_align);
+
+ return 0;
+}
+
+static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ size_t phdr_size, mem_phdr_size, i;
+
+ /* e_phnum is at most 65535 so calculating
+ * the size of the program header cannot overflow.
+ */
+ /* Is the program header in the file buffer? */
+ phdr_size = 0;
+ if (ehdr->ei_class == ELFCLASS32) {
+ phdr_size = sizeof(Elf32_Phdr);
+ }
+ else if (ehdr->ei_class == ELFCLASS64) {
+ phdr_size = sizeof(Elf64_Phdr);
+ }
+ else {
+ fprintf(stderr, "Invalid ei_class?\n");
+ return -1;
+ }
+ phdr_size *= ehdr->e_phnum;
+#if 0
+ if ((uintmax_t)(ehdr->e_phoff + phdr_size) > (uintmax_t)len) {
+ /* The program header did not fit in the file buffer */
+ if (probe_debug || (flags & ELF_SKIP_FILESZ_CHECK)) {
+ fprintf(stderr, "ELF program headers truncated"
+ " have %ju bytes need %ju bytes\n",
+ (uintmax_t)len,
+ (uintmax_t)(ehdr->e_phoff + phdr_size));
+ }
+ return -1;
+ }
+#endif
+
+ /* Allocate the e_phdr array */
+ mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum;
+ ehdr->e_phdr = xmalloc(mem_phdr_size);
+
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ struct mem_phdr *phdr;
+ int result;
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_phdr(buf, ehdr, i);
+
+ }
+ else if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_phdr(buf, ehdr, i);
+ }
+ if (result < 0) {
+ return result;
+ }
+
+ /* Check the program headers to be certain
+ * they are safe to use.
+ * Skip the check if ELF_SKIP_FILESZ_CHECK is set.
+ */
+ phdr = &ehdr->e_phdr[i];
+#if 0
+ if (!(flags & ELF_SKIP_FILESZ_CHECK)
+ && (uintmax_t)(phdr->p_offset + phdr->p_filesz) >
+ (uintmax_t)len) {
+ /* The segment does not fit in the buffer */
+ if (probe_debug) {
+ fprintf(stderr, "ELF segment not in file\n");
+ }
+ return -1;
+ }
+#endif
+ if ((phdr->p_paddr + phdr->p_memsz) < phdr->p_paddr) {
+ /* The memory address wraps */
+ if (probe_debug) {
+ fprintf(stderr, "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))
+ {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "Bad section header(%x) entsize: %lld\n",
+ shdr->sh_type, shdr->sh_entsize);
+ return -1;
+ }
+ return 0;
+}
+
+static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_shdr *shdr;
+ const char *sbuf;
+ int size_ok;
+ Elf64_Shdr lshdr;
+ sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr));
+ shdr = &ehdr->e_shdr[idx];
+ memcpy(&lshdr, sbuf, sizeof(lshdr));
+
+#if 0
+ if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_addr) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_offset) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_size) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_addralign) > UINT64_MAX) ||
+ (elf64_to_cpu(ehdr, lshdr.sh_entsize) > UINT64_MAX))
+ {
+ fprintf(stderr, "Program section size out of range\n");
+ return -1;
+ }
+#endif
+
+ shdr->sh_name = elf32_to_cpu(ehdr, lshdr.sh_name);
+ shdr->sh_type = elf32_to_cpu(ehdr, lshdr.sh_type);
+ shdr->sh_flags = elf64_to_cpu(ehdr, lshdr.sh_flags);
+ shdr->sh_addr = elf64_to_cpu(ehdr, lshdr.sh_addr);
+ shdr->sh_offset = elf64_to_cpu(ehdr, lshdr.sh_offset);
+ shdr->sh_size = elf64_to_cpu(ehdr, lshdr.sh_size);
+ shdr->sh_link = elf32_to_cpu(ehdr, lshdr.sh_link);
+ shdr->sh_info = elf32_to_cpu(ehdr, lshdr.sh_info);
+ shdr->sh_addralign = elf64_to_cpu(ehdr, lshdr.sh_addralign);
+ shdr->sh_entsize = elf64_to_cpu(ehdr, lshdr.sh_entsize);
+
+ /* Now verify sh_entsize */
+ size_ok = 0;
+ switch(shdr->sh_type) {
+ case SHT_SYMTAB:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Sym);
+ break;
+ case SHT_RELA:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
+ break;
+ case SHT_DYNAMIC:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Dyn);
+ break;
+ case SHT_REL:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
+ break;
+ case SHT_NOTE:
+ case SHT_NULL:
+ case SHT_PROGBITS:
+ case SHT_HASH:
+ case SHT_NOBITS:
+ default:
+ /* This is a section whose entsize requirements
+ * I don't care about. If I don't know about
+ * the section I can't care about it's entsize
+ * requirements.
+ */
+ size_ok = 1;
+ break;
+ }
+ if (!size_ok) {
+ fprintf(stderr, "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 {
+ fprintf(stderr, "Invalid ei_class?\n");
+ return -1;
+ }
+ shdr_size *= ehdr->e_shnum;
+#if 0
+ if ((uintmax_t)(ehdr->e_shoff + shdr_size) > (uintmax_t)len) {
+ /* The section header did not fit in the file buffer */
+ if (probe_debug) {
+ fprintf(stderr, "ELF section header does not fit in file\n");
+ }
+ return -1;
+ }
+#endif
+
+ /* Allocate the e_shdr array */
+ mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum;
+ ehdr->e_shdr = xmalloc(mem_shdr_size);
+
+ for(i = 0; i < ehdr->e_shnum; i++) {
+ struct mem_shdr *shdr;
+ int result;
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_shdr(buf, ehdr, i);
+ }
+ else if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_shdr(buf, ehdr, i);
+ }
+ if (result < 0) {
+ return result;
+ }
+ /* Check the section headers to be certain
+ * they are safe to use.
+ * Skip the check if ELF_SKIP_FILESZ_CHECK is set.
+ */
+ shdr = &ehdr->e_shdr[i];
+#if 0
+ if (!(flags & ELF_SKIP_FILESZ_CHECK)
+ && (shdr->sh_type != SHT_NOBITS)
+ && (uintmax_t)(shdr->sh_offset + shdr->sh_size) >
+ (uintmax_t)len) {
+ /* The section does not fit in the buffer */
+ if (probe_debug) {
+ fprintf(stderr, "ELF section %zd not in file\n",
+ i);
+ }
+ return -1;
+ }
+#endif
+ if ((shdr->sh_addr + shdr->sh_size) < shdr->sh_addr) {
+ /* The memory address wraps */
+ if (probe_debug) {
+ fprintf(stderr, "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.
+ */
+ fprintf(stderr, "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;
+}
diff --git a/lib/kexec/kexec-elf.h b/lib/kexec/kexec-elf.h
new file mode 100644
index 0000000..99cb80b
--- /dev/null
+++ b/lib/kexec/kexec-elf.h
@@ -0,0 +1,132 @@
+#ifndef KEXEC_ELF_H
+#define KEXEC_ELF_H
+
+struct kexec_info;
+
+struct mem_ehdr {
+ unsigned ei_class;
+ unsigned ei_data;
+ unsigned e_type;
+ unsigned e_machine;
+ unsigned e_version;
+ unsigned e_flags;
+ unsigned e_phnum;
+ unsigned e_shnum;
+ unsigned e_shstrndx;
+ unsigned long long e_entry;
+ unsigned long long e_phoff;
+ unsigned long long e_shoff;
+ unsigned e_notenum;
+ struct mem_phdr *e_phdr;
+ struct mem_shdr *e_shdr;
+ struct mem_note *e_note;
+ unsigned long rel_addr, rel_size;
+};
+
+struct mem_phdr {
+ unsigned long long p_paddr;
+ unsigned long long p_vaddr;
+ unsigned long long p_filesz;
+ unsigned long long p_memsz;
+ unsigned long long p_offset;
+ const char *p_data;
+ unsigned p_type;
+ unsigned p_flags;
+ unsigned long long p_align;
+};
+
+struct mem_shdr {
+ unsigned sh_name;
+ unsigned sh_type;
+ unsigned long long sh_flags;
+ unsigned long long sh_addr;
+ unsigned long long sh_offset;
+ unsigned long long sh_size;
+ unsigned sh_link;
+ unsigned sh_info;
+ unsigned long long sh_addralign;
+ unsigned long long sh_entsize;
+ const unsigned char *sh_data;
+};
+
+struct mem_sym {
+ unsigned long st_name; /* Symbol name (string tbl index) */
+ unsigned char st_info; /* No defined meaning, 0 */
+ unsigned char st_other; /* Symbol type and binding */
+ unsigned st_shndx; /* Section index */
+ unsigned long long st_value; /* Symbol value */
+ unsigned long long st_size; /* Symbol size */
+};
+
+struct mem_rela {
+ unsigned long long r_offset;
+ unsigned r_sym;
+ unsigned r_type;
+ unsigned long long r_addend;
+};
+
+struct mem_note {
+ unsigned n_type;
+ unsigned n_descsz;
+ const char *n_name;
+ const void *n_desc;
+};
+
+/* The definition of an ELF note does not vary depending
+ * on ELFCLASS.
+ */
+typedef struct
+{
+ uint32_t n_namesz; /* Length of the note's name. */
+ uint32_t n_descsz; /* Length of the note's descriptor. */
+ uint32_t n_type; /* Type of the note. */
+} ElfNN_Nhdr;
+
+/* Misc flags */
+
+#define ELF_SKIP_FILESZ_CHECK 0x00000001
+
+extern void free_elf_info(struct mem_ehdr *ehdr);
+extern int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags);
+extern int build_elf_exec_info(const char *buf, off_t len,
+ struct mem_ehdr *ehdr, uint32_t flags);
+extern int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags);
+
+extern int build_elf_core_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);
+extern int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
+ unsigned long min, unsigned long max, int end);
+
+extern void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, uint32_t flags);
+extern void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, unsigned long min, unsigned long max,
+ int end, uint32_t flags);
+
+extern int elf_rel_find_symbol(struct mem_ehdr *ehdr,
+ const char *name, struct mem_sym *ret_sym);
+extern unsigned long elf_rel_get_addr(struct mem_ehdr *ehdr, const char *name);
+extern void elf_rel_set_symbol(struct mem_ehdr *ehdr,
+ const char *name, const void *buf, size_t size);
+extern void elf_rel_get_symbol(struct mem_ehdr *ehdr,
+ const char *name, void *buf, size_t size);
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value);
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value);
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value);
+
+uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value);
+uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value);
+uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value);
+
+unsigned long elf_max_addr(const struct mem_ehdr *ehdr);
+
+/* Architecture specific helper functions */
+extern int machine_verify_elf_rel(struct mem_ehdr *ehdr);
+extern void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
+ void *location, unsigned long address, unsigned long value);
+#endif /* KEXEC_ELF_H */
+
diff --git a/lib/kexec/kexec.c b/lib/kexec/kexec.c
new file mode 100644
index 0000000..3e9a916
--- /dev/null
+++ b/lib/kexec/kexec.c
@@ -0,0 +1,299 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * Modified (2007-05-15) by Francesco Chiechi to rudely handle mips platform
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#include <linux/types.h>
+#include <common.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+//#include <limits.h>
+//#include <sys/types.h>
+#include <linux/stat.h>
+//#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+//#include <ctype.h>
+
+#include "config.h"
+
+#include "kexec.h"
+//#include "kexec-syscall.h"
+#include "kexec-elf.h"
+#include <fs.h>
+
+unsigned long long mem_min = 0;
+unsigned long long mem_max = ULONG_MAX;
+int kexec_debug = 0;
+
+void die(char *fmt, ...)
+{
+}
+
+int valid_memory_range(struct kexec_info *info,
+ unsigned long sstart, unsigned long send)
+{
+ int i;
+ if (sstart > send) {
+ return 0;
+ }
+ if ((send > mem_max) || (sstart < mem_min)) {
+ return 0;
+ }
+ for (i = 0; i < info->memory_ranges; i++) {
+ unsigned long mstart, mend;
+ /* Only consider memory ranges */
+ if (info->memory_range[i].type != RANGE_RAM)
+ continue;
+ mstart = info->memory_range[i].start;
+ mend = info->memory_range[i].end;
+ if (i < info->memory_ranges - 1
+ && mend == info->memory_range[i+1].start
+ && info->memory_range[i+1].type == RANGE_RAM)
+ mend = info->memory_range[i+1].end;
+
+ /* Check to see if we are fully contained */
+ if ((mstart <= sstart) && (mend >= send)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int valid_memory_segment(struct kexec_info *info,
+ struct kexec_segment *segment)
+{
+ unsigned long sstart, send;
+ sstart = (unsigned long)segment->mem;
+ send = sstart + segment->memsz - 1;
+
+ return valid_memory_range(info, sstart, send);
+}
+
+static void print_segments(int f, struct kexec_info *info)
+{
+ int i;
+
+ fprintf(f, "nr_segments = %d\n", info->nr_segments);
+ for (i = 0; i < info->nr_segments; i++) {
+ fprintf(f, "segment[%d].buf = %p\n", i,
+ info->segment[i].buf);
+ fprintf(f, "segment[%d].bufsz = %zx\n", i,
+ info->segment[i].bufsz);
+ fprintf(f, "segment[%d].mem = %p\n", i,
+ info->segment[i].mem);
+ fprintf(f, "segment[%d].memsz = %zx\n", i,
+ info->segment[i].memsz);
+ }
+}
+
+int sort_segments(struct kexec_info *info)
+{
+ int i, j;
+ void *end;
+
+ /* Do a stupid insertion sort... */
+ for (i = 0; i < info->nr_segments; i++) {
+ int tidx;
+ struct kexec_segment temp;
+ tidx = i;
+ for (j = i +1; j < info->nr_segments; j++) {
+ if (info->segment[j].mem < info->segment[tidx].mem) {
+ tidx = j;
+ }
+ }
+ if (tidx != i) {
+ temp = info->segment[tidx];
+ info->segment[tidx] = info->segment[i];
+ info->segment[i] = temp;
+ }
+ }
+ /* Now see if any of the segments overlap */
+ end = 0;
+ for (i = 0; i < info->nr_segments; i++) {
+ if (end > info->segment[i].mem) {
+ fprintf(stderr, "Overlapping memory segments at %p\n",
+ end);
+ return -1;
+ }
+ end = ((char *)info->segment[i].mem) + info->segment[i].memsz;
+ }
+ return 0;
+}
+
+/*
+ * Load the new kernel
+ */
+static int my_load(char *kernel, unsigned long kexec_flags)
+{
+ char *kernel_buf;
+ off_t kernel_size;
+ int i = 0;
+ int result;
+ struct kexec_info info;
+ long native_arch;
+
+//printf("%s:%d\n", __func__, __LINE__);
+ memset(&info, 0, sizeof(info));
+ info.segment = NULL;
+ info.nr_segments = 0;
+ info.entry = NULL;
+ info.backup_start = 0;
+ info.kexec_flags = kexec_flags;
+
+ result = 0;
+ /* slurp in the input kernel */
+ /* FIXME: add a decompresion routines insted of read_file() */
+ kernel_buf = read_file(kernel, &kernel_size);
+ printf("kernel: %p kernel_size: %lx\n", kernel_buf, kernel_size);
+
+//printf("%s:%d\n", __func__, __LINE__);
+ /* FIXME: check memory banks */
+ if (get_memory_ranges(&info.memory_range, &info.memory_ranges,
+ info.kexec_flags) < 0) {
+ fprintf(stderr, "Could not get memory layout\n");
+ return -1;
+ }
+
+//printf("%s:%d\n", __func__, __LINE__);
+ for (i = 0; i < kexec_file_types; i++) {
+ if (kexec_file_type[i].probe(kernel_buf, kernel_size) >= 0)
+ break;
+ }
+//printf("%s:%d\n", __func__, __LINE__);
+ if (i == kexec_file_types) {
+ fprintf(stderr, "Cannot determine the file type "
+ "of %s\n", kernel);
+ return -1;
+ }
+//printf("%s:%d\n", __func__, __LINE__);
+ /* Figure out our native architecture before load */
+#if 0
+ native_arch = physical_arch();
+ if (native_arch < 0) {
+ return -1;
+ }
+#endif
+ native_arch = 0;
+ info.kexec_flags |= native_arch;
+
+//printf("%s:%d\n", __func__, __LINE__);
+ result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
+//printf("%s:%d\n", __func__, __LINE__);
+ if (result < 0) {
+ switch (result) {
+ case EFAILED:
+ default:
+ fprintf(stderr, "Cannot load %s\n", kernel);
+ break;
+ }
+ return result;
+ }
+#if 0
+ /* If we are not in native mode setup an appropriate trampoline */
+ if (arch_compat_trampoline(&info) < 0) {
+ return -1;
+ }
+#endif
+//printf("%s:%d\n", __func__, __LINE__);
+ /* Verify all of the segments load to a valid location in memory */
+ for (i = 0; i < info.nr_segments; i++) {
+ if (!valid_memory_segment(&info, info.segment +i)) {
+ fprintf(stderr, "Invalid memory segment %p - %p\n",
+ info.segment[i].mem,
+ ((char *)info.segment[i].mem) +
+ info.segment[i].memsz);
+ return -1;
+ }
+ }
+ /* Sort the segments and verify we don't have overlaps */
+ if (sort_segments(&info) < 0) {
+ return -1;
+ }
+#if 1
+ fprintf(stderr, "kexec_load: entry = %p flags = %lx\n",
+ info.entry, info.kexec_flags);
+ print_segments(stderr, &info);
+#endif
+#if 0
+ result = kexec_load(
+ info.entry, info.nr_segments, info.segment, info.kexec_flags);
+#else
+ {
+ void (*barebox)(int a0, int a1, int a2, int a3);
+
+ barebox = info.entry;
+
+ printf("\njumping to %p\n\n", barebox);
+ barebox(2, /* number of arguments? */
+ 0x80002000,
+ 0x80002008,
+ 0x10000000 /* no matter */
+ );
+ }
+#endif
+ if (result != 0) {
+ /* The load failed, print some debugging information */
+ fprintf(stderr, "kexec_load failed: %s\n",
+ strerror(errno));
+ fprintf(stderr, "entry = %p flags = %lx\n",
+ info.entry, info.kexec_flags);
+ print_segments(stderr, &info);
+ }
+ return result;
+}
+
+#include <boot.h>
+#include <init.h>
+#include <binfmt.h>
+
+static int do_bootm_elf(struct image_data *data)
+{
+ printf("\ndo_bootm_elf()\n\n");
+
+ my_load(data->os_file, 0);
+
+ /* unreachable(); */
+ return -1;
+}
+
+static struct image_handler elf_handler = {
+ .name = "ELF",
+ .bootm = do_bootm_elf,
+ .filetype = filetype_elf,
+};
+
+static struct binfmt_hook binfmt_elf_hook = {
+ .type = filetype_elf,
+ .exec = "bootm",
+};
+
+static int elf_register_image_handler(void)
+{
+ register_image_handler(&elf_handler);
+ binfmt_register(&binfmt_elf_hook);
+
+ return 0;
+}
+late_initcall(elf_register_image_handler);
diff --git a/lib/kexec/kexec.h b/lib/kexec/kexec.h
new file mode 100644
index 0000000..4233c1f
--- /dev/null
+++ b/lib/kexec/kexec.h
@@ -0,0 +1,188 @@
+#ifndef KEXEC_H
+#define KEXEC_H
+
+#include <common.h>
+
+#include <stdio.h>
+#include <string.h>
+#define USE_BSD
+#include <asm/byteorder.h>
+#define _GNU_SOURCE
+
+#include "kexec-elf.h"
+#include "unused.h"
+
+/*
+ * Document some of the reasons why crashdump may fail, so we can give
+ * better error messages
+ */
+#define EFAILED -1 /* default error code */
+
+extern unsigned long long mem_min, mem_max;
+extern int kexec_debug;
+
+#define dbgprintf(...) \
+do { \
+ if (kexec_debug) \
+ fprintf(stderr, __VA_ARGS__); \
+} while(0)
+
+struct kexec_segment {
+ const void *buf;
+ size_t bufsz;
+ const void *mem;
+ size_t memsz;
+};
+
+struct memory_range {
+ unsigned long long start, end;
+ unsigned type;
+#define RANGE_RAM 0
+#define RANGE_RESERVED 1
+#define RANGE_ACPI 2
+#define RANGE_ACPI_NVS 3
+#define RANGE_UNCACHED 4
+};
+
+struct kexec_info {
+ struct kexec_segment *segment;
+ int nr_segments;
+ struct memory_range *memory_range;
+ int memory_ranges;
+ void *entry;
+ struct mem_ehdr rhdr;
+ unsigned long backup_start;
+ unsigned long kexec_flags;
+ unsigned long backup_src_start;
+ unsigned long backup_src_size;
+};
+
+struct arch_map_entry {
+ const char *machine;
+ unsigned long arch;
+};
+
+extern const struct arch_map_entry arches[];
+long physical_arch(void);
+
+#define KERNEL_VERSION(major, minor, patch) \
+ (((major) << 16) | ((minor) << 8) | patch)
+long kernel_version(void);
+
+void usage(void);
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags);
+int valid_memory_range(struct kexec_info *info,
+ unsigned long sstart, unsigned long send);
+int sort_segments(struct kexec_info *info);
+unsigned long locate_hole(struct kexec_info *info,
+ unsigned long hole_size, unsigned long hole_align,
+ unsigned long hole_min, unsigned long hole_max,
+ int hole_end);
+
+typedef int (probe_t)(const char *kernel_buf, off_t kernel_size);
+typedef int (load_t )(//int argc, char **argv,
+ const char *kernel_buf, off_t kernel_size,
+ struct kexec_info *info);
+typedef void (usage_t)(void);
+struct kexec_file_type {
+ const char *name;
+ probe_t *probe;
+ load_t *load;
+ usage_t *usage;
+};
+
+extern struct kexec_file_type kexec_file_type[];
+extern int kexec_file_types;
+
+extern void die(char *fmt, ...);
+extern void *xrealloc(void *ptr, size_t size);
+extern unsigned long virt_to_phys(unsigned long addr);
+extern void add_segment(struct kexec_info *info,
+ const void *buf, size_t bufsz, unsigned long base, size_t memsz);
+extern void add_segment_phys_virt(struct kexec_info *info,
+ const void *buf, size_t bufsz, unsigned long base, size_t memsz,
+ int phys);
+extern unsigned long add_buffer(struct kexec_info *info,
+ const void *buf, unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min, unsigned long buf_max,
+ int buf_end);
+extern unsigned long add_buffer_virt(struct kexec_info *info,
+ const void *buf, unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min, unsigned long buf_max,
+ int buf_end);
+extern unsigned long add_buffer_phys_virt(struct kexec_info *info,
+ const void *buf, unsigned long bufsz, unsigned long memsz,
+ unsigned long buf_align, unsigned long buf_min, unsigned long buf_max,
+ int buf_end, int phys);
+
+extern char purgatory[];
+extern size_t purgatory_size;
+
+int arch_compat_trampoline(struct kexec_info *info);
+char *get_command_line(void);
+
+int kexec_iomem_for_each_line(char *match,
+ int (*callback)(void *data,
+ int nr,
+ char *str,
+ unsigned long base,
+ unsigned long length),
+ void *data);
+int parse_iomem_single(char *str, uint64_t *start, uint64_t *end);
+const char * proc_iomem(void);
+
+extern int add_backup_segments(struct kexec_info *info,
+ unsigned long backup_base,
+ unsigned long backup_size);
+
+#define MAX_LINE 160
+
+char *concat_cmdline(const char *base, const char *append);
+
+/* 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)
+
+#endif /* KEXEC_H */
diff --git a/lib/kexec/unused.h b/lib/kexec/unused.h
new file mode 100644
index 0000000..7b40190
--- /dev/null
+++ b/lib/kexec/unused.h
@@ -0,0 +1,15 @@
+#ifndef UNUSED_H
+#define UNUSED_H
+
+/* http://sourcefrog.net/weblog/software/languages/C/unused.html */
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+#endif
--
1.7.10
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map
2012-07-20 8:47 ` [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map Antony Pavlov
@ 2012-07-20 10:01 ` Antony Pavlov
0 siblings, 0 replies; 9+ messages in thread
From: Antony Pavlov @ 2012-07-20 10:01 UTC (permalink / raw)
To: barebox
Sorry, this patch can't be applied!
It cites diff-formatted text in the comment.
adding extra spaces to this cited text fixes the problem.
On 20 July 2012 12:47, Antony Pavlov <antonynpavlov@gmail.com> wrote:
> YAMON-style GT64120 memory map make move UART to the new position.
>
> We need YAMON-style GT64120 memory for running Linux kernel from
> barebox.
>
> WARNING! WIP! We need add some more effor for compartability
> with qemu embedded YAMON.
>
> E.g. we need put special Board ID at the start of bootrom:
>
> --- a/arch/mips/boot/start.S
> +++ b/arch/mips/boot/start.S
> @@ -59,6 +59,10 @@ EXPORT(_start)
> nop
>
> .org 0x10
> +#if 1
> + /* Board ID = 0x420 (Malta Board with CoreLV) */
> + .word 0x00000420
> +#endif
>
> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
> ---
> arch/mips/mach-malta/include/mach/hardware.h | 2 +-
> arch/mips/mach-malta/lowlevel_init.S | 33 +++++++++++++++++++++++++-
> 2 files changed, 33 insertions(+), 2 deletions(-)
>
> diff --git a/arch/mips/mach-malta/include/mach/hardware.h b/arch/mips/mach-malta/include/mach/hardware.h
> index ddeb1b7..fde8f22 100644
> --- a/arch/mips/mach-malta/include/mach/hardware.h
> +++ b/arch/mips/mach-malta/include/mach/hardware.h
> @@ -21,7 +21,7 @@
> #ifndef __INCLUDE_ARCH_HARDWARE_H__
> #define __INCLUDE_ARCH_HARDWARE_H__
>
> -#define DEBUG_LL_UART_ADDR 0xb00003f8
> +#define DEBUG_LL_UART_ADDR 0xb80003f8
> #define DEBUG_LL_UART_SHIFT 0
>
> /*
> diff --git a/arch/mips/mach-malta/lowlevel_init.S b/arch/mips/mach-malta/lowlevel_init.S
> index a34de74..a0134f8 100644
> --- a/arch/mips/mach-malta/lowlevel_init.S
> +++ b/arch/mips/mach-malta/lowlevel_init.S
> @@ -29,7 +29,38 @@
>
> LEAF(mach_init_lowlevel)
>
> - /* nothing special yet */
> + /*
> + * Load BAR registers of GT64120 as done by YAMON
> + *
> + * based on write_bootloader() in qemu.git/hw/mips_malta.c
> + * see GT64120 manual and qemu.git/hw/gt64xxx.c for details
> + *
> + * This is big-endian version of code!
> + */
> +
> + /* move GT64120 registers to 0x1be00000 */
> + lui t1, 0xb400
> + lui t0, 0xdf00
> + sw t0, 104(t1)
> +
> + /* setup MEM-to-PCI0 mapping */
> + lui t1, 0xbbe0
> +
> + /* setup PCI0 io window to 18000000-181fffff */
> + lui t0, 0xc000
> + sw t0, 72(t1) /* GT_PCI0IOLD */
> + lui t0, 0x4000
> + sw t0, 80(t1) /* GT_PCI0IOHD */
> +
> + /* setup PCI0 mem windows */
> + lui t0, 0x8000
> + sw t0, 88(t1) /* GT_PCI0M0LD */
> + lui t0, 0x3f00
> + sw t0, 96(t1) /* GT_PCI0M0HD */
> + lui t0, 0xc100
> + sw t0, 128(t1) /* GT_PCI0M1LD */
> + lui t0, 0x5e00
> + sw t0, 136(t1) /* GT_PCI0M1HD */
>
> /* return */
> b mach_init_lowlevel_return
> --
> 1.7.10
>
--
Best regards,
Antony Pavlov
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-07-20 10:03 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-20 8:47 [WIP] add very basic ELF support on MIPS Antony Pavlov
2012-07-20 8:47 ` [RFC 1/7] MIPS: move start.S code to .text_entry section Antony Pavlov
2012-07-20 8:47 ` [RFC 2/7] MIPS: add the initial support for lowlevel init Antony Pavlov
2012-07-20 8:47 ` [RFC 3/7] MIPS: malta: add bogus " Antony Pavlov
2012-07-20 8:47 ` [RFC 4/7] MIPS: malta: add YAMON-style GT64120 memory map Antony Pavlov
2012-07-20 10:01 ` Antony Pavlov
2012-07-20 8:47 ` [RFC 5/7] MIPS: malta: make KSEG0 uncached Antony Pavlov
2012-07-20 8:47 ` [RFC 6/7] MIPS: qemu-malta: add KSEG0 memory region Antony Pavlov
2012-07-20 8:47 ` [RFC 7/7] bootm: add very basic ELF support on MIPS (stealed from kexec) Antony Pavlov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox