2013-09-17 10:01:59

by Geert Uytterhoeven

[permalink] [raw]
Subject: Preliminary kexec support for Linux/m68k

This is a preliminary set of patches to add kexec support for m68k.

- Kexec only, no kdump support yet (do you have enough RAM to keep a
crashdump kernel in memory at all times? ;-)

- Tested on ARAnyM only. No support for other CPU/MMUs than 68040.

- Although the code contains some phys/virt conversions, it will probably
fail miserably on platforms where kernel virtual addresses are different
from physical address.

- To have automatic "kexec -e" on reboot, copy /etc/rc6.d/S85kexec from
another system, and fix it up for kexec living in /usr/local/sbin instead
of /sbin.

- Sample invocation:

kexec -d -l vmlinux --reuse-cmdline


KERNEL:

Patches:
- [PATCH 1/3] m68k: Add preliminary kexec support
- [PATCH 2/3] m68k: Add support to export bootinfo in procfs
- [PATCH 3/3] [RFC] m68k: Add System RAM to /proc/iomem

Notes:
- The bootinfo is now saved and exported to /proc/bootinfo, so kexec-tools
can read it and pass it (possibly after modification) to the new kernel.
This is similar to /proc/atags on ARM.

- We should create arch/m68k/include/uapi/asm/bootinfo.h (and probably a few
more, e.g. machine-specific model IDs), as this is needed by both m68kboot
and kexec-tools.

- I based [PATCH 3/3] on the PowerPC version, but it's no longer needed as we
now get this information from the bootinfo.
Does anyone think this is nice to have anyway?


KEXEC-TOOLS:

Patches:
- [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes
- [PATCH 2/2] kexec: Add preliminary m68k support

Notes:
- Based on git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git

- Tagged bootinfo is read from /proc/bootinfo by default, but this can be
overridden using --bootinfo. No bootinfo editor is provided.
The kexec command will replace/delete command line and ramdisk tags in the
bootinfo.

- The ramdisk is loaded at the top of memory minus 4096, unlike with
m68boot (ataboot/amiboot), as locate_hole() seems to have a bug that it
cannot reserve a block at the real top of memory.

- The first unused page of the kernel image (at address zero) is
automatically removed, just like m68kboot does.
If I don't do this, relocate_new_kernel() fails with a "cannot handle
kernel paging request at address NULL" exception, although the MMU is
disabled at that point.
As m68kboot does this too, I guess this is OK?

- Do we want to check the struct bootversion at the start of the kernel,
like m68kboot does?
Kexec may be used to load ELF files that are not Linux kernel images,
and thus don't have a Linux-specific struct bootversion.

- Do we want to check the size of the kernel image + bootinfo, and warn the
user if it's larger than 4 MiB?
This is a limitation of the current Linux/m68k kernel only.

All comments are welcome!
Have fun! ;-)

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds


2013-09-17 10:02:18

by Geert Uytterhoeven

[permalink] [raw]
Subject: [PATCH 1/3] m68k: Add preliminary kexec support

Signed-off-by: Geert Uytterhoeven <[email protected]>
---
arch/m68k/Kconfig | 17 ++++++++
arch/m68k/include/asm/kexec.h | 29 +++++++++++++
arch/m68k/kernel/Makefile | 2 +
arch/m68k/kernel/machine_kexec.c | 71 ++++++++++++++++++++++++++++++++
arch/m68k/kernel/relocate_kernel.S | 79 ++++++++++++++++++++++++++++++++++++
include/uapi/linux/kexec.h | 1 +
6 files changed, 199 insertions(+)
create mode 100644 arch/m68k/include/asm/kexec.h
create mode 100644 arch/m68k/kernel/machine_kexec.c
create mode 100644 arch/m68k/kernel/relocate_kernel.S

diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 311a300..d60497f 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -86,6 +86,23 @@ config MMU_SUN3
bool
depends on MMU && !MMU_MOTOROLA && !MMU_COLDFIRE

+config KEXEC
+ bool "kexec system call"
+ depends on MMU # FIXME
+ help
+ kexec is a system call that implements the ability to shutdown your
+ current kernel, and to start another kernel. It is like a reboot
+ but it is independent of the system firmware. And like a reboot
+ you can start any kernel with it, not just Linux.
+
+ The name comes from the similarity to the exec system call.
+
+ It is an ongoing process to be certain the hardware in a machine
+ is properly shutdown, so do not be surprised if this code does not
+ initially work for you. As of this writing the exact hardware
+ interface is strongly in flux, so no good recommendation can be
+ made.
+
menu "Platform setup"

source arch/m68k/Kconfig.cpu
diff --git a/arch/m68k/include/asm/kexec.h b/arch/m68k/include/asm/kexec.h
new file mode 100644
index 0000000..3df97ab
--- /dev/null
+++ b/arch/m68k/include/asm/kexec.h
@@ -0,0 +1,29 @@
+#ifndef _ASM_M68K_KEXEC_H
+#define _ASM_M68K_KEXEC_H
+
+#ifdef CONFIG_KEXEC
+
+/* Maximum physical address we can use pages from */
+#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
+/* Maximum address we can reach in physical address mode */
+#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
+/* Maximum address we can use for the control code buffer */
+#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
+
+#define KEXEC_CONTROL_PAGE_SIZE 4096
+
+#define KEXEC_ARCH KEXEC_ARCH_68K
+
+#ifndef __ASSEMBLY__
+
+static inline void crash_setup_regs(struct pt_regs *newregs,
+ struct pt_regs *oldregs)
+{
+ /* Dummy implementation for now */
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_KEXEC */
+
+#endif /* _ASM_M68K_KEXEC_H */
diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
index 655347d..7ee5f00 100644
--- a/arch/m68k/kernel/Makefile
+++ b/arch/m68k/kernel/Makefile
@@ -22,3 +22,5 @@ obj-$(CONFIG_PCI) += pcibios.o

obj-$(CONFIG_HAS_DMA) += dma.o

+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+
diff --git a/arch/m68k/kernel/machine_kexec.c b/arch/m68k/kernel/machine_kexec.c
new file mode 100644
index 0000000..c775da3
--- /dev/null
+++ b/arch/m68k/kernel/machine_kexec.c
@@ -0,0 +1,71 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ */
+#include <linux/compiler.h>
+#include <linux/kexec.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/cacheflush.h>
+#include <asm/page.h>
+
+extern const unsigned char relocate_new_kernel[];
+extern const size_t relocate_new_kernel_size;
+
+int machine_kexec_prepare(struct kimage *kimage)
+{
+ return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *kimage)
+{
+}
+
+void machine_shutdown(void)
+{
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+typedef void (*relocate_kernel_t)(unsigned long ptr,
+ unsigned long start) __noreturn;
+
+void machine_kexec(struct kimage *image)
+{
+ void *reboot_code_buffer;
+ unsigned long kexec_indirection_page;
+
+ unsigned long entry;
+ unsigned long *ptr;
+
+ reboot_code_buffer = page_address(image->control_code_page);
+
+ memcpy(reboot_code_buffer, relocate_new_kernel,
+ relocate_new_kernel_size);
+
+ /*
+ * The generic kexec code builds a page list with physical
+ * addresses, while we need virtual addresses
+ */
+ for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE);
+ ptr = (entry & IND_INDIRECTION) ?
+ phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
+ if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION ||
+ *ptr & IND_DESTINATION)
+ *ptr = (unsigned long) phys_to_virt(*ptr);
+ }
+
+ kexec_indirection_page = image->head & PAGE_MASK;
+
+ /*
+ * we do not want to be bothered.
+ */
+ local_irq_disable();
+
+ pr_info("Will call new kernel at 0x%08lx. Bye...\n", image->start);
+ __flush_cache_all();
+ ((relocate_kernel_t) reboot_code_buffer)(kexec_indirection_page,
+ image->start);
+}
diff --git a/arch/m68k/kernel/relocate_kernel.S b/arch/m68k/kernel/relocate_kernel.S
new file mode 100644
index 0000000..8d45d9c
--- /dev/null
+++ b/arch/m68k/kernel/relocate_kernel.S
@@ -0,0 +1,79 @@
+#include <linux/linkage.h>
+
+#include <asm/page.h>
+
+
+.globl relocate_new_kernel
+
+.text
+ENTRY(relocate_new_kernel)
+ moveq #0,%d0
+ .chip 68040 /* FIXME */
+ /* Disable MMU */
+ /* FIXME Keep caches enabled? */
+ movec %d0,%tc
+ movec %d0,%itt0
+ movec %d0,%itt1
+ movec %d0,%dtt0
+ movec %d0,%dtt1
+ .chip 68k
+
+ movel 4(%sp),%a0 /* a0 = ptr */
+ movel 8(%sp),%a1 /* a1 = start */
+ movew #PAGE_MASK,%d2 /* d2 = PAGE_MASK */
+
+1:
+ movel (%a0)+,%d0 /* d0 = entry = *ptr */
+ jeq 5f
+
+ btst #2,%d0 /* entry & IND_DONE? */
+ jne 5f
+
+ btst #1,%d0 /* entry & IND_INDIRECTION? */
+ jeq 2f
+ andw %d2,%d0
+ movel %d0,%a0 /* ptr = entry & PAGE_MASK */
+ bra 1b
+
+2:
+ btst #0,%d0 /* entry & IND_DESTINATION? */
+ jeq 3f
+ andw %d2,%d0
+ movel %d0,%a2 /* a2 = dst = entry & PAGE_MASK */
+ bra 1b
+
+3:
+ btst #3,%d0 /* entry & IND_SOURCE? */
+ jeq 1b
+
+ andw %d2,%d0
+ movel %d0,%a3 /* a3 = src = entry & PAGE_MASK */
+ movew #PAGE_SIZE/32 - 1,%d0 /* d0 = PAGE_SIZE/32 - 1 */
+4:
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ movel (%a3)+,(%a2)+ /* *dst++ = *src++ */
+ dbf %d0, 4b
+ bra 1b
+
+5:
+ .chip 68040 /* FIXME */
+ /* Flush all caches */
+ nop
+ cpusha %bc
+ nop
+ cinva %bc
+ nop
+ .chip 68k
+
+ jmp (%a1)
+
+relocate_new_kernel_end:
+
+ENTRY(relocate_new_kernel_size)
+ .long relocate_new_kernel_end - relocate_new_kernel
diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 104838f..d6629d4 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -18,6 +18,7 @@
*/
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
#define KEXEC_ARCH_386 ( 3 << 16)
+#define KEXEC_ARCH_68K ( 4 << 16)
#define KEXEC_ARCH_X86_64 (62 << 16)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)
--
1.7.9.5

2013-09-17 10:02:24

by Geert Uytterhoeven

[permalink] [raw]
Subject: [PATCH 3/3] [RFC] m68k: Add System RAM to /proc/iomem

We've resisted adding System RAM to /proc/iomem because it is the wrong
place for it. Unfortunately we continue to find tools that rely on this
behaviour (e.g. kdump) so give up and add it in.

As the ST-RAM Pool is now a child of the first System RAM resource, it must
be switched from request_resource() to insert_resource().

Cfr. commit c40dd2f76644016ca7677545fc846ec2470d70a1 ("powerpc: Add System
RAM to /proc/iomem").

Signed-off-by: Geert Uytterhoeven <[email protected]>
---
arch/m68k/atari/stram.c | 2 +-
arch/m68k/kernel/setup_mm.c | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c
index 0810c8d..74eaed4 100644
--- a/arch/m68k/atari/stram.c
+++ b/arch/m68k/atari/stram.c
@@ -102,7 +102,7 @@ void __init atari_stram_reserve_pages(void *start_mem)

stram_pool.start = (resource_size_t)alloc_bootmem_low_pages(pool_size);
stram_pool.end = stram_pool.start + pool_size - 1;
- request_resource(&iomem_resource, &stram_pool);
+ insert_resource(&iomem_resource, &stram_pool);

pr_debug("atari_stram pool: size = %lu bytes, resource = %pR\n",
pool_size, &stram_pool);
diff --git a/arch/m68k/kernel/setup_mm.c b/arch/m68k/kernel/setup_mm.c
index cb7b0c9..55d718a 100644
--- a/arch/m68k/kernel/setup_mm.c
+++ b/arch/m68k/kernel/setup_mm.c
@@ -215,6 +215,27 @@ static void __init m68k_parse_bootinfo(const struct bi_record *record)
#endif
}

+/*
+ * System memory should not be in /proc/iomem but various tools expect it
+ * (e.g. kdump).
+ */
+static int __init add_system_ram_resources(void)
+{
+ int i;
+
+ for (i = 0; i < m68k_num_memory; i++) {
+ struct resource *res;
+ res = alloc_bootmem(sizeof(struct resource));
+ res->name = "System RAM";
+ res->start = m68k_memory[i].addr;
+ res->end = m68k_memory[i].addr + m68k_memory[i].size - 1;
+ res->flags = IORESOURCE_MEM;
+ WARN_ON(request_resource(&iomem_resource, res) < 0);
+ }
+
+ return 0;
+}
+
void __init setup_arch(char **cmdline_p)
{
#ifndef CONFIG_SUN3
@@ -344,6 +365,8 @@ void __init setup_arch(char **cmdline_p)

paging_init();

+ add_system_ram_resources();
+
#ifndef CONFIG_SUN3
for (i = 1; i < m68k_num_memory; i++)
free_bootmem_node(NODE_DATA(i), m68k_memory[i].addr,
--
1.7.9.5

2013-09-17 10:02:22

by Geert Uytterhoeven

[permalink] [raw]
Subject: [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes read

Add an optional output parameter to slurp_file_len() so it can return the
actual number of bytes read.

Signed-off-by: Geert Uytterhoeven <[email protected]>
---
kexec/arch/i386/crashdump-x86.c | 5 ++---
kexec/kexec.c | 4 +++-
kexec/kexec.h | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c
index e44fceb..e2da50b 100644
--- a/kexec/arch/i386/crashdump-x86.c
+++ b/kexec/arch/i386/crashdump-x86.c
@@ -121,7 +121,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
struct mem_ehdr ehdr;
struct mem_phdr *phdr, *end_phdr;
int align;
- unsigned long size;
+ off_t size;
uint32_t elf_flags = 0;

if (elf_info->machine != EM_X86_64)
@@ -131,8 +131,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
return 0;

align = getpagesize();
- size = KCORE_ELF_HEADERS_SIZE;
- buf = slurp_file_len(kcore, size);
+ buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size);
if (!buf) {
fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno));
return -1;
diff --git a/kexec/kexec.c b/kexec/kexec.c
index b863d2a..2b98ef0 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -537,7 +537,7 @@ char *slurp_file(const char *filename, off_t *r_size)
/* This functions reads either specified number of bytes from the file or
lesser if EOF is met. */

-char *slurp_file_len(const char *filename, off_t size)
+char *slurp_file_len(const char *filename, off_t size, off_t *nread)
{
int fd;
char *buf;
@@ -575,6 +575,8 @@ char *slurp_file_len(const char *filename, off_t size)
die("Close of %s failed: %s\n",
filename, strerror(errno));
}
+ if (nread)
+ *nread = progress;
return buf;
}

diff --git a/kexec/kexec.h b/kexec/kexec.h
index 715b568..5844b42 100644
--- a/kexec/kexec.h
+++ b/kexec/kexec.h
@@ -237,7 +237,7 @@ extern void die(const char *fmt, ...)
extern void *xmalloc(size_t size);
extern void *xrealloc(void *ptr, size_t size);
extern char *slurp_file(const char *filename, off_t *r_size);
-extern char *slurp_file_len(const char *filename, off_t size);
+extern char *slurp_file_len(const char *filename, off_t size, off_t *nread);
extern char *slurp_decompress_file(const char *filename, off_t *r_size);
extern unsigned long virt_to_phys(unsigned long addr);
extern void add_segment(struct kexec_info *info,
--
1.7.9.5

2013-09-17 10:02:54

by Geert Uytterhoeven

[permalink] [raw]
Subject: [PATCH 2/2] kexec: Add preliminary m68k support

Signed-off-by: Geert Uytterhoeven <[email protected]>
---
configure.ac | 2 +-
kexec/Makefile | 1 +
kexec/arch/m68k/Makefile | 15 +++
kexec/arch/m68k/bootinfo.c | 208 ++++++++++++++++++++++++++++++++
kexec/arch/m68k/bootinfo.h | 29 +++++
kexec/arch/m68k/include/arch/options.h | 45 +++++++
kexec/arch/m68k/kexec-elf-m68k.c | 171 ++++++++++++++++++++++++++
kexec/arch/m68k/kexec-elf-rel-m68k.c | 41 +++++++
kexec/arch/m68k/kexec-m68k.c | 113 +++++++++++++++++
kexec/arch/m68k/kexec-m68k.h | 9 ++
kexec/kexec-syscall.h | 7 ++
11 files changed, 640 insertions(+), 1 deletion(-)
create mode 100644 kexec/arch/m68k/Makefile
create mode 100644 kexec/arch/m68k/bootinfo.c
create mode 100644 kexec/arch/m68k/bootinfo.h
create mode 100644 kexec/arch/m68k/include/arch/options.h
create mode 100644 kexec/arch/m68k/kexec-elf-m68k.c
create mode 100644 kexec/arch/m68k/kexec-elf-rel-m68k.c
create mode 100644 kexec/arch/m68k/kexec-m68k.c
create mode 100644 kexec/arch/m68k/kexec-m68k.h

diff --git a/configure.ac b/configure.ac
index 7b61dbf..704d4f9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,7 +45,7 @@ case $target_cpu in
cris|crisv32 )
ARCH="cris"
;;
- ia64|x86_64|alpha )
+ ia64|x86_64|alpha|m68k )
ARCH="$target_cpu"
;;
* )
diff --git a/kexec/Makefile b/kexec/Makefile
index 8a6138d..ceb33af 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -72,6 +72,7 @@ include $(srcdir)/kexec/arch/alpha/Makefile
include $(srcdir)/kexec/arch/arm/Makefile
include $(srcdir)/kexec/arch/i386/Makefile
include $(srcdir)/kexec/arch/ia64/Makefile
+include $(srcdir)/kexec/arch/m68k/Makefile
include $(srcdir)/kexec/arch/mips/Makefile
include $(srcdir)/kexec/arch/cris/Makefile
include $(srcdir)/kexec/arch/ppc/Makefile
diff --git a/kexec/arch/m68k/Makefile b/kexec/arch/m68k/Makefile
new file mode 100644
index 0000000..4645894
--- /dev/null
+++ b/kexec/arch/m68k/Makefile
@@ -0,0 +1,15 @@
+#
+# kexec m68k (linux booting linux)
+#
+m68k_KEXEC_SRCS = kexec/arch/m68k/kexec-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-rel-m68k.c
+m68k_KEXEC_SRCS += kexec/arch/m68k/bootinfo.c
+
+m68k_ADD_BUFFER =
+m68k_ADD_SEGMENT =
+m68k_VIRT_TO_PHYS =
+
+dist += kexec/arch/m68k/Makefile $(m68k_KEXEC_SRCS) \
+ kexec/arch/m68k/kexec-m68k.h \
+ kexec/arch/m68k/include/arch/options.h
diff --git a/kexec/arch/m68k/bootinfo.c b/kexec/arch/m68k/bootinfo.c
new file mode 100644
index 0000000..328a838
--- /dev/null
+++ b/kexec/arch/m68k/bootinfo.c
@@ -0,0 +1,208 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../kexec.h"
+
+#include "bootinfo.h"
+
+const char *bootinfo_file = DEFAULT_BOOTINFO_FILE;
+static struct bi_records *bootinfo;
+static off_t bootinfo_size;
+
+static unsigned int num_memchunks;
+
+static struct bi_records *bi_next(struct bi_records *bi, uint16_t size)
+{
+ return (void *)((unsigned long)bi + size);
+}
+
+static void bi_remove(uint16_t tag)
+{
+ struct bi_records *bi;
+ off_t rem;
+ uint16_t size;
+
+ bi = bootinfo;
+ rem = bootinfo_size;
+ while (1) {
+ if (bi->tag == BI_LAST)
+ break;
+
+ size = bi->size;
+ if (bi->tag == tag) {
+ memmove(bi, bi_next(bi, size), rem - size);
+ bootinfo_size -= size;
+ rem -= size;
+ continue;
+ }
+
+ bi = bi_next(bi, size);
+ rem -= size;
+ }
+}
+
+static struct bi_records *bi_add(uint16_t tag, uint16_t size)
+{
+ struct bi_records *bi;
+
+ /* Add 4-byte header and round up to multiple of 4 bytes */
+ size = _ALIGN_UP(4 + size, 4);
+
+ bootinfo = xrealloc(bootinfo, bootinfo_size + size);
+
+ /* Replace old sentinel by new record */
+ bi = bi_next(bootinfo, bootinfo_size - 2);
+ bootinfo_size += size;
+ memset(bi, 0, size);
+ bi->tag = tag;
+ bi->size = size;
+
+ /* Re-add sentinel */
+ bi_next(bi, size)->tag = BI_LAST;
+
+ return bi;
+}
+
+void bootinfo_load(void)
+{
+ struct bi_records *bi;
+ off_t rem;
+ uint16_t tag, size;
+
+ printf("Loading bootinfo from %s\n", bootinfo_file);
+ bootinfo = (void *)slurp_file_len(bootinfo_file, MAX_BOOTINFO_SIZE,
+ &bootinfo_size);
+ if (!bootinfo)
+ die("No bootinfo");
+
+ bi = bootinfo;
+ rem = bootinfo_size;
+ while (1) {
+ if (rem < 2)
+ die("Unexpected end of bootinfo");
+
+ tag = bi->tag;
+ if (tag == BI_LAST) {
+ rem -= 2;
+ break;
+ }
+
+ if (rem < 4)
+ die("Unexpected end of bootinfo");
+
+ size = bi->size;
+ if (size < 4 || size % 4)
+ die("Invalid tag size");
+ if (rem < size)
+ die("Unexpected end of bootinfo");
+
+ if (tag == BI_MEMCHUNK)
+ num_memchunks++;
+
+ bi = bi_next(bi, size);
+ rem -= size;
+ }
+
+ if (rem)
+ die("Trailing data at end of bootinfo");
+}
+
+void bootinfo_print(void)
+{
+ struct bi_records *bi = bootinfo;
+ uint16_t tag, size;
+
+ while (1) {
+ tag = bi->tag;
+ if (tag == BI_LAST) {
+ puts("BI_LAST");
+ break;
+ }
+
+ size = bi->size;
+ switch (tag) {
+ case BI_MEMCHUNK:
+ printf("BI_MEMCHUNK: 0x%08lx bytes at 0x%08lx\n",
+ bi->mem_info.size, bi->mem_info.addr);
+ break;
+
+ case BI_RAMDISK:
+ printf("BI_RAMDISK: 0x%08lx bytes at 0x%08lx\n",
+ bi->mem_info.size, bi->mem_info.addr);
+ break;
+
+ case BI_COMMAND_LINE:
+ printf("BI_COMMAND_LINE: %s\n", bi->string);
+ break;
+
+ default:
+ printf("BI tag 0x%04x size %u\n", tag, size);
+ break;
+ }
+ bi = bi_next(bi, size);
+ }
+}
+
+int bootinfo_get_memory_ranges(struct memory_range **range)
+{
+ struct memory_range *ranges;
+ struct bi_records *bi;
+ int i;
+
+ ranges = xmalloc(num_memchunks * sizeof(struct memory_range));
+ for (bi = bootinfo, i = 0; i < num_memchunks;
+ bi = bi_next(bi, bi->size)) {
+ if (bi->tag != BI_MEMCHUNK)
+ continue;
+
+ ranges[i].start = bi->mem_info.addr;
+ ranges[i].end = bi->mem_info.addr + bi->mem_info.size - 1;
+ ranges[i].type = RANGE_RAM;
+ i++;
+ }
+
+ *range = ranges;
+ return num_memchunks;
+}
+
+void bootinfo_set_cmdline(const char *cmdline)
+{
+ struct bi_records *bi;
+ uint16_t size;
+
+ /* Remove existing command line records */
+ bi_remove(BI_COMMAND_LINE);
+
+ if (!cmdline)
+ return;
+
+ /* Add new command line record */
+ size = strlen(cmdline) + 1;
+ bi = bi_add(BI_COMMAND_LINE, size);
+ memcpy(bi->string, cmdline, size);
+}
+
+void bootinfo_set_ramdisk(unsigned long ramdisk_addr,
+ unsigned long ramdisk_size)
+{
+ struct bi_records *bi;
+
+ /* Remove existing ramdisk records */
+ bi_remove(BI_RAMDISK);
+
+ if (!ramdisk_size)
+ return;
+
+ /* Add new ramdisk record */
+ bi = bi_add(BI_RAMDISK, sizeof(bi->mem_info));
+ bi->mem_info.addr = ramdisk_addr;
+ bi->mem_info.size = ramdisk_size;
+}
+
+void add_bootinfo(struct kexec_info *info, unsigned long addr)
+{
+ add_buffer(info, bootinfo, bootinfo_size, bootinfo_size,
+ sizeof(void *), addr, 0x0fffffff, 1);
+}
diff --git a/kexec/arch/m68k/bootinfo.h b/kexec/arch/m68k/bootinfo.h
new file mode 100644
index 0000000..90e2576
--- /dev/null
+++ b/kexec/arch/m68k/bootinfo.h
@@ -0,0 +1,29 @@
+#define DEFAULT_BOOTINFO_FILE "/proc/bootinfo"
+#define MAX_BOOTINFO_SIZE 1536
+
+struct bi_records {
+ unsigned short tag;
+ unsigned short size;
+ union {
+ unsigned long generic[0];
+ struct {
+ unsigned long addr;
+ unsigned long size;
+ } mem_info;
+ char string[0];
+ };
+};
+
+#define BI_LAST 0x0000 // FIXME To be obtained from <asm/bootinfo.h>
+#define BI_MEMCHUNK 0x0005 // FIXME
+#define BI_RAMDISK 0x0006 // FIXME
+#define BI_COMMAND_LINE 0x0007 // FIXME
+
+extern const char *bootinfo_file;
+extern void bootinfo_load(void);
+extern void bootinfo_print(void);
+extern int bootinfo_get_memory_ranges(struct memory_range **range);
+extern void bootinfo_set_cmdline(const char *cmdline);
+extern void bootinfo_set_ramdisk(unsigned long ramdisk_addr,
+ unsigned long ramdisk_size);
+extern void add_bootinfo(struct kexec_info *info, unsigned long addr);
diff --git a/kexec/arch/m68k/include/arch/options.h b/kexec/arch/m68k/include/arch/options.h
new file mode 100644
index 0000000..f279d54
--- /dev/null
+++ b/kexec/arch/m68k/include/arch/options.h
@@ -0,0 +1,45 @@
+#ifndef KEXEC_ARCH_M68K_OPTIONS_H
+#define KEXEC_ARCH_M68K_OPTIONS_H
+
+#define OPT_ARCH_MAX (OPT_MAX+0)
+
+/* All 'local' loader options: */
+#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1)
+#define OPT_RAMDISK (OPT_ARCH_MAX+2)
+#define OPT_BOOTINFO (OPT_ARCH_MAX+3)
+
+/* Options relevant to the architecture (excluding loader-specific ones),
+ * in this case none:
+ */
+#define KEXEC_ARCH_OPTIONS \
+ KEXEC_OPTIONS \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
+
+/* The following two #defines list ALL of the options added by all of the
+ * architecture's loaders.
+ * o main() uses this complete list to scan for its options, ignoring
+ * arch-specific/loader-specific ones.
+ * o Then, arch_process_options() uses this complete list to scan for its
+ * options, ignoring general/loader-specific ones.
+ * o Then, the file_type[n].load re-scans for options, using
+ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset.
+ * Any unrecognised options cause an error here.
+ *
+ * This is done so that main()'s/arch_process_options()'s getopt_long() calls
+ * don't choose a kernel filename from random arguments to options they don't
+ * recognise -- as they now recognise (if not act upon) all possible options.
+ */
+#define KEXEC_ALL_OPTIONS \
+ KEXEC_ARCH_OPTIONS \
+ { "command-line", 1, NULL, OPT_APPEND }, \
+ { "append", 1, NULL, OPT_APPEND }, \
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+ { "ramdisk", 1, NULL, OPT_RAMDISK }, \
+ { "initrd", 1, NULL, OPT_RAMDISK }, \
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+#endif /* KEXEC_ARCH_M68K_OPTIONS_H */
diff --git a/kexec/arch/m68k/kexec-elf-m68k.c b/kexec/arch/m68k/kexec-elf-m68k.c
new file mode 100644
index 0000000..06d329b
--- /dev/null
+++ b/kexec/arch/m68k/kexec-elf-m68k.c
@@ -0,0 +1,171 @@
+/*
+ * kexec-elf-m68k.c - kexec Elf loader for m68k
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-m68k.h"
+#include "bootinfo.h"
+#include <arch/options.h>
+
+int elf_m68k_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_68K) {
+ /* for a different architecture */
+ fprintf(stderr, "Not for this architecture.\n");
+ result = -1;
+ goto out;
+ }
+ result = 0;
+ out:
+ free_elf_info(&ehdr);
+ return result;
+}
+
+void elf_m68k_usage(void)
+{
+ printf( " --command-line=STRING Set the kernel command line to STRING\n"
+ " --append=STRING Set the kernel command line to STRING\n"
+ " --reuse-cmdline Use kernel command line from running system.\n"
+ " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
+ " --bootinfo=FILE Use FILE as the kernel's bootinfo\n"
+ );
+}
+
+int elf_m68k_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ const char *cmdline = NULL, *ramdisk_file = NULL;
+ int opt;
+ int result;
+ unsigned long ramdisk_addr = 0;
+ off_t ramdisk_size = 0;
+ size_t i;
+
+ /* See options.h if adding any more options. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case '?':
+ usage();
+ return -1;
+ case OPT_APPEND:
+ cmdline = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ cmdline = get_command_line();
+ break;
+ case OPT_RAMDISK:
+ ramdisk_file = optarg;
+ break;
+ case OPT_BOOTINFO:
+ break;
+ }
+ }
+
+ 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)
+ 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");
+
+ info->entry = (void *)virt_to_phys(ehdr.e_entry);
+
+ if (info->segment[0].mem == NULL) {
+ // FIXME Do not touch page zero
+ printf("Removing page zero\n");
+ info->segment[0].buf =
+ (void *)((unsigned long)info->segment[0].buf + 4096);
+ info->segment[0].bufsz -= 4096;
+ info->segment[0].mem =
+ (void *)((unsigned long)info->segment[0].mem + 4096);
+ info->segment[0].memsz -= 4096;
+ }
+
+ // FIXME check struct bootversion at start of kernel
+
+ bootinfo_set_cmdline(cmdline);
+
+ /* Load ramdisk */
+ if (ramdisk_file) {
+ void *ramdisk = slurp_decompress_file(ramdisk_file,
+ &ramdisk_size);
+ /* Store ramdisk at top of first memory chunk */
+ /* FIXME Without the -4096, locate_hole() fails with
+ * Could not find a free area of memory of 0x15e000 bytes...
+ */
+ ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end -
+ ramdisk_size + 1 - 4096 /* FIXME */,
+ 4096);
+ if (!buf)
+ die("Ramdisk load failed\n");
+ add_buffer(info, ramdisk, ramdisk_size, ramdisk_size, 4096,
+ ramdisk_addr, info->memory_range[0].end, 1);
+ }
+ bootinfo_set_ramdisk(ramdisk_addr, ramdisk_size);
+
+ bootinfo_print(); // FIXME
+
+ /* Bootinfo must be stored right after the kernel */
+ add_bootinfo(info, 0);
+
+ return 0;
+}
diff --git a/kexec/arch/m68k/kexec-elf-rel-m68k.c b/kexec/arch/m68k/kexec-elf-rel-m68k.c
new file mode 100644
index 0000000..3bad106
--- /dev/null
+++ b/kexec/arch/m68k/kexec-elf-rel-m68k.c
@@ -0,0 +1,41 @@
+/*
+ * kexec-elf-rel-m68k.c - kexec Elf relocation routines
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+*/
+
+#include <stdio.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+ if (ehdr->ei_data != ELFDATA2MSB) {
+ return 0;
+ }
+ if (ehdr->ei_class != ELFCLASS32) {
+ return 0;
+ }
+ if (ehdr->e_machine != EM_68K) {
+ return 0;
+ }
+ return 1;
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), unsigned long r_type,
+ void *UNUSED(location),
+ unsigned long UNUSED(address),
+ unsigned long UNUSED(value))
+{
+ switch(r_type) {
+
+ default:
+ die("Unknown rela relocation: %lu\n", r_type);
+ break;
+ }
+ return;
+}
diff --git a/kexec/arch/m68k/kexec-m68k.c b/kexec/arch/m68k/kexec-m68k.c
new file mode 100644
index 0000000..d53a839
--- /dev/null
+++ b/kexec/arch/m68k/kexec-m68k.c
@@ -0,0 +1,113 @@
+/*
+ * kexec-m68k.c - kexec for m68k
+ *
+ * Copyright (C) 2013 Geert Uytterhoeven
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-m68k.h"
+#include "bootinfo.h"
+#include <arch/options.h>
+
+
+/* Return a sorted list of memory ranges. */
+int get_memory_ranges(struct memory_range **range, int *ranges,
+ unsigned long kexec_flags)
+{
+ bootinfo_load();
+ *ranges = bootinfo_get_memory_ranges(range);
+ return 0;
+}
+
+
+struct file_type file_type[] = {
+ {"elf-m68k", elf_m68k_probe, elf_m68k_load, elf_m68k_usage},
+};
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+void arch_usage(void)
+{
+}
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ALL_OPTIONS
+ { "bootinfo", 1, NULL, OPT_BOOTINFO },
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ALL_OPT_STR;
+ int opt;
+ unsigned long value;
+ char *end;
+
+ opterr = 0; /* Don't complain about unrecognized options here */
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+ switch(opt) {
+ default:
+ break;
+ case OPT_BOOTINFO:
+ bootinfo_file = optarg;
+ break;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+const struct arch_map_entry arches[] = {
+ { "m68k", KEXEC_ARCH_68K },
+ { NULL, 0 },
+};
+
+int arch_compat_trampoline(struct kexec_info *UNUSED(info))
+{
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ return 0;
+}
+
+unsigned long virt_to_phys(unsigned long addr)
+{
+ return addr & 0x7fffffff; // FIXME
+}
+
+/*
+ * add_segment() should convert base to a physical address on m68k,
+ * while the default is just to work with base as is */
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz)
+{
+ add_segment_phys_virt(info, buf, bufsz, virt_to_phys(base), memsz, 1);
+}
+
+/*
+ * add_buffer() should convert base to a physical address on m68k,
+ * while the default is just to work with base as is */
+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)
+{
+ return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align,
+ buf_min, buf_max, buf_end, 1);
+}
diff --git a/kexec/arch/m68k/kexec-m68k.h b/kexec/arch/m68k/kexec-m68k.h
new file mode 100644
index 0000000..99482c4
--- /dev/null
+++ b/kexec/arch/m68k/kexec-m68k.h
@@ -0,0 +1,9 @@
+#ifndef KEXEC_M68K_H
+#define KEXEC_M68K_H
+
+int elf_m68k_probe(const char *buf, off_t len);
+int elf_m68k_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void elf_m68k_usage(void);
+
+#endif /* KEXEC_M68K_H */
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
index b56cb00..6238044 100644
--- a/kexec/kexec-syscall.h
+++ b/kexec/kexec-syscall.h
@@ -45,6 +45,9 @@
#if defined(__mips__)
#define __NR_kexec_load 4311
#endif
+#ifdef __m68k__
+#define __NR_kexec_load 313
+#endif
#ifndef __NR_kexec_load
#error Unknown processor architecture. Needs a kexec_load syscall number.
#endif
@@ -67,6 +70,7 @@ static inline long kexec_load(void *entry, unsigned long nr_segments,
*/
#define KEXEC_ARCH_DEFAULT ( 0 << 16)
#define KEXEC_ARCH_386 ( 3 << 16)
+#define KEXEC_ARCH_68K ( 4 << 16)
#define KEXEC_ARCH_X86_64 (62 << 16)
#define KEXEC_ARCH_PPC (20 << 16)
#define KEXEC_ARCH_PPC64 (21 << 16)
@@ -114,5 +118,8 @@ static inline long kexec_load(void *entry, unsigned long nr_segments,
#if defined(__mips__)
#define KEXEC_ARCH_NATIVE KEXEC_ARCH_MIPS
#endif
+#ifdef __m68k__
+#define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K
+#endif

#endif /* KEXEC_SYSCALL_H */
--
1.7.9.5

2013-09-17 10:03:13

by Geert Uytterhoeven

[permalink] [raw]
Subject: [PATCH 2/3] m68k: Add support to export bootinfo in procfs

Add optional support to export the bootinfo used to boot the kernel in a
"bootinfo" file in procfs. This is useful with kexec.

This is based on the similar feature for ATAGS on ARM.

Signed-off-by: Geert Uytterhoeven <[email protected]>
---
arch/m68k/Kconfig | 7 ++++
arch/m68k/include/asm/bootinfo.h | 6 +++
arch/m68k/kernel/Makefile | 1 +
arch/m68k/kernel/bootinfo_proc.c | 78 ++++++++++++++++++++++++++++++++++++++
arch/m68k/kernel/setup_mm.c | 2 +
5 files changed, 94 insertions(+)
create mode 100644 arch/m68k/kernel/bootinfo_proc.c

diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index d60497f..edd40b0 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -103,6 +103,13 @@ config KEXEC
interface is strongly in flux, so no good recommendation can be
made.

+config BOOTINFO_PROC
+ bool "Export bootinfo in procfs"
+ depends on KEXEC && M68KCLASSIC
+ help
+ Say Y to export the bootinfo used to boot the kernel in a
+ "bootinfo" file in procfs. This is useful with kexec.
+
menu "Platform setup"

source arch/m68k/Kconfig.cpu
diff --git a/arch/m68k/include/asm/bootinfo.h b/arch/m68k/include/asm/bootinfo.h
index fdc6266..65f5cc6 100644
--- a/arch/m68k/include/asm/bootinfo.h
+++ b/arch/m68k/include/asm/bootinfo.h
@@ -47,6 +47,12 @@ struct bi_record {
unsigned long data[0]; /* data */
};

+#ifdef CONFIG_BOOTINFO_PROC
+extern void save_bootinfo(const struct bi_record *bi);
+#else
+static inline void save_bootinfo(const struct bi_record *bi) {}
+#endif
+
#endif /* __ASSEMBLY__ */


diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
index 7ee5f00..2d5d9be 100644
--- a/arch/m68k/kernel/Makefile
+++ b/arch/m68k/kernel/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_PCI) += pcibios.o
obj-$(CONFIG_HAS_DMA) += dma.o

obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_BOOTINFO_PROC) += bootinfo_proc.o

diff --git a/arch/m68k/kernel/bootinfo_proc.c b/arch/m68k/kernel/bootinfo_proc.c
new file mode 100644
index 0000000..a22eac1
--- /dev/null
+++ b/arch/m68k/kernel/bootinfo_proc.c
@@ -0,0 +1,78 @@
+/*
+ * Based on arch/arm/kernel/atags_proc.c
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/printk.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+
+
+static char bootinfo_tmp[1536] __initdata;
+
+static void *bootinfo_copy;
+static size_t bootinfo_size;
+
+static ssize_t bootinfo_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(buf, count, ppos, bootinfo_copy,
+ bootinfo_size);
+}
+
+static const struct file_operations bootinfo_fops = {
+ .read = bootinfo_read,
+ .llseek = default_llseek,
+};
+
+void __init save_bootinfo(const struct bi_record *bi)
+{
+ const void *start = bi;
+ size_t size = sizeof(bi->tag);
+
+ while (bi->tag != BI_LAST) {
+ size += bi->size;
+ bi = (struct bi_record *)((unsigned long)bi + bi->size);
+ }
+
+ if (size > sizeof(bootinfo_tmp)) {
+ pr_err("Cannot save %zu bytes of bootinfo\n", size);
+ return;
+ }
+
+ pr_info("Saving %zu bytes of bootinfo\n", size);
+ memcpy(bootinfo_tmp, start, size);
+ bootinfo_size = size;
+}
+
+static int __init init_bootinfo_procfs(void)
+{
+ /*
+ * This cannot go into save_bootinfo() because kmalloc and proc don't
+ * work yet when it is called.
+ */
+ struct proc_dir_entry *pde;
+
+ if (!bootinfo_size)
+ return -EINVAL;
+
+ bootinfo_copy = kmalloc(bootinfo_size, GFP_KERNEL);
+ if (!bootinfo_copy)
+ return -ENOMEM;
+
+ memcpy(bootinfo_copy, bootinfo_tmp, bootinfo_size);
+
+ pde = proc_create_data("bootinfo", 0400, NULL, &bootinfo_fops, NULL);
+ if (!pde) {
+ kfree(bootinfo_copy);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+arch_initcall(init_bootinfo_procfs);
diff --git a/arch/m68k/kernel/setup_mm.c b/arch/m68k/kernel/setup_mm.c
index 1e3e2cb..cb7b0c9 100644
--- a/arch/m68k/kernel/setup_mm.c
+++ b/arch/m68k/kernel/setup_mm.c
@@ -143,6 +143,8 @@ extern void paging_init(void);

static void __init m68k_parse_bootinfo(const struct bi_record *record)
{
+ save_bootinfo(record);
+
while (record->tag != BI_LAST) {
int unknown = 0;
const unsigned long *data = record->data;
--
1.7.9.5

2013-09-19 09:20:24

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: Preliminary kexec support for Linux/m68k

On Tue, Sep 17, 2013 at 12:01 PM, Geert Uytterhoeven
<[email protected]> wrote:
> This is a preliminary set of patches to add kexec support for m68k.

> - [PATCH 1/3] m68k: Add preliminary kexec support
> - [PATCH 2/3] m68k: Add support to export bootinfo in procfs
> - [PATCH 3/3] [RFC] m68k: Add System RAM to /proc/iomem
>
> Notes:
> - The bootinfo is now saved and exported to /proc/bootinfo, so kexec-tools
> can read it and pass it (possibly after modification) to the new kernel.
> This is similar to /proc/atags on ARM.

> - I based [PATCH 3/3] on the PowerPC version, but it's no longer needed as we
> now get this information from the bootinfo.
> Does anyone think this is nice to have anyway?

It seems kexec/kdump on ppc don't use /proc/iomem anymore, and only rely on
/proc/device-tree these days?

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2013-09-19 14:40:51

by Dave Young

[permalink] [raw]
Subject: Re: [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes read

On 09/17/13 at 12:01pm, Geert Uytterhoeven wrote:
> Add an optional output parameter to slurp_file_len() so it can return the
> actual number of bytes read.

Acked-by: Dave Young <[email protected]>

BTW, looking the slurp_file function, it would be better to call/reuse slurp_file_len
in slurp_file, care to improve this as well?

>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> ---
> kexec/arch/i386/crashdump-x86.c | 5 ++---
> kexec/kexec.c | 4 +++-
> kexec/kexec.h | 2 +-
> 3 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c
> index e44fceb..e2da50b 100644
> --- a/kexec/arch/i386/crashdump-x86.c
> +++ b/kexec/arch/i386/crashdump-x86.c
> @@ -121,7 +121,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
> struct mem_ehdr ehdr;
> struct mem_phdr *phdr, *end_phdr;
> int align;
> - unsigned long size;
> + off_t size;
> uint32_t elf_flags = 0;
>
> if (elf_info->machine != EM_X86_64)
> @@ -131,8 +131,7 @@ static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info),
> return 0;
>
> align = getpagesize();
> - size = KCORE_ELF_HEADERS_SIZE;
> - buf = slurp_file_len(kcore, size);
> + buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size);
> if (!buf) {
> fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno));
> return -1;
> diff --git a/kexec/kexec.c b/kexec/kexec.c
> index b863d2a..2b98ef0 100644
> --- a/kexec/kexec.c
> +++ b/kexec/kexec.c
> @@ -537,7 +537,7 @@ char *slurp_file(const char *filename, off_t *r_size)
> /* This functions reads either specified number of bytes from the file or
> lesser if EOF is met. */
>
> -char *slurp_file_len(const char *filename, off_t size)
> +char *slurp_file_len(const char *filename, off_t size, off_t *nread)
> {
> int fd;
> char *buf;
> @@ -575,6 +575,8 @@ char *slurp_file_len(const char *filename, off_t size)
> die("Close of %s failed: %s\n",
> filename, strerror(errno));
> }
> + if (nread)
> + *nread = progress;
> return buf;
> }
>
> diff --git a/kexec/kexec.h b/kexec/kexec.h
> index 715b568..5844b42 100644
> --- a/kexec/kexec.h
> +++ b/kexec/kexec.h
> @@ -237,7 +237,7 @@ extern void die(const char *fmt, ...)
> extern void *xmalloc(size_t size);
> extern void *xrealloc(void *ptr, size_t size);
> extern char *slurp_file(const char *filename, off_t *r_size);
> -extern char *slurp_file_len(const char *filename, off_t size);
> +extern char *slurp_file_len(const char *filename, off_t size, off_t *nread);
> extern char *slurp_decompress_file(const char *filename, off_t *r_size);
> extern unsigned long virt_to_phys(unsigned long addr);
> extern void add_segment(struct kexec_info *info,
> --
> 1.7.9.5
>
>
> _______________________________________________
> kexec mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/kexec

2013-09-19 23:52:18

by Simon Horman

[permalink] [raw]
Subject: Re: Preliminary kexec support for Linux/m68k

On Thu, Sep 19, 2013 at 11:20:22AM +0200, Geert Uytterhoeven wrote:
> On Tue, Sep 17, 2013 at 12:01 PM, Geert Uytterhoeven
> <[email protected]> wrote:
> > This is a preliminary set of patches to add kexec support for m68k.
>
> > - [PATCH 1/3] m68k: Add preliminary kexec support
> > - [PATCH 2/3] m68k: Add support to export bootinfo in procfs
> > - [PATCH 3/3] [RFC] m68k: Add System RAM to /proc/iomem
> >
> > Notes:
> > - The bootinfo is now saved and exported to /proc/bootinfo, so kexec-tools
> > can read it and pass it (possibly after modification) to the new kernel.
> > This is similar to /proc/atags on ARM.
>
> > - I based [PATCH 3/3] on the PowerPC version, but it's no longer needed as we
> > now get this information from the bootinfo.
> > Does anyone think this is nice to have anyway?
>
> It seems kexec/kdump on ppc don't use /proc/iomem anymore, and only rely on
> /proc/device-tree these days?

I think thats entirely a matter for the m68k kernel maintainers to decide.
But my personal opinion is that it is nice not to add it if it isn't needed.

2013-09-19 23:52:26

by Simon Horman

[permalink] [raw]
Subject: Re: Preliminary kexec support for Linux/m68k

On Tue, Sep 17, 2013 at 12:01:30PM +0200, Geert Uytterhoeven wrote:
> This is a preliminary set of patches to add kexec support for m68k.

Great, this has been a long time coming!

> - Kexec only, no kdump support yet (do you have enough RAM to keep a
> crashdump kernel in memory at all times? ;-)
>
> - Tested on ARAnyM only. No support for other CPU/MMUs than 68040.
>
> - Although the code contains some phys/virt conversions, it will probably
> fail miserably on platforms where kernel virtual addresses are different
> from physical address.
>
> - To have automatic "kexec -e" on reboot, copy /etc/rc6.d/S85kexec from
> another system, and fix it up for kexec living in /usr/local/sbin instead
> of /sbin.
>
> - Sample invocation:
>
> kexec -d -l vmlinux --reuse-cmdline
>
>
> KERNEL:
>
> Patches:
> - [PATCH 1/3] m68k: Add preliminary kexec support
> - [PATCH 2/3] m68k: Add support to export bootinfo in procfs
> - [PATCH 3/3] [RFC] m68k: Add System RAM to /proc/iomem
>
> Notes:
> - The bootinfo is now saved and exported to /proc/bootinfo, so kexec-tools
> can read it and pass it (possibly after modification) to the new kernel.
> This is similar to /proc/atags on ARM.
>
> - We should create arch/m68k/include/uapi/asm/bootinfo.h (and probably a few
> more, e.g. machine-specific model IDs), as this is needed by both m68kboot
> and kexec-tools.
>
> - I based [PATCH 3/3] on the PowerPC version, but it's no longer needed as we
> now get this information from the bootinfo.
> Does anyone think this is nice to have anyway?
>
>
> KEXEC-TOOLS:

Pasting two series in one was a bit confusing for me at first.
Perhaps you could consider posting two separate series in future.

> Patches:
> - [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes
> - [PATCH 2/2] kexec: Add preliminary m68k support
>
> Notes:
> - Based on git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git

A good choice.

> - Tagged bootinfo is read from /proc/bootinfo by default, but this can be
> overridden using --bootinfo. No bootinfo editor is provided.
> The kexec command will replace/delete command line and ramdisk tags in the
> bootinfo.

This sounds good to me.

> - The ramdisk is loaded at the top of memory minus 4096, unlike with
> m68boot (ataboot/amiboot), as locate_hole() seems to have a bug that it
> cannot reserve a block at the real top of memory.

Is this a bug that could be fixed?

> - The first unused page of the kernel image (at address zero) is
> automatically removed, just like m68kboot does.
> If I don't do this, relocate_new_kernel() fails with a "cannot handle
> kernel paging request at address NULL" exception, although the MMU is
> disabled at that point.
> As m68kboot does this too, I guess this is OK?

It sounds sane to me. But I would appreciate some feedback from
someone familiar with the m68k kernel.

> - Do we want to check the struct bootversion at the start of the kernel,
> like m68kboot does?
> Kexec may be used to load ELF files that are not Linux kernel images,
> and thus don't have a Linux-specific struct bootversion.

If the check can sanely be skipped for non Linux kernel images then
this sounds like a reasonable idea to me. Otherwise I would lean towards
omitting it. Either way, I don't feel strongly about this.

>
> - Do we want to check the size of the kernel image + bootinfo, and warn the
> user if it's larger than 4 MiB?
> This is a limitation of the current Linux/m68k kernel only.

I think that sounds like a good idea but I don't feel strongly about it.

>
> All comments are welcome!
> Have fun! ;-)
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
>
>
> _______________________________________________
> kexec mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/kexec
>

2013-09-20 07:15:04

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: Preliminary kexec support for Linux/m68k

Hi Simon,

Thanks for your comments!

On Thu, Sep 19, 2013 at 11:07 PM, Simon Horman <[email protected]> wrote:
> Pasting two series in one was a bit confusing for me at first.
> Perhaps you could consider posting two separate series in future.

Sorry, I wanted to have all information in one series for the first posting, to
avoid people having to look around too much if they want to give it a try.

I'll post seperate series in the future.

>> Patches:
>> - [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes
>> - [PATCH 2/2] kexec: Add preliminary m68k support
>>
>> Notes:
>> - Based on git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
>
> A good choice.

Is it normal I don't see much activity there?

>> - The ramdisk is loaded at the top of memory minus 4096, unlike with
>> m68boot (ataboot/amiboot), as locate_hole() seems to have a bug that it
>> cannot reserve a block at the real top of memory.
>
> Is this a bug that could be fixed?

Possibly. I suspect an off-by-one bug somewhere, but I haven't looked deeply
into it.

>> - Do we want to check the struct bootversion at the start of the kernel,
>> like m68kboot does?
>> Kexec may be used to load ELF files that are not Linux kernel images,
>> and thus don't have a Linux-specific struct bootversion.
>
> If the check can sanely be skipped for non Linux kernel images then
> this sounds like a reasonable idea to me. Otherwise I would lean towards
> omitting it. Either way, I don't feel strongly about this.
>
>> - Do we want to check the size of the kernel image + bootinfo, and warn the
>> user if it's larger than 4 MiB?
>> This is a limitation of the current Linux/m68k kernel only.
>
> I think that sounds like a good idea but I don't feel strongly about it.

Currently I'm leaning towards just printing a warning for both
(missing/incompatible
bootversion and image-too-large).

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2013-09-20 19:19:01

by Simon Horman

[permalink] [raw]
Subject: Re: Preliminary kexec support for Linux/m68k

On Fri, Sep 20, 2013 at 09:15:02AM +0200, Geert Uytterhoeven wrote:
> Hi Simon,
>
> Thanks for your comments!
>
> On Thu, Sep 19, 2013 at 11:07 PM, Simon Horman <[email protected]> wrote:
> > Pasting two series in one was a bit confusing for me at first.
> > Perhaps you could consider posting two separate series in future.
>
> Sorry, I wanted to have all information in one series for the first posting, to
> avoid people having to look around too much if they want to give it a try.
>
> I'll post seperate series in the future.

Thanks.

> >> Patches:
> >> - [PATCH 1/2] kexec: Let slurp_file_len() return the number of bytes
> >> - [PATCH 2/2] kexec: Add preliminary m68k support
> >>
> >> Notes:
> >> - Based on git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
> >
> > A good choice.
>
> Is it normal I don't see much activity there?

Yes, that is normal.

>
> >> - The ramdisk is loaded at the top of memory minus 4096, unlike with
> >> m68boot (ataboot/amiboot), as locate_hole() seems to have a bug that it
> >> cannot reserve a block at the real top of memory.
> >
> > Is this a bug that could be fixed?
>
> Possibly. I suspect an off-by-one bug somewhere, but I haven't looked deeply
> into it.

My feeling is that if possible it would be better to fix the bug than add a
work-around.

> >> - Do we want to check the struct bootversion at the start of the kernel,
> >> like m68kboot does?
> >> Kexec may be used to load ELF files that are not Linux kernel images,
> >> and thus don't have a Linux-specific struct bootversion.
> >
> > If the check can sanely be skipped for non Linux kernel images then
> > this sounds like a reasonable idea to me. Otherwise I would lean towards
> > omitting it. Either way, I don't feel strongly about this.
> >
> >> - Do we want to check the size of the kernel image + bootinfo, and warn the
> >> user if it's larger than 4 MiB?
> >> This is a limitation of the current Linux/m68k kernel only.
> >
> > I think that sounds like a good idea but I don't feel strongly about it.
>
> Currently I'm leaning towards just printing a warning for both
> (missing/incompatible
> bootversion and image-too-large).

That sounds fine to me.