generic: improve MIPS kexec support

It is based on patches from the linux-longsoon-community git tree:
http://dev.lemote.com/cgit/linux-loongson-community.git/

Now the kernel can use the command line parameter from kexec-tools.

Runtime tested on ar71xx with 2.6.39.4 (the wathdog must be stopped
before executing the new kernel). Compile tested with lantiq (3.1.4)
and brcm47xx (3.0.12).

SVN-Revision: 29674
lede-17.01
Gabor Juhos 2012-01-07 16:29:16 +00:00
parent f638c6207b
commit 6c74274c8d
19 changed files with 3553 additions and 1 deletions

View File

@ -40,12 +40,12 @@ CONFIG_AR71XX_MACH_RB4XX=y
CONFIG_AR71XX_MACH_RB750=y
CONFIG_AR71XX_MACH_RW2458N=y
CONFIG_AR71XX_MACH_TEW_632BRP=y
CONFIG_AR71XX_MACH_TL_MR3020=y
CONFIG_AR71XX_MACH_TL_MR3X20=y
CONFIG_AR71XX_MACH_TL_WA901ND=y
CONFIG_AR71XX_MACH_TL_WA901ND_V2=y
CONFIG_AR71XX_MACH_TL_WR1043ND=y
CONFIG_AR71XX_MACH_TL_WR703N=y
CONFIG_AR71XX_MACH_TL_MR3020=y
CONFIG_AR71XX_MACH_TL_WR741ND=y
CONFIG_AR71XX_MACH_TL_WR741ND_V4=y
CONFIG_AR71XX_MACH_TL_WR841N_V1=y

View File

@ -0,0 +1,616 @@
From eee16330c9de9adf7880cce9f1d32e13f89706bb Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:16:47 +0000
Subject: MIPS: Add crash and kdump support
From: http://patchwork.linux-mips.org/patch/1025/
Hello folks,
Please find here MIPS crash and kdump patches.
This is patch set of 3 patches:
1. generic MIPS changes (kernel);
2. MIPS Cavium Octeon board kexec/kdump code (kernel);
3. Kexec user space MIPS changes.
Patches were tested on the latest linux-mips@ git kernel and the latest
kexec-tools git on Cavium Octeon 50xx board.
I also made the same code working on RMI XLR/XLS boards for both
mips32 and mips64 kernels.
Best regards,
Maxim Uvarov.
------
[ Zhangjin: Several trivial building failure has been fixed.
Note: the 2nd patch can not be cleanly applied, but may be a good
reference for the other board development:
+ MIPS Cavium Octeon board kexec,kdump support
http://patchwork.linux-mips.org/patch/1026/
And the 3rd patch has already been merged into the mainline kexec-tools:
+ some kexec MIPS improvements
http://patchwork.linux-mips.org/patch/1027/
kexec-tools is available here:
+ http://horms.net/projects/kexec/
git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
]
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -92,7 +92,8 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
+obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o
obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
--- /dev/null
+++ b/arch/mips/kernel/crash.c
@@ -0,0 +1,75 @@
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/kexec.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#ifdef CONFIG_CRASH_DUMP
+unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX;
+#endif
+
+/* This keeps a track of which one is crashing cpu. */
+int crashing_cpu = -1;
+static cpumask_t cpus_in_crash = CPU_MASK_NONE;
+
+#ifdef CONFIG_SMP
+void crash_shutdown_secondary(void *ignore)
+{
+ struct pt_regs *regs;
+ int cpu = smp_processor_id();
+
+ regs = task_pt_regs(current);
+
+ if (!cpu_online(cpu))
+ return;
+
+ local_irq_disable();
+ if (!cpu_isset(cpu, cpus_in_crash))
+ crash_save_cpu(regs, cpu);
+ cpu_set(cpu, cpus_in_crash);
+
+ while (!atomic_read(&kexec_ready_to_reboot))
+ cpu_relax();
+ relocated_kexec_smp_wait(NULL);
+ /* NOTREACHED */
+}
+
+static void crash_kexec_prepare_cpus(void)
+{
+ unsigned int msecs;
+
+ unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
+
+ dump_send_ipi(crash_shutdown_secondary);
+ smp_wmb();
+
+ /*
+ * The crash CPU sends an IPI and wait for other CPUs to
+ * respond. Delay of at least 10 seconds.
+ */
+ printk(KERN_EMERG "Sending IPI to other cpus...\n");
+ msecs = 10000;
+ while ((cpus_weight(cpus_in_crash) < ncpus) && (--msecs > 0)) {
+ cpu_relax();
+ mdelay(1);
+ }
+}
+
+#else
+static void crash_kexec_prepare_cpus(void) {}
+#endif
+
+void default_machine_crash_shutdown(struct pt_regs *regs)
+{
+ local_irq_disable();
+ crashing_cpu = smp_processor_id();
+ crash_save_cpu(regs, crashing_cpu);
+ crash_kexec_prepare_cpus();
+ cpu_set(crashing_cpu, cpus_in_crash);
+}
--- /dev/null
+++ b/arch/mips/kernel/crash_dump.c
@@ -0,0 +1,86 @@
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PROC_VMCORE
+static int __init parse_elfcorehdr(char *p)
+{
+ if (p)
+ elfcorehdr_addr = memparse(p, &p);
+ return 1;
+}
+__setup("elfcorehdr=", parse_elfcorehdr);
+#endif
+
+static int __init parse_savemaxmem(char *p)
+{
+ if (p)
+ saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1;
+
+ return 1;
+}
+__setup("savemaxmem=", parse_savemaxmem);
+
+
+static void *kdump_buf_page;
+
+/**
+ * copy_oldmem_page - copy one page from "oldmem"
+ * @pfn: page frame number to be copied
+ * @buf: target memory address for the copy; this can be in kernel address
+ * space or user address space (see @userbuf)
+ * @csize: number of bytes to copy
+ * @offset: offset in bytes into the page (based on pfn) to begin the copy
+ * @userbuf: if set, @buf is in user address space, use copy_to_user(),
+ * otherwise @buf is in kernel address space, use memcpy().
+ *
+ * Copy a page from "oldmem". For this page, there is no pte mapped
+ * in the current kernel.
+ *
+ * Calling copy_to_user() in atomic context is not desirable. Hence first
+ * copying the data to a pre-allocated kernel page and then copying to user
+ * space in non-atomic context.
+ */
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+ size_t csize, unsigned long offset, int userbuf)
+{
+ void *vaddr;
+
+ if (!csize)
+ return 0;
+
+ vaddr = kmap_atomic_pfn(pfn, KM_PTE0);
+
+ if (!userbuf) {
+ memcpy(buf, (vaddr + offset), csize);
+ kunmap_atomic(vaddr, KM_PTE0);
+ } else {
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Kdump buffer page not"
+ " allocated\n");
+ return -EFAULT;
+ }
+ copy_page(kdump_buf_page, vaddr);
+ kunmap_atomic(vaddr, KM_PTE0);
+ if (copy_to_user(buf, (kdump_buf_page + offset), csize))
+ return -EFAULT;
+ }
+
+ return csize;
+}
+
+static int __init kdump_buf_page_init(void)
+{
+ int ret = 0;
+
+ kdump_buf_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Failed to allocate kdump buffer"
+ " page\n");
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+arch_initcall(kdump_buf_page_init);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -19,9 +19,19 @@ extern const size_t relocate_new_kernel_
extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
+int (*_machine_kexec_prepare)(struct kimage *) = NULL;
+void (*_machine_kexec_shutdown)(void) = NULL;
+void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+#ifdef CONFIG_SMP
+void (*relocated_kexec_smp_wait) (void *);
+atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
+#endif
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ if (_machine_kexec_prepare)
+ return _machine_kexec_prepare(kimage);
return 0;
}
@@ -33,11 +43,17 @@ machine_kexec_cleanup(struct kimage *kim
void
machine_shutdown(void)
{
+ if (_machine_kexec_shutdown)
+ _machine_kexec_shutdown();
}
void
machine_crash_shutdown(struct pt_regs *regs)
{
+ if (_machine_crash_shutdown)
+ _machine_crash_shutdown(regs);
+ else
+ default_machine_crash_shutdown(regs);
}
typedef void (*noretfun_t)(void) __attribute__((noreturn));
@@ -52,7 +68,9 @@ machine_kexec(struct kimage *image)
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
- kexec_start_address = (unsigned long) phys_to_virt(image->start);
+ kexec_start_address =
+ (unsigned long) phys_to_virt(image->start);
+
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
@@ -63,7 +81,7 @@ machine_kexec(struct kimage *image)
* The generic kexec code builds a page list with physical
* addresses. they are directly accessible through KSEG0 (or
* CKSEG0 or XPHYS if on 64bit system), hence the
- * pys_to_virt() call.
+ * phys_to_virt() call.
*/
for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE);
ptr = (entry & IND_INDIRECTION) ?
@@ -81,5 +99,13 @@ machine_kexec(struct kimage *image)
printk("Will call new kernel at %08lx\n", image->start);
printk("Bye ...\n");
__flush_cache_all();
+#ifdef CONFIG_SMP
+ /* All secondary cpus now may jump to kexec_wait cycle */
+ relocated_kexec_smp_wait = reboot_code_buffer +
+ (void *)(kexec_smp_wait - relocate_new_kernel);
+ smp_wmb();
+ atomic_set(&kexec_ready_to_reboot, 1);
+#endif
((noretfun_t) reboot_code_buffer)();
}
+
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -15,6 +15,11 @@
#include <asm/addrspace.h>
LEAF(relocate_new_kernel)
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+ PTR_L a3, arg3
+
PTR_L s0, kexec_indirection_page
PTR_L s1, kexec_start_address
@@ -26,7 +31,6 @@ process_entry:
and s3, s2, 0x1
beq s3, zero, 1f
and s4, s2, ~0x1 /* store destination addr in s4 */
- move a0, s4
b process_entry
1:
@@ -60,23 +64,100 @@ copy_word:
b process_entry
done:
+#ifdef CONFIG_SMP
+ /* kexec_flag reset is signal to other CPUs what kernel
+ was moved to it's location. Note - we need relocated address
+ of kexec_flag. */
+
+ bal 1f
+ 1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+ LONG_S zero,(t0)
+#endif
+
+ sync
/* jump to kexec_start_address */
j s1
END(relocate_new_kernel)
-kexec_start_address:
- EXPORT(kexec_start_address)
+#ifdef CONFIG_SMP
+/*
+ * Other CPUs should wait until code is relocated and
+ * then start at entry (?) point.
+ */
+LEAF(kexec_smp_wait)
+ PTR_L a0, s_arg0
+ PTR_L a1, s_arg1
+ PTR_L a2, s_arg2
+ PTR_L a3, s_arg3
+ PTR_L s1, kexec_start_address
+
+ /* Non-relocated address works for args and kexec_start_address ( old
+ * kernel is not overwritten). But we need relocated address of
+ * kexec_flag.
+ */
+
+ bal 1f
+1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+
+1: LONG_L s0, (t0)
+ bne s0, zero,1b
+
+ sync
+ j s1
+ END(kexec_smp_wait)
+#endif
+
+#ifdef __mips64
+ /* all PTR's must be aligned to 8 byte in 64-bit mode */
+ .align 3
+#endif
+
+/* All parameters to new kernel are passed in registers a0-a3.
+ * kexec_args[0..3] are uses to prepare register values.
+ */
+
+EXPORT(kexec_args)
+arg0: PTR 0x0
+arg1: PTR 0x0
+arg2: PTR 0x0
+arg3: PTR 0x0
+ .size kexec_args,PTRSIZE*4
+
+#ifdef CONFIG_SMP
+/*
+ * Secondary CPUs may have different kernel parameters in
+ * their registers a0-a3. secondary_kexec_args[0..3] are used
+ * to prepare register values.
+ */
+EXPORT(secondary_kexec_args)
+s_arg0: PTR 0x0
+s_arg1: PTR 0x0
+s_arg2: PTR 0x0
+s_arg3: PTR 0x0
+ .size secondary_kexec_args,PTRSIZE*4
+kexec_flag:
+ LONG 0x1
+
+#endif
+
+EXPORT(kexec_start_address)
PTR 0x0
.size kexec_start_address, PTRSIZE
-kexec_indirection_page:
- EXPORT(kexec_indirection_page)
+EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
relocate_new_kernel_end:
-relocate_new_kernel_size:
- EXPORT(relocate_new_kernel_size)
+EXPORT(relocate_new_kernel_size)
PTR relocate_new_kernel_end - relocate_new_kernel
.size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -21,6 +21,7 @@
#include <linux/console.h>
#include <linux/pfn.h>
#include <linux/debugfs.h>
+#include <linux/kexec.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
@@ -488,12 +489,62 @@ static void __init arch_mem_init(char **
}
bootmem_init();
+#ifdef CONFIG_KEXEC
+ if (crashk_res.start != crashk_res.end)
+ reserve_bootmem(crashk_res.start,
+ crashk_res.end - crashk_res.start + 1,
+ BOOTMEM_DEFAULT);
+#endif
device_tree_init();
sparse_init();
plat_swiotlb_setup();
paging_init();
}
+#ifdef CONFIG_KEXEC
+static inline unsigned long long get_total_mem(void)
+{
+ unsigned long long total;
+ total = max_pfn - min_low_pfn;
+ return total << PAGE_SHIFT;
+}
+
+static void __init mips_parse_crashkernel(void)
+{
+ unsigned long long total_mem;
+ unsigned long long crash_size, crash_base;
+ int ret;
+
+ total_mem = get_total_mem();
+ ret = parse_crashkernel(boot_command_line, total_mem,
+ &crash_size, &crash_base);
+ if (ret != 0 || crash_size <= 0)
+ return;
+
+ crashk_res.start = crash_base;
+ crashk_res.end = crash_base + crash_size - 1;
+}
+static void __init request_crashkernel(struct resource *res)
+{
+ int ret;
+
+ ret = request_resource(res, &crashk_res);
+ if (!ret)
+ printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
+ "for crashkernel\n",
+ (unsigned long)((crashk_res.end -
+ crashk_res.start + 1) >> 20),
+ (unsigned long)(crashk_res.start >> 20));
+}
+#else
+static void __init mips_parse_crashkernel(void)
+{
+}
+static void __init request_crashkernel(struct resource *res)
+{
+}
+#endif
+
static void __init resource_init(void)
{
int i;
@@ -509,6 +560,8 @@ static void __init resource_init(void)
/*
* Request address space for all standard RAM.
*/
+ mips_parse_crashkernel();
+
for (i = 0; i < boot_mem_map.nr_map; i++) {
struct resource *res;
unsigned long start, end;
@@ -544,6 +597,7 @@ static void __init resource_init(void)
*/
request_resource(res, &code_resource);
request_resource(res, &data_resource);
+ request_crashkernel(res);
}
}
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -433,3 +433,21 @@ void flush_tlb_one(unsigned long vaddr)
EXPORT_SYMBOL(flush_tlb_page);
EXPORT_SYMBOL(flush_tlb_one);
+
+#if defined(CONFIG_KEXEC)
+void (*dump_ipi_function_ptr)(void *) = NULL;
+void dump_send_ipi(void (*dump_ipi_callback)(void *))
+{
+ int i;
+ int cpu = smp_processor_id();
+
+ dump_ipi_function_ptr = dump_ipi_callback;
+ smp_mb();
+ for_each_online_cpu(i)
+ if (i != cpu)
+ core_send_ipi(i, SMP_DUMP);
+
+}
+EXPORT_SYMBOL(dump_send_ipi);
+#endif
+
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -9,22 +9,45 @@
#ifndef _MIPS_KEXEC
# define _MIPS_KEXEC
+#include <asm/stacktrace.h>
+
+extern unsigned long long elfcorehdr_addr;
+
/* Maximum physical address we can use pages from */
#define KEXEC_SOURCE_MEMORY_LIMIT (0x20000000)
/* Maximum address we can reach in physical address mode */
#define KEXEC_DESTINATION_MEMORY_LIMIT (0x20000000)
/* Maximum address we can use for the control code buffer */
#define KEXEC_CONTROL_MEMORY_LIMIT (0x20000000)
-
-#define KEXEC_CONTROL_PAGE_SIZE 4096
+/* Reserve 3*4096 bytes for board-specific info */
+#define KEXEC_CONTROL_PAGE_SIZE (4096 + 3*4096)
/* The native architecture */
#define KEXEC_ARCH KEXEC_ARCH_MIPS
+#define MAX_NOTE_BYTES 1024
static inline void crash_setup_regs(struct pt_regs *newregs,
- struct pt_regs *oldregs)
+ struct pt_regs *oldregs)
{
- /* Dummy implementation for now */
+ if (oldregs)
+ memcpy(newregs, oldregs, sizeof(*newregs));
+ else
+ prepare_frametrace(newregs);
}
+#ifdef CONFIG_KEXEC
+struct kimage;
+extern unsigned long kexec_args[4];
+extern int (*_machine_kexec_prepare)(struct kimage *);
+extern void (*_machine_kexec_shutdown)(void);
+extern void (*_machine_crash_shutdown)(struct pt_regs *regs);
+extern void default_machine_crash_shutdown(struct pt_regs *regs);
+#ifdef CONFIG_SMP
+extern const unsigned char kexec_smp_wait[];
+extern unsigned long secondary_kexec_args[4];
+extern void (*relocated_kexec_smp_wait) (void *);
+extern atomic_t kexec_ready_to_reboot;
+#endif
+#endif
+
#endif /* !_MIPS_KEXEC */
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -40,6 +40,8 @@ extern int __cpu_logical_map[NR_CPUS];
#define SMP_CALL_FUNCTION 0x2
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
+/* Used by kexec crashdump to save all cpu's state */
+#define SMP_DUMP 0x8
extern volatile cpumask_t cpu_callin_map;
@@ -91,4 +93,9 @@ static inline void arch_send_call_functi
mp_ops->send_ipi_mask(mask, SMP_CALL_FUNCTION);
}
+extern void core_send_ipi(int cpu, unsigned int action);
+#if defined(CONFIG_KEXEC)
+extern void (*dump_ipi_function_ptr)(void *);
+void dump_send_ipi(void (*dump_ipi_callback)(void *));
+#endif
#endif /* __ASM_SMP_H */

View File

@ -0,0 +1,159 @@
From 03cd81fbca6b91317ec1a7b3b3c09fb8d08f83a6 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 18:42:08 +0000
Subject: MIPS: Kexec: Enhance the support
Changes:
o Print more information in machine_kexec() for debugging
E.g. with this information, the kexec_start_address has been found
it was wrong with 64bit kernel / o32 kexec-tools. Which must be
fixed later.
o Link relocate_kernel.S to a section for future extension
This allows more functions can be added for the kexec relocation
part even written in C. to add code into that section, you just need
to mark your function or data with __kexec or
__attribute__((__section__(".__kexec.relocate")))
TODO:
1. Make 64bit kernel / o32|n32|64 kexec-tools works
Fix the user-space kexec-tools, seems the tool only work for 32bit
machine. So, we need to add 64bit support for it. The address of the
entry point(kexec_start_address) is wrong and make the "kexec -e" fail.
the real entry point must be read from the new kernel image by the
user-space kexec-tools, otherwise, it will not work. The above 64bit
support tested is 64bit kernel with o32 user-space kexec-tools. The root
cause may be the different definition of virt_to_phys() and
phys_to_virt() in the kexec-tools and kernel space for 64bit system /
o32 kernel.
Ref: http://www.linux-mips.org/archives/linux-mips/2009-08/msg00149.html
2. Pass the arguments from kexec-tools to the new kernel image
Please refer to: "MIPS: Loongson: Kexec: Pass parameters to new kernel"
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -36,6 +36,16 @@ static inline void crash_setup_regs(stru
}
#ifdef CONFIG_KEXEC
+
+#define __kexec __attribute__((__section__(".__kexec.relocate")))
+
+/* The linker tells us where the relocate_new_kernel part is. */
+extern const unsigned char __start___kexec_relocate;
+extern const unsigned char __end___kexec_relocate;
+
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+
struct kimage;
extern unsigned long kexec_args[4];
extern int (*_machine_kexec_prepare)(struct kimage *);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,12 +13,6 @@
#include <asm/cacheflush.h>
#include <asm/page.h>
-extern const unsigned char relocate_new_kernel[];
-extern const size_t relocate_new_kernel_size;
-
-extern unsigned long kexec_start_address;
-extern unsigned long kexec_indirection_page;
-
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
@@ -61,21 +55,34 @@ typedef void (*noretfun_t)(void) __attri
void
machine_kexec(struct kimage *image)
{
+ unsigned long kexec_relocate_size;
unsigned long reboot_code_buffer;
unsigned long entry;
unsigned long *ptr;
+ kexec_relocate_size = (unsigned long)(&__end___kexec_relocate) -
+ (unsigned long)(&__start___kexec_relocate);
+ pr_info("kexec_relocate_size = %lu\n", kexec_relocate_size);
+
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
+ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
kexec_start_address =
(unsigned long) phys_to_virt(image->start);
+ pr_info("kexec_start_address(entry point of new kernel) = %p\n",
+ (void *)kexec_start_address);
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
+ pr_info("kexec_indirection_page = %p\n",
+ (void *)kexec_indirection_page);
- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
- relocate_new_kernel_size);
+ memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
+ kexec_relocate_size);
+
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
/*
* The generic kexec code builds a page list with physical
@@ -96,8 +103,8 @@ machine_kexec(struct kimage *image)
*/
local_irq_disable();
- printk("Will call new kernel at %08lx\n", image->start);
- printk("Bye ...\n");
+ pr_info("Will call new kernel at %p\n", (void *)kexec_start_address);
+ pr_info("Bye ...\n");
__flush_cache_all();
#ifdef CONFIG_SMP
/* All secondary cpus now may jump to kexec_wait cycle */
@@ -108,4 +115,3 @@ machine_kexec(struct kimage *image)
#endif
((noretfun_t) reboot_code_buffer)();
}
-
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -14,6 +14,8 @@
#include <asm/stackframe.h>
#include <asm/addrspace.h>
+ .section .kexec.relocate, "ax"
+
LEAF(relocate_new_kernel)
PTR_L a0, arg0
PTR_L a1, arg1
@@ -155,9 +157,3 @@ EXPORT(kexec_start_address)
EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
-
-relocate_new_kernel_end:
-
-EXPORT(relocate_new_kernel_size)
- PTR relocate_new_kernel_end - relocate_new_kernel
- .size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -50,6 +50,10 @@ SECTIONS
*(.text.*)
*(.fixup)
*(.gnu.warning)
+ __start___kexec_relocate = .;
+ KEEP(*(.kexec.relocate))
+ KEEP(*(.__kexec.relocate))
+ __end___kexec_relocate = .;
} :text = 0
_etext = .; /* End of text section */

View File

@ -0,0 +1,52 @@
From 49d07a29653b1f2c6ae273b3d8fe93d981f43004 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Wed, 12 Jan 2011 20:59:32 +0000
Subject: MIPS: Kexec: Init the arguments for the new kernel image
Whenever the kexec-tools pass the command lines to the new kernel image,
init the arguments as the ones for the 1st kernel image. This fixed the
booting failure of Kexec on YeeLoong.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/delay.h>
+#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
@@ -21,9 +22,30 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
+static void machine_kexec_init_args(void)
+{
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
+ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
+ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
+ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ /*
+ * Whenever arguments passed from kexec-tools, Init the arguments as
+ * the original ones to avoid booting failure.
+ *
+ * This can be overrided by _machine_kexec_prepare().
+ */
+ machine_kexec_init_args();
+
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
return 0;

View File

@ -0,0 +1,88 @@
From 240c76841b26f1b09aaced33414ee1d08b6454cf Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Sat, 15 Jan 2011 12:46:03 +0000
Subject: MIPS: Get kernel parameters from kexec-tools
Before, we simply use the command lines from the original bootloader,
but it is not convenient. Now, we accept the kernel parameters from the
--command-line or --append option of the kexec-tools. But If not
--command-line or --apend option indicated, will fall back to use the
ones from the original bootloader.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,6 +13,7 @@
#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
+#include <asm/uaccess.h>
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
@@ -35,6 +36,56 @@ static void machine_kexec_init_args(void
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
}
+#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+int machine_kexec_pass_args(struct kimage *image)
+{
+ int i, argc = 0;
+ char *bootloader = "kexec";
+ int *kexec_argv = (int *)kexec_args[1];
+
+ for (i = 0; i < image->nr_segments; i++) {
+ if (!strncmp(bootloader, (char *)image->segment[i].buf,
+ strlen(bootloader))) {
+ /*
+ * convert command line string to array
+ * of parameters (as bootloader does).
+ */
+ /*
+ * Note: we do treat the 1st string "kexec" as an
+ * argument ;-) so, argc here is 1.
+ */
+ char *str = (char *)image->segment[i].buf;
+ char *ptr = strchr(str, ' ');
+ char *kbuf = (char *)kexec_argv[0];
+ /* Whenever --command-line or --append used, "kexec" is copied */
+ argc = 1;
+ /* Parse the offset */
+ while (ptr && (ARGV_MAX_ARGS > argc)) {
+ *ptr = '\0';
+ if (ptr[1] != ' ' && ptr[1] != '\0') {
+ int offt = (int)(ptr - str + 1);
+ kexec_argv[argc] = (int)kbuf + offt;
+ argc++;
+ }
+ ptr = strchr(ptr + 1, ' ');
+ }
+ if (argc > 1) {
+ /* Copy to kernel space */
+ copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
+ fw_arg0 = kexec_args[0] = argc;
+ }
+ break;
+ }
+ }
+
+ pr_info("argc = %lu\n", kexec_args[0]);
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+
+ return 0;
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
@@ -45,6 +96,7 @@ machine_kexec_prepare(struct kimage *kim
* This can be overrided by _machine_kexec_prepare().
*/
machine_kexec_init_args();
+ machine_kexec_pass_args(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);

View File

@ -0,0 +1,83 @@
From 4aded085fa0057a9a1e1dcec631f950307360c1f Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:46:19 +0000
Subject: MIPS: Fix compiling failure of relocate_kernel.S
The following errors is fixed with the help of <asm/asm_nosec.h>. for
this file need to put different symbols in the same section, the
original LEAF, NESTED and EXPORT (without explicit section indication)
must be used, <asm/asm_nosec.h> does it.
arch/mips/kernel/relocate_kernel.S: Assembler messages:
arch/mips/kernel/relocate_kernel.S:162: Error: operation combines symbols in different segments
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -7,6 +7,7 @@
*/
#include <asm/asm.h>
+#include <asm/asm_nosec.h>
#include <asm/asmmacro.h>
#include <asm/regdef.h>
#include <asm/page.h>
--- /dev/null
+++ b/arch/mips/include/asm/asm_nosec.h
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1999, 2001 by Ralf Baechle
+ * Copyright (C) 1999 by Silicon Graphics, Inc.
+ * Copyright (C) 2001 MIPS Technologies, Inc.
+ * Copyright (C) 2002 Maciej W. Rozycki
+ * Copyright (C) 2010 Wu Zhangjin <wuzhangjin@gmail.com>
+ *
+ * Derive from <asm/asm.h>
+ *
+ * Override the macros without -ffunction-sections and -fdata-sections support.
+ * If several functions or data must be put in the same section, please include
+ * this header file after the <asm/asm.h> to override the generic definition.
+ */
+
+#ifndef __ASM_ASM_NOSEC_H
+#define __ASM_ASM_NOSEC_H
+
+#undef LEAF
+#undef NESTED
+#undef EXPORT
+
+/*
+ * LEAF - declare leaf routine
+ */
+#define LEAF(symbol) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, 0, ra
+
+/*
+ * NESTED - declare nested routine entry point
+ */
+#define NESTED(symbol, framesize, rpc) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, framesize, rpc
+
+/*
+ * EXPORT - export definition of symbol
+ */
+#define EXPORT(symbol) \
+ .globl symbol; \
+symbol:
+
+#endif /* __ASM_ASM_NOSEC_H */

View File

@ -0,0 +1,186 @@
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -23,67 +23,104 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
-static void machine_kexec_init_args(void)
+#define KEXEC_MIPS_ARGV_BUF_SIZE COMMAND_LINE_SIZE
+#define KEXEC_MIPS_ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+char kexec_argv_buf[KEXEC_MIPS_ARGV_BUF_SIZE] __kexec;
+int kexec_argv[KEXEC_MIPS_ARGV_MAX_ARGS] __kexec;
+
+static void
+machine_kexec_print_args(void)
{
- kexec_args[0] = fw_arg0;
- kexec_args[1] = fw_arg1;
- kexec_args[2] = fw_arg2;
- kexec_args[3] = fw_arg3;
+ int i;
pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
-}
-#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("kexec_argv[%d] = %p, %s\n", i,
+ (char *)kexec_argv[i], (char *)kexec_argv[i]);
+}
-int machine_kexec_pass_args(struct kimage *image)
+static void
+machine_kexec_init_argv(struct kimage *image)
{
- int i, argc = 0;
- char *bootloader = "kexec";
- int *kexec_argv = (int *)kexec_args[1];
+ void __user *buf;
+ size_t bufsz;
+ size_t size;
+ int i;
+ bufsz = 0;
for (i = 0; i < image->nr_segments; i++) {
- if (!strncmp(bootloader, (char *)image->segment[i].buf,
- strlen(bootloader))) {
- /*
- * convert command line string to array
- * of parameters (as bootloader does).
- */
- /*
- * Note: we do treat the 1st string "kexec" as an
- * argument ;-) so, argc here is 1.
- */
- char *str = (char *)image->segment[i].buf;
- char *ptr = strchr(str, ' ');
- char *kbuf = (char *)kexec_argv[0];
- /* Whenever --command-line or --append used, "kexec" is copied */
- argc = 1;
- /* Parse the offset */
- while (ptr && (ARGV_MAX_ARGS > argc)) {
- *ptr = '\0';
- if (ptr[1] != ' ' && ptr[1] != '\0') {
- int offt = (int)(ptr - str + 1);
- kexec_argv[argc] = (int)kbuf + offt;
- argc++;
- }
- ptr = strchr(ptr + 1, ' ');
- }
- if (argc > 1) {
- /* Copy to kernel space */
- copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
- fw_arg0 = kexec_args[0] = argc;
- }
- break;
+ struct kexec_segment *seg;
+
+ seg = &image->segment[i];
+ if (seg->bufsz < 6)
+ continue;
+
+ if (strncmp((char *) seg->buf, "kexec", 5))
+ continue;
+
+ /* don't copy "kexec" */
+ buf = seg->buf + 5;
+ bufsz = seg->bufsz - 5;
+ break;
+ }
+
+ if (i >= image->nr_segments)
+ return;
+
+ size = KEXEC_MIPS_ARGV_BUF_SIZE - 1;
+ size = min(size, bufsz);
+ if (size < bufsz)
+ pr_warn("kexec command line truncated to %d bytes\n", size);
+
+ /* Copy to kernel space */
+ copy_from_user(kexec_argv_buf, buf, size);
+}
+
+static void
+machine_kexec_parse_argv(struct kimage *image)
+{
+ char *reboot_code_buffer;
+ int reloc_delta;
+ char *ptr;
+ int argc;
+ int i;
+
+ ptr = kexec_argv_buf;
+ argc = 0;
+
+ /*
+ * convert command line string to array of parameters
+ * (as bootloader does).
+ */
+ while (ptr && *ptr && (KEXEC_MIPS_ARGV_MAX_ARGS > argc)) {
+ if (*ptr == ' ') {
+ *ptr++ = '\0';
+ continue;
}
+
+ kexec_argv[argc++] = (int) ptr;
+ ptr = strchr(ptr, ' ');
}
- pr_info("argc = %lu\n", kexec_args[0]);
- for (i = 0; i < kexec_args[0]; i++)
- pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+ if (!argc)
+ return;
- return 0;
+ kexec_args[0] = argc;
+ kexec_args[1] = (int) kexec_argv;
+ kexec_args[2] = 0;
+ kexec_args[3] = 0;
+
+ reboot_code_buffer = page_address(image->control_code_page);
+ reloc_delta = reboot_code_buffer - (char *) &__start___kexec_relocate;
+
+ kexec_args[1] += reloc_delta;
+ for (i = 0; i < argc; i++)
+ kexec_argv[i] += reloc_delta;
}
int
@@ -95,8 +132,14 @@ machine_kexec_prepare(struct kimage *kim
*
* This can be overrided by _machine_kexec_prepare().
*/
- machine_kexec_init_args();
- machine_kexec_pass_args(kimage);
+
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ machine_kexec_init_argv(kimage);
+ machine_kexec_parse_argv(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
@@ -152,11 +195,13 @@ machine_kexec(struct kimage *image)
pr_info("kexec_indirection_page = %p\n",
(void *)kexec_indirection_page);
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
+
memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
kexec_relocate_size);
- pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
- &__start___kexec_relocate, (void *)reboot_code_buffer);
+ machine_kexec_print_args();
/*
* The generic kexec code builds a page list with physical

View File

@ -0,0 +1,616 @@
From eee16330c9de9adf7880cce9f1d32e13f89706bb Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:16:47 +0000
Subject: MIPS: Add crash and kdump support
From: http://patchwork.linux-mips.org/patch/1025/
Hello folks,
Please find here MIPS crash and kdump patches.
This is patch set of 3 patches:
1. generic MIPS changes (kernel);
2. MIPS Cavium Octeon board kexec/kdump code (kernel);
3. Kexec user space MIPS changes.
Patches were tested on the latest linux-mips@ git kernel and the latest
kexec-tools git on Cavium Octeon 50xx board.
I also made the same code working on RMI XLR/XLS boards for both
mips32 and mips64 kernels.
Best regards,
Maxim Uvarov.
------
[ Zhangjin: Several trivial building failure has been fixed.
Note: the 2nd patch can not be cleanly applied, but may be a good
reference for the other board development:
+ MIPS Cavium Octeon board kexec,kdump support
http://patchwork.linux-mips.org/patch/1026/
And the 3rd patch has already been merged into the mainline kexec-tools:
+ some kexec MIPS improvements
http://patchwork.linux-mips.org/patch/1027/
kexec-tools is available here:
+ http://horms.net/projects/kexec/
git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
]
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -93,7 +93,8 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
+obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o
obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
--- /dev/null
+++ b/arch/mips/kernel/crash.c
@@ -0,0 +1,75 @@
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/kexec.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#ifdef CONFIG_CRASH_DUMP
+unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX;
+#endif
+
+/* This keeps a track of which one is crashing cpu. */
+int crashing_cpu = -1;
+static cpumask_t cpus_in_crash = CPU_MASK_NONE;
+
+#ifdef CONFIG_SMP
+void crash_shutdown_secondary(void *ignore)
+{
+ struct pt_regs *regs;
+ int cpu = smp_processor_id();
+
+ regs = task_pt_regs(current);
+
+ if (!cpu_online(cpu))
+ return;
+
+ local_irq_disable();
+ if (!cpu_isset(cpu, cpus_in_crash))
+ crash_save_cpu(regs, cpu);
+ cpu_set(cpu, cpus_in_crash);
+
+ while (!atomic_read(&kexec_ready_to_reboot))
+ cpu_relax();
+ relocated_kexec_smp_wait(NULL);
+ /* NOTREACHED */
+}
+
+static void crash_kexec_prepare_cpus(void)
+{
+ unsigned int msecs;
+
+ unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
+
+ dump_send_ipi(crash_shutdown_secondary);
+ smp_wmb();
+
+ /*
+ * The crash CPU sends an IPI and wait for other CPUs to
+ * respond. Delay of at least 10 seconds.
+ */
+ printk(KERN_EMERG "Sending IPI to other cpus...\n");
+ msecs = 10000;
+ while ((cpus_weight(cpus_in_crash) < ncpus) && (--msecs > 0)) {
+ cpu_relax();
+ mdelay(1);
+ }
+}
+
+#else
+static void crash_kexec_prepare_cpus(void) {}
+#endif
+
+void default_machine_crash_shutdown(struct pt_regs *regs)
+{
+ local_irq_disable();
+ crashing_cpu = smp_processor_id();
+ crash_save_cpu(regs, crashing_cpu);
+ crash_kexec_prepare_cpus();
+ cpu_set(crashing_cpu, cpus_in_crash);
+}
--- /dev/null
+++ b/arch/mips/kernel/crash_dump.c
@@ -0,0 +1,86 @@
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PROC_VMCORE
+static int __init parse_elfcorehdr(char *p)
+{
+ if (p)
+ elfcorehdr_addr = memparse(p, &p);
+ return 1;
+}
+__setup("elfcorehdr=", parse_elfcorehdr);
+#endif
+
+static int __init parse_savemaxmem(char *p)
+{
+ if (p)
+ saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1;
+
+ return 1;
+}
+__setup("savemaxmem=", parse_savemaxmem);
+
+
+static void *kdump_buf_page;
+
+/**
+ * copy_oldmem_page - copy one page from "oldmem"
+ * @pfn: page frame number to be copied
+ * @buf: target memory address for the copy; this can be in kernel address
+ * space or user address space (see @userbuf)
+ * @csize: number of bytes to copy
+ * @offset: offset in bytes into the page (based on pfn) to begin the copy
+ * @userbuf: if set, @buf is in user address space, use copy_to_user(),
+ * otherwise @buf is in kernel address space, use memcpy().
+ *
+ * Copy a page from "oldmem". For this page, there is no pte mapped
+ * in the current kernel.
+ *
+ * Calling copy_to_user() in atomic context is not desirable. Hence first
+ * copying the data to a pre-allocated kernel page and then copying to user
+ * space in non-atomic context.
+ */
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+ size_t csize, unsigned long offset, int userbuf)
+{
+ void *vaddr;
+
+ if (!csize)
+ return 0;
+
+ vaddr = kmap_atomic_pfn(pfn, KM_PTE0);
+
+ if (!userbuf) {
+ memcpy(buf, (vaddr + offset), csize);
+ kunmap_atomic(vaddr, KM_PTE0);
+ } else {
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Kdump buffer page not"
+ " allocated\n");
+ return -EFAULT;
+ }
+ copy_page(kdump_buf_page, vaddr);
+ kunmap_atomic(vaddr, KM_PTE0);
+ if (copy_to_user(buf, (kdump_buf_page + offset), csize))
+ return -EFAULT;
+ }
+
+ return csize;
+}
+
+static int __init kdump_buf_page_init(void)
+{
+ int ret = 0;
+
+ kdump_buf_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Failed to allocate kdump buffer"
+ " page\n");
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+arch_initcall(kdump_buf_page_init);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -19,9 +19,19 @@ extern const size_t relocate_new_kernel_
extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
+int (*_machine_kexec_prepare)(struct kimage *) = NULL;
+void (*_machine_kexec_shutdown)(void) = NULL;
+void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+#ifdef CONFIG_SMP
+void (*relocated_kexec_smp_wait) (void *);
+atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
+#endif
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ if (_machine_kexec_prepare)
+ return _machine_kexec_prepare(kimage);
return 0;
}
@@ -33,11 +43,17 @@ machine_kexec_cleanup(struct kimage *kim
void
machine_shutdown(void)
{
+ if (_machine_kexec_shutdown)
+ _machine_kexec_shutdown();
}
void
machine_crash_shutdown(struct pt_regs *regs)
{
+ if (_machine_crash_shutdown)
+ _machine_crash_shutdown(regs);
+ else
+ default_machine_crash_shutdown(regs);
}
typedef void (*noretfun_t)(void) __attribute__((noreturn));
@@ -52,7 +68,9 @@ machine_kexec(struct kimage *image)
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
- kexec_start_address = (unsigned long) phys_to_virt(image->start);
+ kexec_start_address =
+ (unsigned long) phys_to_virt(image->start);
+
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
@@ -63,7 +81,7 @@ machine_kexec(struct kimage *image)
* The generic kexec code builds a page list with physical
* addresses. they are directly accessible through KSEG0 (or
* CKSEG0 or XPHYS if on 64bit system), hence the
- * pys_to_virt() call.
+ * phys_to_virt() call.
*/
for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE);
ptr = (entry & IND_INDIRECTION) ?
@@ -81,5 +99,13 @@ machine_kexec(struct kimage *image)
printk("Will call new kernel at %08lx\n", image->start);
printk("Bye ...\n");
__flush_cache_all();
+#ifdef CONFIG_SMP
+ /* All secondary cpus now may jump to kexec_wait cycle */
+ relocated_kexec_smp_wait = reboot_code_buffer +
+ (void *)(kexec_smp_wait - relocate_new_kernel);
+ smp_wmb();
+ atomic_set(&kexec_ready_to_reboot, 1);
+#endif
((noretfun_t) reboot_code_buffer)();
}
+
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -15,6 +15,11 @@
#include <asm/addrspace.h>
LEAF(relocate_new_kernel)
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+ PTR_L a3, arg3
+
PTR_L s0, kexec_indirection_page
PTR_L s1, kexec_start_address
@@ -26,7 +31,6 @@ process_entry:
and s3, s2, 0x1
beq s3, zero, 1f
and s4, s2, ~0x1 /* store destination addr in s4 */
- move a0, s4
b process_entry
1:
@@ -60,23 +64,100 @@ copy_word:
b process_entry
done:
+#ifdef CONFIG_SMP
+ /* kexec_flag reset is signal to other CPUs what kernel
+ was moved to it's location. Note - we need relocated address
+ of kexec_flag. */
+
+ bal 1f
+ 1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+ LONG_S zero,(t0)
+#endif
+
+ sync
/* jump to kexec_start_address */
j s1
END(relocate_new_kernel)
-kexec_start_address:
- EXPORT(kexec_start_address)
+#ifdef CONFIG_SMP
+/*
+ * Other CPUs should wait until code is relocated and
+ * then start at entry (?) point.
+ */
+LEAF(kexec_smp_wait)
+ PTR_L a0, s_arg0
+ PTR_L a1, s_arg1
+ PTR_L a2, s_arg2
+ PTR_L a3, s_arg3
+ PTR_L s1, kexec_start_address
+
+ /* Non-relocated address works for args and kexec_start_address ( old
+ * kernel is not overwritten). But we need relocated address of
+ * kexec_flag.
+ */
+
+ bal 1f
+1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+
+1: LONG_L s0, (t0)
+ bne s0, zero,1b
+
+ sync
+ j s1
+ END(kexec_smp_wait)
+#endif
+
+#ifdef __mips64
+ /* all PTR's must be aligned to 8 byte in 64-bit mode */
+ .align 3
+#endif
+
+/* All parameters to new kernel are passed in registers a0-a3.
+ * kexec_args[0..3] are uses to prepare register values.
+ */
+
+EXPORT(kexec_args)
+arg0: PTR 0x0
+arg1: PTR 0x0
+arg2: PTR 0x0
+arg3: PTR 0x0
+ .size kexec_args,PTRSIZE*4
+
+#ifdef CONFIG_SMP
+/*
+ * Secondary CPUs may have different kernel parameters in
+ * their registers a0-a3. secondary_kexec_args[0..3] are used
+ * to prepare register values.
+ */
+EXPORT(secondary_kexec_args)
+s_arg0: PTR 0x0
+s_arg1: PTR 0x0
+s_arg2: PTR 0x0
+s_arg3: PTR 0x0
+ .size secondary_kexec_args,PTRSIZE*4
+kexec_flag:
+ LONG 0x1
+
+#endif
+
+EXPORT(kexec_start_address)
PTR 0x0
.size kexec_start_address, PTRSIZE
-kexec_indirection_page:
- EXPORT(kexec_indirection_page)
+EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
relocate_new_kernel_end:
-relocate_new_kernel_size:
- EXPORT(relocate_new_kernel_size)
+EXPORT(relocate_new_kernel_size)
PTR relocate_new_kernel_end - relocate_new_kernel
.size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -21,6 +21,7 @@
#include <linux/console.h>
#include <linux/pfn.h>
#include <linux/debugfs.h>
+#include <linux/kexec.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
@@ -488,12 +489,62 @@ static void __init arch_mem_init(char **
}
bootmem_init();
+#ifdef CONFIG_KEXEC
+ if (crashk_res.start != crashk_res.end)
+ reserve_bootmem(crashk_res.start,
+ crashk_res.end - crashk_res.start + 1,
+ BOOTMEM_DEFAULT);
+#endif
device_tree_init();
sparse_init();
plat_swiotlb_setup();
paging_init();
}
+#ifdef CONFIG_KEXEC
+static inline unsigned long long get_total_mem(void)
+{
+ unsigned long long total;
+ total = max_pfn - min_low_pfn;
+ return total << PAGE_SHIFT;
+}
+
+static void __init mips_parse_crashkernel(void)
+{
+ unsigned long long total_mem;
+ unsigned long long crash_size, crash_base;
+ int ret;
+
+ total_mem = get_total_mem();
+ ret = parse_crashkernel(boot_command_line, total_mem,
+ &crash_size, &crash_base);
+ if (ret != 0 || crash_size <= 0)
+ return;
+
+ crashk_res.start = crash_base;
+ crashk_res.end = crash_base + crash_size - 1;
+}
+static void __init request_crashkernel(struct resource *res)
+{
+ int ret;
+
+ ret = request_resource(res, &crashk_res);
+ if (!ret)
+ printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
+ "for crashkernel\n",
+ (unsigned long)((crashk_res.end -
+ crashk_res.start + 1) >> 20),
+ (unsigned long)(crashk_res.start >> 20));
+}
+#else
+static void __init mips_parse_crashkernel(void)
+{
+}
+static void __init request_crashkernel(struct resource *res)
+{
+}
+#endif
+
static void __init resource_init(void)
{
int i;
@@ -509,6 +560,8 @@ static void __init resource_init(void)
/*
* Request address space for all standard RAM.
*/
+ mips_parse_crashkernel();
+
for (i = 0; i < boot_mem_map.nr_map; i++) {
struct resource *res;
unsigned long start, end;
@@ -544,6 +597,7 @@ static void __init resource_init(void)
*/
request_resource(res, &code_resource);
request_resource(res, &data_resource);
+ request_crashkernel(res);
}
}
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -433,3 +433,21 @@ void flush_tlb_one(unsigned long vaddr)
EXPORT_SYMBOL(flush_tlb_page);
EXPORT_SYMBOL(flush_tlb_one);
+
+#if defined(CONFIG_KEXEC)
+void (*dump_ipi_function_ptr)(void *) = NULL;
+void dump_send_ipi(void (*dump_ipi_callback)(void *))
+{
+ int i;
+ int cpu = smp_processor_id();
+
+ dump_ipi_function_ptr = dump_ipi_callback;
+ smp_mb();
+ for_each_online_cpu(i)
+ if (i != cpu)
+ core_send_ipi(i, SMP_DUMP);
+
+}
+EXPORT_SYMBOL(dump_send_ipi);
+#endif
+
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -9,22 +9,45 @@
#ifndef _MIPS_KEXEC
# define _MIPS_KEXEC
+#include <asm/stacktrace.h>
+
+extern unsigned long long elfcorehdr_addr;
+
/* Maximum physical address we can use pages from */
#define KEXEC_SOURCE_MEMORY_LIMIT (0x20000000)
/* Maximum address we can reach in physical address mode */
#define KEXEC_DESTINATION_MEMORY_LIMIT (0x20000000)
/* Maximum address we can use for the control code buffer */
#define KEXEC_CONTROL_MEMORY_LIMIT (0x20000000)
-
-#define KEXEC_CONTROL_PAGE_SIZE 4096
+/* Reserve 3*4096 bytes for board-specific info */
+#define KEXEC_CONTROL_PAGE_SIZE (4096 + 3*4096)
/* The native architecture */
#define KEXEC_ARCH KEXEC_ARCH_MIPS
+#define MAX_NOTE_BYTES 1024
static inline void crash_setup_regs(struct pt_regs *newregs,
- struct pt_regs *oldregs)
+ struct pt_regs *oldregs)
{
- /* Dummy implementation for now */
+ if (oldregs)
+ memcpy(newregs, oldregs, sizeof(*newregs));
+ else
+ prepare_frametrace(newregs);
}
+#ifdef CONFIG_KEXEC
+struct kimage;
+extern unsigned long kexec_args[4];
+extern int (*_machine_kexec_prepare)(struct kimage *);
+extern void (*_machine_kexec_shutdown)(void);
+extern void (*_machine_crash_shutdown)(struct pt_regs *regs);
+extern void default_machine_crash_shutdown(struct pt_regs *regs);
+#ifdef CONFIG_SMP
+extern const unsigned char kexec_smp_wait[];
+extern unsigned long secondary_kexec_args[4];
+extern void (*relocated_kexec_smp_wait) (void *);
+extern atomic_t kexec_ready_to_reboot;
+#endif
+#endif
+
#endif /* !_MIPS_KEXEC */
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -40,6 +40,8 @@ extern int __cpu_logical_map[NR_CPUS];
#define SMP_CALL_FUNCTION 0x2
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
+/* Used by kexec crashdump to save all cpu's state */
+#define SMP_DUMP 0x8
extern volatile cpumask_t cpu_callin_map;
@@ -91,4 +93,9 @@ static inline void arch_send_call_functi
mp_ops->send_ipi_mask(mask, SMP_CALL_FUNCTION);
}
+extern void core_send_ipi(int cpu, unsigned int action);
+#if defined(CONFIG_KEXEC)
+extern void (*dump_ipi_function_ptr)(void *);
+void dump_send_ipi(void (*dump_ipi_callback)(void *));
+#endif
#endif /* __ASM_SMP_H */

View File

@ -0,0 +1,159 @@
From 03cd81fbca6b91317ec1a7b3b3c09fb8d08f83a6 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 18:42:08 +0000
Subject: MIPS: Kexec: Enhance the support
Changes:
o Print more information in machine_kexec() for debugging
E.g. with this information, the kexec_start_address has been found
it was wrong with 64bit kernel / o32 kexec-tools. Which must be
fixed later.
o Link relocate_kernel.S to a section for future extension
This allows more functions can be added for the kexec relocation
part even written in C. to add code into that section, you just need
to mark your function or data with __kexec or
__attribute__((__section__(".__kexec.relocate")))
TODO:
1. Make 64bit kernel / o32|n32|64 kexec-tools works
Fix the user-space kexec-tools, seems the tool only work for 32bit
machine. So, we need to add 64bit support for it. The address of the
entry point(kexec_start_address) is wrong and make the "kexec -e" fail.
the real entry point must be read from the new kernel image by the
user-space kexec-tools, otherwise, it will not work. The above 64bit
support tested is 64bit kernel with o32 user-space kexec-tools. The root
cause may be the different definition of virt_to_phys() and
phys_to_virt() in the kexec-tools and kernel space for 64bit system /
o32 kernel.
Ref: http://www.linux-mips.org/archives/linux-mips/2009-08/msg00149.html
2. Pass the arguments from kexec-tools to the new kernel image
Please refer to: "MIPS: Loongson: Kexec: Pass parameters to new kernel"
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -36,6 +36,16 @@ static inline void crash_setup_regs(stru
}
#ifdef CONFIG_KEXEC
+
+#define __kexec __attribute__((__section__(".__kexec.relocate")))
+
+/* The linker tells us where the relocate_new_kernel part is. */
+extern const unsigned char __start___kexec_relocate;
+extern const unsigned char __end___kexec_relocate;
+
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+
struct kimage;
extern unsigned long kexec_args[4];
extern int (*_machine_kexec_prepare)(struct kimage *);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,12 +13,6 @@
#include <asm/cacheflush.h>
#include <asm/page.h>
-extern const unsigned char relocate_new_kernel[];
-extern const size_t relocate_new_kernel_size;
-
-extern unsigned long kexec_start_address;
-extern unsigned long kexec_indirection_page;
-
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
@@ -61,21 +55,34 @@ typedef void (*noretfun_t)(void) __attri
void
machine_kexec(struct kimage *image)
{
+ unsigned long kexec_relocate_size;
unsigned long reboot_code_buffer;
unsigned long entry;
unsigned long *ptr;
+ kexec_relocate_size = (unsigned long)(&__end___kexec_relocate) -
+ (unsigned long)(&__start___kexec_relocate);
+ pr_info("kexec_relocate_size = %lu\n", kexec_relocate_size);
+
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
+ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
kexec_start_address =
(unsigned long) phys_to_virt(image->start);
+ pr_info("kexec_start_address(entry point of new kernel) = %p\n",
+ (void *)kexec_start_address);
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
+ pr_info("kexec_indirection_page = %p\n",
+ (void *)kexec_indirection_page);
- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
- relocate_new_kernel_size);
+ memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
+ kexec_relocate_size);
+
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
/*
* The generic kexec code builds a page list with physical
@@ -96,8 +103,8 @@ machine_kexec(struct kimage *image)
*/
local_irq_disable();
- printk("Will call new kernel at %08lx\n", image->start);
- printk("Bye ...\n");
+ pr_info("Will call new kernel at %p\n", (void *)kexec_start_address);
+ pr_info("Bye ...\n");
__flush_cache_all();
#ifdef CONFIG_SMP
/* All secondary cpus now may jump to kexec_wait cycle */
@@ -108,4 +115,3 @@ machine_kexec(struct kimage *image)
#endif
((noretfun_t) reboot_code_buffer)();
}
-
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -14,6 +14,8 @@
#include <asm/stackframe.h>
#include <asm/addrspace.h>
+ .section .kexec.relocate, "ax"
+
LEAF(relocate_new_kernel)
PTR_L a0, arg0
PTR_L a1, arg1
@@ -155,9 +157,3 @@ EXPORT(kexec_start_address)
EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
-
-relocate_new_kernel_end:
-
-EXPORT(relocate_new_kernel_size)
- PTR relocate_new_kernel_end - relocate_new_kernel
- .size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -50,6 +50,10 @@ SECTIONS
*(.text.*)
*(.fixup)
*(.gnu.warning)
+ __start___kexec_relocate = .;
+ KEEP(*(.kexec.relocate))
+ KEEP(*(.__kexec.relocate))
+ __end___kexec_relocate = .;
} :text = 0
_etext = .; /* End of text section */

View File

@ -0,0 +1,52 @@
From 49d07a29653b1f2c6ae273b3d8fe93d981f43004 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Wed, 12 Jan 2011 20:59:32 +0000
Subject: MIPS: Kexec: Init the arguments for the new kernel image
Whenever the kexec-tools pass the command lines to the new kernel image,
init the arguments as the ones for the 1st kernel image. This fixed the
booting failure of Kexec on YeeLoong.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/delay.h>
+#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
@@ -21,9 +22,30 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
+static void machine_kexec_init_args(void)
+{
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
+ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
+ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
+ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ /*
+ * Whenever arguments passed from kexec-tools, Init the arguments as
+ * the original ones to avoid booting failure.
+ *
+ * This can be overrided by _machine_kexec_prepare().
+ */
+ machine_kexec_init_args();
+
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
return 0;

View File

@ -0,0 +1,88 @@
From 240c76841b26f1b09aaced33414ee1d08b6454cf Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Sat, 15 Jan 2011 12:46:03 +0000
Subject: MIPS: Get kernel parameters from kexec-tools
Before, we simply use the command lines from the original bootloader,
but it is not convenient. Now, we accept the kernel parameters from the
--command-line or --append option of the kexec-tools. But If not
--command-line or --apend option indicated, will fall back to use the
ones from the original bootloader.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,6 +13,7 @@
#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
+#include <asm/uaccess.h>
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
@@ -35,6 +36,56 @@ static void machine_kexec_init_args(void
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
}
+#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+int machine_kexec_pass_args(struct kimage *image)
+{
+ int i, argc = 0;
+ char *bootloader = "kexec";
+ int *kexec_argv = (int *)kexec_args[1];
+
+ for (i = 0; i < image->nr_segments; i++) {
+ if (!strncmp(bootloader, (char *)image->segment[i].buf,
+ strlen(bootloader))) {
+ /*
+ * convert command line string to array
+ * of parameters (as bootloader does).
+ */
+ /*
+ * Note: we do treat the 1st string "kexec" as an
+ * argument ;-) so, argc here is 1.
+ */
+ char *str = (char *)image->segment[i].buf;
+ char *ptr = strchr(str, ' ');
+ char *kbuf = (char *)kexec_argv[0];
+ /* Whenever --command-line or --append used, "kexec" is copied */
+ argc = 1;
+ /* Parse the offset */
+ while (ptr && (ARGV_MAX_ARGS > argc)) {
+ *ptr = '\0';
+ if (ptr[1] != ' ' && ptr[1] != '\0') {
+ int offt = (int)(ptr - str + 1);
+ kexec_argv[argc] = (int)kbuf + offt;
+ argc++;
+ }
+ ptr = strchr(ptr + 1, ' ');
+ }
+ if (argc > 1) {
+ /* Copy to kernel space */
+ copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
+ fw_arg0 = kexec_args[0] = argc;
+ }
+ break;
+ }
+ }
+
+ pr_info("argc = %lu\n", kexec_args[0]);
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+
+ return 0;
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
@@ -45,6 +96,7 @@ machine_kexec_prepare(struct kimage *kim
* This can be overrided by _machine_kexec_prepare().
*/
machine_kexec_init_args();
+ machine_kexec_pass_args(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);

View File

@ -0,0 +1,83 @@
From 4aded085fa0057a9a1e1dcec631f950307360c1f Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:46:19 +0000
Subject: MIPS: Fix compiling failure of relocate_kernel.S
The following errors is fixed with the help of <asm/asm_nosec.h>. for
this file need to put different symbols in the same section, the
original LEAF, NESTED and EXPORT (without explicit section indication)
must be used, <asm/asm_nosec.h> does it.
arch/mips/kernel/relocate_kernel.S: Assembler messages:
arch/mips/kernel/relocate_kernel.S:162: Error: operation combines symbols in different segments
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -7,6 +7,7 @@
*/
#include <asm/asm.h>
+#include <asm/asm_nosec.h>
#include <asm/asmmacro.h>
#include <asm/regdef.h>
#include <asm/page.h>
--- /dev/null
+++ b/arch/mips/include/asm/asm_nosec.h
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1999, 2001 by Ralf Baechle
+ * Copyright (C) 1999 by Silicon Graphics, Inc.
+ * Copyright (C) 2001 MIPS Technologies, Inc.
+ * Copyright (C) 2002 Maciej W. Rozycki
+ * Copyright (C) 2010 Wu Zhangjin <wuzhangjin@gmail.com>
+ *
+ * Derive from <asm/asm.h>
+ *
+ * Override the macros without -ffunction-sections and -fdata-sections support.
+ * If several functions or data must be put in the same section, please include
+ * this header file after the <asm/asm.h> to override the generic definition.
+ */
+
+#ifndef __ASM_ASM_NOSEC_H
+#define __ASM_ASM_NOSEC_H
+
+#undef LEAF
+#undef NESTED
+#undef EXPORT
+
+/*
+ * LEAF - declare leaf routine
+ */
+#define LEAF(symbol) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, 0, ra
+
+/*
+ * NESTED - declare nested routine entry point
+ */
+#define NESTED(symbol, framesize, rpc) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, framesize, rpc
+
+/*
+ * EXPORT - export definition of symbol
+ */
+#define EXPORT(symbol) \
+ .globl symbol; \
+symbol:
+
+#endif /* __ASM_ASM_NOSEC_H */

View File

@ -0,0 +1,186 @@
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -23,67 +23,104 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
-static void machine_kexec_init_args(void)
+#define KEXEC_MIPS_ARGV_BUF_SIZE COMMAND_LINE_SIZE
+#define KEXEC_MIPS_ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+char kexec_argv_buf[KEXEC_MIPS_ARGV_BUF_SIZE] __kexec;
+int kexec_argv[KEXEC_MIPS_ARGV_MAX_ARGS] __kexec;
+
+static void
+machine_kexec_print_args(void)
{
- kexec_args[0] = fw_arg0;
- kexec_args[1] = fw_arg1;
- kexec_args[2] = fw_arg2;
- kexec_args[3] = fw_arg3;
+ int i;
pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
-}
-#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("kexec_argv[%d] = %p, %s\n", i,
+ (char *)kexec_argv[i], (char *)kexec_argv[i]);
+}
-int machine_kexec_pass_args(struct kimage *image)
+static void
+machine_kexec_init_argv(struct kimage *image)
{
- int i, argc = 0;
- char *bootloader = "kexec";
- int *kexec_argv = (int *)kexec_args[1];
+ void __user *buf;
+ size_t bufsz;
+ size_t size;
+ int i;
+ bufsz = 0;
for (i = 0; i < image->nr_segments; i++) {
- if (!strncmp(bootloader, (char *)image->segment[i].buf,
- strlen(bootloader))) {
- /*
- * convert command line string to array
- * of parameters (as bootloader does).
- */
- /*
- * Note: we do treat the 1st string "kexec" as an
- * argument ;-) so, argc here is 1.
- */
- char *str = (char *)image->segment[i].buf;
- char *ptr = strchr(str, ' ');
- char *kbuf = (char *)kexec_argv[0];
- /* Whenever --command-line or --append used, "kexec" is copied */
- argc = 1;
- /* Parse the offset */
- while (ptr && (ARGV_MAX_ARGS > argc)) {
- *ptr = '\0';
- if (ptr[1] != ' ' && ptr[1] != '\0') {
- int offt = (int)(ptr - str + 1);
- kexec_argv[argc] = (int)kbuf + offt;
- argc++;
- }
- ptr = strchr(ptr + 1, ' ');
- }
- if (argc > 1) {
- /* Copy to kernel space */
- copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
- fw_arg0 = kexec_args[0] = argc;
- }
- break;
+ struct kexec_segment *seg;
+
+ seg = &image->segment[i];
+ if (seg->bufsz < 6)
+ continue;
+
+ if (strncmp((char *) seg->buf, "kexec", 5))
+ continue;
+
+ /* don't copy "kexec" */
+ buf = seg->buf + 5;
+ bufsz = seg->bufsz - 5;
+ break;
+ }
+
+ if (i >= image->nr_segments)
+ return;
+
+ size = KEXEC_MIPS_ARGV_BUF_SIZE - 1;
+ size = min(size, bufsz);
+ if (size < bufsz)
+ pr_warn("kexec command line truncated to %d bytes\n", size);
+
+ /* Copy to kernel space */
+ copy_from_user(kexec_argv_buf, buf, size);
+}
+
+static void
+machine_kexec_parse_argv(struct kimage *image)
+{
+ char *reboot_code_buffer;
+ int reloc_delta;
+ char *ptr;
+ int argc;
+ int i;
+
+ ptr = kexec_argv_buf;
+ argc = 0;
+
+ /*
+ * convert command line string to array of parameters
+ * (as bootloader does).
+ */
+ while (ptr && *ptr && (KEXEC_MIPS_ARGV_MAX_ARGS > argc)) {
+ if (*ptr == ' ') {
+ *ptr++ = '\0';
+ continue;
}
+
+ kexec_argv[argc++] = (int) ptr;
+ ptr = strchr(ptr, ' ');
}
- pr_info("argc = %lu\n", kexec_args[0]);
- for (i = 0; i < kexec_args[0]; i++)
- pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+ if (!argc)
+ return;
- return 0;
+ kexec_args[0] = argc;
+ kexec_args[1] = (int) kexec_argv;
+ kexec_args[2] = 0;
+ kexec_args[3] = 0;
+
+ reboot_code_buffer = page_address(image->control_code_page);
+ reloc_delta = reboot_code_buffer - (char *) &__start___kexec_relocate;
+
+ kexec_args[1] += reloc_delta;
+ for (i = 0; i < argc; i++)
+ kexec_argv[i] += reloc_delta;
}
int
@@ -95,8 +132,14 @@ machine_kexec_prepare(struct kimage *kim
*
* This can be overrided by _machine_kexec_prepare().
*/
- machine_kexec_init_args();
- machine_kexec_pass_args(kimage);
+
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ machine_kexec_init_argv(kimage);
+ machine_kexec_parse_argv(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
@@ -152,11 +195,13 @@ machine_kexec(struct kimage *image)
pr_info("kexec_indirection_page = %p\n",
(void *)kexec_indirection_page);
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
+
memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
kexec_relocate_size);
- pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
- &__start___kexec_relocate, (void *)reboot_code_buffer);
+ machine_kexec_print_args();
/*
* The generic kexec code builds a page list with physical

View File

@ -0,0 +1,616 @@
From eee16330c9de9adf7880cce9f1d32e13f89706bb Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:16:47 +0000
Subject: MIPS: Add crash and kdump support
From: http://patchwork.linux-mips.org/patch/1025/
Hello folks,
Please find here MIPS crash and kdump patches.
This is patch set of 3 patches:
1. generic MIPS changes (kernel);
2. MIPS Cavium Octeon board kexec/kdump code (kernel);
3. Kexec user space MIPS changes.
Patches were tested on the latest linux-mips@ git kernel and the latest
kexec-tools git on Cavium Octeon 50xx board.
I also made the same code working on RMI XLR/XLS boards for both
mips32 and mips64 kernels.
Best regards,
Maxim Uvarov.
------
[ Zhangjin: Several trivial building failure has been fixed.
Note: the 2nd patch can not be cleanly applied, but may be a good
reference for the other board development:
+ MIPS Cavium Octeon board kexec,kdump support
http://patchwork.linux-mips.org/patch/1026/
And the 3rd patch has already been merged into the mainline kexec-tools:
+ some kexec MIPS improvements
http://patchwork.linux-mips.org/patch/1027/
kexec-tools is available here:
+ http://horms.net/projects/kexec/
git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git
]
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -93,7 +93,8 @@ obj-$(CONFIG_I8253) += i8253.o
obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o
-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
+obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o
obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
--- /dev/null
+++ b/arch/mips/kernel/crash.c
@@ -0,0 +1,75 @@
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/kexec.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#ifdef CONFIG_CRASH_DUMP
+unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX;
+#endif
+
+/* This keeps a track of which one is crashing cpu. */
+int crashing_cpu = -1;
+static cpumask_t cpus_in_crash = CPU_MASK_NONE;
+
+#ifdef CONFIG_SMP
+void crash_shutdown_secondary(void *ignore)
+{
+ struct pt_regs *regs;
+ int cpu = smp_processor_id();
+
+ regs = task_pt_regs(current);
+
+ if (!cpu_online(cpu))
+ return;
+
+ local_irq_disable();
+ if (!cpu_isset(cpu, cpus_in_crash))
+ crash_save_cpu(regs, cpu);
+ cpu_set(cpu, cpus_in_crash);
+
+ while (!atomic_read(&kexec_ready_to_reboot))
+ cpu_relax();
+ relocated_kexec_smp_wait(NULL);
+ /* NOTREACHED */
+}
+
+static void crash_kexec_prepare_cpus(void)
+{
+ unsigned int msecs;
+
+ unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
+
+ dump_send_ipi(crash_shutdown_secondary);
+ smp_wmb();
+
+ /*
+ * The crash CPU sends an IPI and wait for other CPUs to
+ * respond. Delay of at least 10 seconds.
+ */
+ printk(KERN_EMERG "Sending IPI to other cpus...\n");
+ msecs = 10000;
+ while ((cpus_weight(cpus_in_crash) < ncpus) && (--msecs > 0)) {
+ cpu_relax();
+ mdelay(1);
+ }
+}
+
+#else
+static void crash_kexec_prepare_cpus(void) {}
+#endif
+
+void default_machine_crash_shutdown(struct pt_regs *regs)
+{
+ local_irq_disable();
+ crashing_cpu = smp_processor_id();
+ crash_save_cpu(regs, crashing_cpu);
+ crash_kexec_prepare_cpus();
+ cpu_set(crashing_cpu, cpus_in_crash);
+}
--- /dev/null
+++ b/arch/mips/kernel/crash_dump.c
@@ -0,0 +1,86 @@
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PROC_VMCORE
+static int __init parse_elfcorehdr(char *p)
+{
+ if (p)
+ elfcorehdr_addr = memparse(p, &p);
+ return 1;
+}
+__setup("elfcorehdr=", parse_elfcorehdr);
+#endif
+
+static int __init parse_savemaxmem(char *p)
+{
+ if (p)
+ saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1;
+
+ return 1;
+}
+__setup("savemaxmem=", parse_savemaxmem);
+
+
+static void *kdump_buf_page;
+
+/**
+ * copy_oldmem_page - copy one page from "oldmem"
+ * @pfn: page frame number to be copied
+ * @buf: target memory address for the copy; this can be in kernel address
+ * space or user address space (see @userbuf)
+ * @csize: number of bytes to copy
+ * @offset: offset in bytes into the page (based on pfn) to begin the copy
+ * @userbuf: if set, @buf is in user address space, use copy_to_user(),
+ * otherwise @buf is in kernel address space, use memcpy().
+ *
+ * Copy a page from "oldmem". For this page, there is no pte mapped
+ * in the current kernel.
+ *
+ * Calling copy_to_user() in atomic context is not desirable. Hence first
+ * copying the data to a pre-allocated kernel page and then copying to user
+ * space in non-atomic context.
+ */
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+ size_t csize, unsigned long offset, int userbuf)
+{
+ void *vaddr;
+
+ if (!csize)
+ return 0;
+
+ vaddr = kmap_atomic_pfn(pfn, KM_PTE0);
+
+ if (!userbuf) {
+ memcpy(buf, (vaddr + offset), csize);
+ kunmap_atomic(vaddr, KM_PTE0);
+ } else {
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Kdump buffer page not"
+ " allocated\n");
+ return -EFAULT;
+ }
+ copy_page(kdump_buf_page, vaddr);
+ kunmap_atomic(vaddr, KM_PTE0);
+ if (copy_to_user(buf, (kdump_buf_page + offset), csize))
+ return -EFAULT;
+ }
+
+ return csize;
+}
+
+static int __init kdump_buf_page_init(void)
+{
+ int ret = 0;
+
+ kdump_buf_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!kdump_buf_page) {
+ printk(KERN_WARNING "Kdump: Failed to allocate kdump buffer"
+ " page\n");
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+arch_initcall(kdump_buf_page_init);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -19,9 +19,19 @@ extern const size_t relocate_new_kernel_
extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
+int (*_machine_kexec_prepare)(struct kimage *) = NULL;
+void (*_machine_kexec_shutdown)(void) = NULL;
+void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+#ifdef CONFIG_SMP
+void (*relocated_kexec_smp_wait) (void *);
+atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
+#endif
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ if (_machine_kexec_prepare)
+ return _machine_kexec_prepare(kimage);
return 0;
}
@@ -33,11 +43,17 @@ machine_kexec_cleanup(struct kimage *kim
void
machine_shutdown(void)
{
+ if (_machine_kexec_shutdown)
+ _machine_kexec_shutdown();
}
void
machine_crash_shutdown(struct pt_regs *regs)
{
+ if (_machine_crash_shutdown)
+ _machine_crash_shutdown(regs);
+ else
+ default_machine_crash_shutdown(regs);
}
typedef void (*noretfun_t)(void) __attribute__((noreturn));
@@ -52,7 +68,9 @@ machine_kexec(struct kimage *image)
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
- kexec_start_address = (unsigned long) phys_to_virt(image->start);
+ kexec_start_address =
+ (unsigned long) phys_to_virt(image->start);
+
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
@@ -63,7 +81,7 @@ machine_kexec(struct kimage *image)
* The generic kexec code builds a page list with physical
* addresses. they are directly accessible through KSEG0 (or
* CKSEG0 or XPHYS if on 64bit system), hence the
- * pys_to_virt() call.
+ * phys_to_virt() call.
*/
for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE);
ptr = (entry & IND_INDIRECTION) ?
@@ -81,5 +99,13 @@ machine_kexec(struct kimage *image)
printk("Will call new kernel at %08lx\n", image->start);
printk("Bye ...\n");
__flush_cache_all();
+#ifdef CONFIG_SMP
+ /* All secondary cpus now may jump to kexec_wait cycle */
+ relocated_kexec_smp_wait = reboot_code_buffer +
+ (void *)(kexec_smp_wait - relocate_new_kernel);
+ smp_wmb();
+ atomic_set(&kexec_ready_to_reboot, 1);
+#endif
((noretfun_t) reboot_code_buffer)();
}
+
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -15,6 +15,11 @@
#include <asm/addrspace.h>
LEAF(relocate_new_kernel)
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+ PTR_L a3, arg3
+
PTR_L s0, kexec_indirection_page
PTR_L s1, kexec_start_address
@@ -26,7 +31,6 @@ process_entry:
and s3, s2, 0x1
beq s3, zero, 1f
and s4, s2, ~0x1 /* store destination addr in s4 */
- move a0, s4
b process_entry
1:
@@ -60,23 +64,100 @@ copy_word:
b process_entry
done:
+#ifdef CONFIG_SMP
+ /* kexec_flag reset is signal to other CPUs what kernel
+ was moved to it's location. Note - we need relocated address
+ of kexec_flag. */
+
+ bal 1f
+ 1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+ LONG_S zero,(t0)
+#endif
+
+ sync
/* jump to kexec_start_address */
j s1
END(relocate_new_kernel)
-kexec_start_address:
- EXPORT(kexec_start_address)
+#ifdef CONFIG_SMP
+/*
+ * Other CPUs should wait until code is relocated and
+ * then start at entry (?) point.
+ */
+LEAF(kexec_smp_wait)
+ PTR_L a0, s_arg0
+ PTR_L a1, s_arg1
+ PTR_L a2, s_arg2
+ PTR_L a3, s_arg3
+ PTR_L s1, kexec_start_address
+
+ /* Non-relocated address works for args and kexec_start_address ( old
+ * kernel is not overwritten). But we need relocated address of
+ * kexec_flag.
+ */
+
+ bal 1f
+1: move t1,ra;
+ PTR_LA t2,1b
+ PTR_LA t0,kexec_flag
+ PTR_SUB t0,t0,t2;
+ PTR_ADD t0,t1,t0;
+
+1: LONG_L s0, (t0)
+ bne s0, zero,1b
+
+ sync
+ j s1
+ END(kexec_smp_wait)
+#endif
+
+#ifdef __mips64
+ /* all PTR's must be aligned to 8 byte in 64-bit mode */
+ .align 3
+#endif
+
+/* All parameters to new kernel are passed in registers a0-a3.
+ * kexec_args[0..3] are uses to prepare register values.
+ */
+
+EXPORT(kexec_args)
+arg0: PTR 0x0
+arg1: PTR 0x0
+arg2: PTR 0x0
+arg3: PTR 0x0
+ .size kexec_args,PTRSIZE*4
+
+#ifdef CONFIG_SMP
+/*
+ * Secondary CPUs may have different kernel parameters in
+ * their registers a0-a3. secondary_kexec_args[0..3] are used
+ * to prepare register values.
+ */
+EXPORT(secondary_kexec_args)
+s_arg0: PTR 0x0
+s_arg1: PTR 0x0
+s_arg2: PTR 0x0
+s_arg3: PTR 0x0
+ .size secondary_kexec_args,PTRSIZE*4
+kexec_flag:
+ LONG 0x1
+
+#endif
+
+EXPORT(kexec_start_address)
PTR 0x0
.size kexec_start_address, PTRSIZE
-kexec_indirection_page:
- EXPORT(kexec_indirection_page)
+EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
relocate_new_kernel_end:
-relocate_new_kernel_size:
- EXPORT(relocate_new_kernel_size)
+EXPORT(relocate_new_kernel_size)
PTR relocate_new_kernel_end - relocate_new_kernel
.size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -21,6 +21,7 @@
#include <linux/console.h>
#include <linux/pfn.h>
#include <linux/debugfs.h>
+#include <linux/kexec.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
@@ -488,12 +489,62 @@ static void __init arch_mem_init(char **
}
bootmem_init();
+#ifdef CONFIG_KEXEC
+ if (crashk_res.start != crashk_res.end)
+ reserve_bootmem(crashk_res.start,
+ crashk_res.end - crashk_res.start + 1,
+ BOOTMEM_DEFAULT);
+#endif
device_tree_init();
sparse_init();
plat_swiotlb_setup();
paging_init();
}
+#ifdef CONFIG_KEXEC
+static inline unsigned long long get_total_mem(void)
+{
+ unsigned long long total;
+ total = max_pfn - min_low_pfn;
+ return total << PAGE_SHIFT;
+}
+
+static void __init mips_parse_crashkernel(void)
+{
+ unsigned long long total_mem;
+ unsigned long long crash_size, crash_base;
+ int ret;
+
+ total_mem = get_total_mem();
+ ret = parse_crashkernel(boot_command_line, total_mem,
+ &crash_size, &crash_base);
+ if (ret != 0 || crash_size <= 0)
+ return;
+
+ crashk_res.start = crash_base;
+ crashk_res.end = crash_base + crash_size - 1;
+}
+static void __init request_crashkernel(struct resource *res)
+{
+ int ret;
+
+ ret = request_resource(res, &crashk_res);
+ if (!ret)
+ printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
+ "for crashkernel\n",
+ (unsigned long)((crashk_res.end -
+ crashk_res.start + 1) >> 20),
+ (unsigned long)(crashk_res.start >> 20));
+}
+#else
+static void __init mips_parse_crashkernel(void)
+{
+}
+static void __init request_crashkernel(struct resource *res)
+{
+}
+#endif
+
static void __init resource_init(void)
{
int i;
@@ -509,6 +560,8 @@ static void __init resource_init(void)
/*
* Request address space for all standard RAM.
*/
+ mips_parse_crashkernel();
+
for (i = 0; i < boot_mem_map.nr_map; i++) {
struct resource *res;
unsigned long start, end;
@@ -544,6 +597,7 @@ static void __init resource_init(void)
*/
request_resource(res, &code_resource);
request_resource(res, &data_resource);
+ request_crashkernel(res);
}
}
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -433,3 +433,21 @@ void flush_tlb_one(unsigned long vaddr)
EXPORT_SYMBOL(flush_tlb_page);
EXPORT_SYMBOL(flush_tlb_one);
+
+#if defined(CONFIG_KEXEC)
+void (*dump_ipi_function_ptr)(void *) = NULL;
+void dump_send_ipi(void (*dump_ipi_callback)(void *))
+{
+ int i;
+ int cpu = smp_processor_id();
+
+ dump_ipi_function_ptr = dump_ipi_callback;
+ smp_mb();
+ for_each_online_cpu(i)
+ if (i != cpu)
+ core_send_ipi(i, SMP_DUMP);
+
+}
+EXPORT_SYMBOL(dump_send_ipi);
+#endif
+
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -9,22 +9,45 @@
#ifndef _MIPS_KEXEC
# define _MIPS_KEXEC
+#include <asm/stacktrace.h>
+
+extern unsigned long long elfcorehdr_addr;
+
/* Maximum physical address we can use pages from */
#define KEXEC_SOURCE_MEMORY_LIMIT (0x20000000)
/* Maximum address we can reach in physical address mode */
#define KEXEC_DESTINATION_MEMORY_LIMIT (0x20000000)
/* Maximum address we can use for the control code buffer */
#define KEXEC_CONTROL_MEMORY_LIMIT (0x20000000)
-
-#define KEXEC_CONTROL_PAGE_SIZE 4096
+/* Reserve 3*4096 bytes for board-specific info */
+#define KEXEC_CONTROL_PAGE_SIZE (4096 + 3*4096)
/* The native architecture */
#define KEXEC_ARCH KEXEC_ARCH_MIPS
+#define MAX_NOTE_BYTES 1024
static inline void crash_setup_regs(struct pt_regs *newregs,
- struct pt_regs *oldregs)
+ struct pt_regs *oldregs)
{
- /* Dummy implementation for now */
+ if (oldregs)
+ memcpy(newregs, oldregs, sizeof(*newregs));
+ else
+ prepare_frametrace(newregs);
}
+#ifdef CONFIG_KEXEC
+struct kimage;
+extern unsigned long kexec_args[4];
+extern int (*_machine_kexec_prepare)(struct kimage *);
+extern void (*_machine_kexec_shutdown)(void);
+extern void (*_machine_crash_shutdown)(struct pt_regs *regs);
+extern void default_machine_crash_shutdown(struct pt_regs *regs);
+#ifdef CONFIG_SMP
+extern const unsigned char kexec_smp_wait[];
+extern unsigned long secondary_kexec_args[4];
+extern void (*relocated_kexec_smp_wait) (void *);
+extern atomic_t kexec_ready_to_reboot;
+#endif
+#endif
+
#endif /* !_MIPS_KEXEC */
--- a/arch/mips/include/asm/smp.h
+++ b/arch/mips/include/asm/smp.h
@@ -40,6 +40,8 @@ extern int __cpu_logical_map[NR_CPUS];
#define SMP_CALL_FUNCTION 0x2
/* Octeon - Tell another core to flush its icache */
#define SMP_ICACHE_FLUSH 0x4
+/* Used by kexec crashdump to save all cpu's state */
+#define SMP_DUMP 0x8
extern volatile cpumask_t cpu_callin_map;
@@ -91,4 +93,9 @@ static inline void arch_send_call_functi
mp_ops->send_ipi_mask(mask, SMP_CALL_FUNCTION);
}
+extern void core_send_ipi(int cpu, unsigned int action);
+#if defined(CONFIG_KEXEC)
+extern void (*dump_ipi_function_ptr)(void *);
+void dump_send_ipi(void (*dump_ipi_callback)(void *));
+#endif
#endif /* __ASM_SMP_H */

View File

@ -0,0 +1,159 @@
From 03cd81fbca6b91317ec1a7b3b3c09fb8d08f83a6 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 18:42:08 +0000
Subject: MIPS: Kexec: Enhance the support
Changes:
o Print more information in machine_kexec() for debugging
E.g. with this information, the kexec_start_address has been found
it was wrong with 64bit kernel / o32 kexec-tools. Which must be
fixed later.
o Link relocate_kernel.S to a section for future extension
This allows more functions can be added for the kexec relocation
part even written in C. to add code into that section, you just need
to mark your function or data with __kexec or
__attribute__((__section__(".__kexec.relocate")))
TODO:
1. Make 64bit kernel / o32|n32|64 kexec-tools works
Fix the user-space kexec-tools, seems the tool only work for 32bit
machine. So, we need to add 64bit support for it. The address of the
entry point(kexec_start_address) is wrong and make the "kexec -e" fail.
the real entry point must be read from the new kernel image by the
user-space kexec-tools, otherwise, it will not work. The above 64bit
support tested is 64bit kernel with o32 user-space kexec-tools. The root
cause may be the different definition of virt_to_phys() and
phys_to_virt() in the kexec-tools and kernel space for 64bit system /
o32 kernel.
Ref: http://www.linux-mips.org/archives/linux-mips/2009-08/msg00149.html
2. Pass the arguments from kexec-tools to the new kernel image
Please refer to: "MIPS: Loongson: Kexec: Pass parameters to new kernel"
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/include/asm/kexec.h
+++ b/arch/mips/include/asm/kexec.h
@@ -36,6 +36,16 @@ static inline void crash_setup_regs(stru
}
#ifdef CONFIG_KEXEC
+
+#define __kexec __attribute__((__section__(".__kexec.relocate")))
+
+/* The linker tells us where the relocate_new_kernel part is. */
+extern const unsigned char __start___kexec_relocate;
+extern const unsigned char __end___kexec_relocate;
+
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+
struct kimage;
extern unsigned long kexec_args[4];
extern int (*_machine_kexec_prepare)(struct kimage *);
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,12 +13,6 @@
#include <asm/cacheflush.h>
#include <asm/page.h>
-extern const unsigned char relocate_new_kernel[];
-extern const size_t relocate_new_kernel_size;
-
-extern unsigned long kexec_start_address;
-extern unsigned long kexec_indirection_page;
-
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
@@ -61,21 +55,34 @@ typedef void (*noretfun_t)(void) __attri
void
machine_kexec(struct kimage *image)
{
+ unsigned long kexec_relocate_size;
unsigned long reboot_code_buffer;
unsigned long entry;
unsigned long *ptr;
+ kexec_relocate_size = (unsigned long)(&__end___kexec_relocate) -
+ (unsigned long)(&__start___kexec_relocate);
+ pr_info("kexec_relocate_size = %lu\n", kexec_relocate_size);
+
reboot_code_buffer =
(unsigned long)page_address(image->control_code_page);
+ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
kexec_start_address =
(unsigned long) phys_to_virt(image->start);
+ pr_info("kexec_start_address(entry point of new kernel) = %p\n",
+ (void *)kexec_start_address);
kexec_indirection_page =
(unsigned long) phys_to_virt(image->head & PAGE_MASK);
+ pr_info("kexec_indirection_page = %p\n",
+ (void *)kexec_indirection_page);
- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
- relocate_new_kernel_size);
+ memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
+ kexec_relocate_size);
+
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
/*
* The generic kexec code builds a page list with physical
@@ -96,8 +103,8 @@ machine_kexec(struct kimage *image)
*/
local_irq_disable();
- printk("Will call new kernel at %08lx\n", image->start);
- printk("Bye ...\n");
+ pr_info("Will call new kernel at %p\n", (void *)kexec_start_address);
+ pr_info("Bye ...\n");
__flush_cache_all();
#ifdef CONFIG_SMP
/* All secondary cpus now may jump to kexec_wait cycle */
@@ -108,4 +115,3 @@ machine_kexec(struct kimage *image)
#endif
((noretfun_t) reboot_code_buffer)();
}
-
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -14,6 +14,8 @@
#include <asm/stackframe.h>
#include <asm/addrspace.h>
+ .section .kexec.relocate, "ax"
+
LEAF(relocate_new_kernel)
PTR_L a0, arg0
PTR_L a1, arg1
@@ -155,9 +157,3 @@ EXPORT(kexec_start_address)
EXPORT(kexec_indirection_page)
PTR 0
.size kexec_indirection_page, PTRSIZE
-
-relocate_new_kernel_end:
-
-EXPORT(relocate_new_kernel_size)
- PTR relocate_new_kernel_end - relocate_new_kernel
- .size relocate_new_kernel_size, PTRSIZE
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -50,6 +50,10 @@ SECTIONS
*(.text.*)
*(.fixup)
*(.gnu.warning)
+ __start___kexec_relocate = .;
+ KEEP(*(.kexec.relocate))
+ KEEP(*(.__kexec.relocate))
+ __end___kexec_relocate = .;
} :text = 0
_etext = .; /* End of text section */

View File

@ -0,0 +1,52 @@
From 49d07a29653b1f2c6ae273b3d8fe93d981f43004 Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Wed, 12 Jan 2011 20:59:32 +0000
Subject: MIPS: Kexec: Init the arguments for the new kernel image
Whenever the kexec-tools pass the command lines to the new kernel image,
init the arguments as the ones for the 1st kernel image. This fixed the
booting failure of Kexec on YeeLoong.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/delay.h>
+#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
@@ -21,9 +22,30 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
+static void machine_kexec_init_args(void)
+{
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
+ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
+ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
+ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
+ /*
+ * Whenever arguments passed from kexec-tools, Init the arguments as
+ * the original ones to avoid booting failure.
+ *
+ * This can be overrided by _machine_kexec_prepare().
+ */
+ machine_kexec_init_args();
+
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
return 0;

View File

@ -0,0 +1,88 @@
From 240c76841b26f1b09aaced33414ee1d08b6454cf Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Sat, 15 Jan 2011 12:46:03 +0000
Subject: MIPS: Get kernel parameters from kexec-tools
Before, we simply use the command lines from the original bootloader,
but it is not convenient. Now, we accept the kernel parameters from the
--command-line or --append option of the kexec-tools. But If not
--command-line or --apend option indicated, will fall back to use the
ones from the original bootloader.
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -13,6 +13,7 @@
#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
+#include <asm/uaccess.h>
int (*_machine_kexec_prepare)(struct kimage *) = NULL;
void (*_machine_kexec_shutdown)(void) = NULL;
@@ -35,6 +36,56 @@ static void machine_kexec_init_args(void
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
}
+#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+int machine_kexec_pass_args(struct kimage *image)
+{
+ int i, argc = 0;
+ char *bootloader = "kexec";
+ int *kexec_argv = (int *)kexec_args[1];
+
+ for (i = 0; i < image->nr_segments; i++) {
+ if (!strncmp(bootloader, (char *)image->segment[i].buf,
+ strlen(bootloader))) {
+ /*
+ * convert command line string to array
+ * of parameters (as bootloader does).
+ */
+ /*
+ * Note: we do treat the 1st string "kexec" as an
+ * argument ;-) so, argc here is 1.
+ */
+ char *str = (char *)image->segment[i].buf;
+ char *ptr = strchr(str, ' ');
+ char *kbuf = (char *)kexec_argv[0];
+ /* Whenever --command-line or --append used, "kexec" is copied */
+ argc = 1;
+ /* Parse the offset */
+ while (ptr && (ARGV_MAX_ARGS > argc)) {
+ *ptr = '\0';
+ if (ptr[1] != ' ' && ptr[1] != '\0') {
+ int offt = (int)(ptr - str + 1);
+ kexec_argv[argc] = (int)kbuf + offt;
+ argc++;
+ }
+ ptr = strchr(ptr + 1, ' ');
+ }
+ if (argc > 1) {
+ /* Copy to kernel space */
+ copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
+ fw_arg0 = kexec_args[0] = argc;
+ }
+ break;
+ }
+ }
+
+ pr_info("argc = %lu\n", kexec_args[0]);
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+
+ return 0;
+}
+
int
machine_kexec_prepare(struct kimage *kimage)
{
@@ -45,6 +96,7 @@ machine_kexec_prepare(struct kimage *kim
* This can be overrided by _machine_kexec_prepare().
*/
machine_kexec_init_args();
+ machine_kexec_pass_args(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);

View File

@ -0,0 +1,83 @@
From 4aded085fa0057a9a1e1dcec631f950307360c1f Mon Sep 17 00:00:00 2001
From: Wu Zhangjin <wuzhangjin@gmail.com>
Date: Tue, 11 Jan 2011 13:46:19 +0000
Subject: MIPS: Fix compiling failure of relocate_kernel.S
The following errors is fixed with the help of <asm/asm_nosec.h>. for
this file need to put different symbols in the same section, the
original LEAF, NESTED and EXPORT (without explicit section indication)
must be used, <asm/asm_nosec.h> does it.
arch/mips/kernel/relocate_kernel.S: Assembler messages:
arch/mips/kernel/relocate_kernel.S:162: Error: operation combines symbols in different segments
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
---
(limited to 'arch/mips/kernel')
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -7,6 +7,7 @@
*/
#include <asm/asm.h>
+#include <asm/asm_nosec.h>
#include <asm/asmmacro.h>
#include <asm/regdef.h>
#include <asm/page.h>
--- /dev/null
+++ b/arch/mips/include/asm/asm_nosec.h
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1999, 2001 by Ralf Baechle
+ * Copyright (C) 1999 by Silicon Graphics, Inc.
+ * Copyright (C) 2001 MIPS Technologies, Inc.
+ * Copyright (C) 2002 Maciej W. Rozycki
+ * Copyright (C) 2010 Wu Zhangjin <wuzhangjin@gmail.com>
+ *
+ * Derive from <asm/asm.h>
+ *
+ * Override the macros without -ffunction-sections and -fdata-sections support.
+ * If several functions or data must be put in the same section, please include
+ * this header file after the <asm/asm.h> to override the generic definition.
+ */
+
+#ifndef __ASM_ASM_NOSEC_H
+#define __ASM_ASM_NOSEC_H
+
+#undef LEAF
+#undef NESTED
+#undef EXPORT
+
+/*
+ * LEAF - declare leaf routine
+ */
+#define LEAF(symbol) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, 0, ra
+
+/*
+ * NESTED - declare nested routine entry point
+ */
+#define NESTED(symbol, framesize, rpc) \
+ .globl symbol; \
+ .align 2; \
+ .type symbol, @function; \
+ .ent symbol, 0; \
+symbol: .frame sp, framesize, rpc
+
+/*
+ * EXPORT - export definition of symbol
+ */
+#define EXPORT(symbol) \
+ .globl symbol; \
+symbol:
+
+#endif /* __ASM_ASM_NOSEC_H */

View File

@ -0,0 +1,186 @@
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -23,67 +23,104 @@ void (*relocated_kexec_smp_wait) (void *
atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
#endif
-static void machine_kexec_init_args(void)
+#define KEXEC_MIPS_ARGV_BUF_SIZE COMMAND_LINE_SIZE
+#define KEXEC_MIPS_ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+
+char kexec_argv_buf[KEXEC_MIPS_ARGV_BUF_SIZE] __kexec;
+int kexec_argv[KEXEC_MIPS_ARGV_MAX_ARGS] __kexec;
+
+static void
+machine_kexec_print_args(void)
{
- kexec_args[0] = fw_arg0;
- kexec_args[1] = fw_arg1;
- kexec_args[2] = fw_arg2;
- kexec_args[3] = fw_arg3;
+ int i;
pr_info("kexec_args[0] (argc): %lu\n", kexec_args[0]);
pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
-}
-#define ARGV_MAX_ARGS (COMMAND_LINE_SIZE / 15)
+ for (i = 0; i < kexec_args[0]; i++)
+ pr_info("kexec_argv[%d] = %p, %s\n", i,
+ (char *)kexec_argv[i], (char *)kexec_argv[i]);
+}
-int machine_kexec_pass_args(struct kimage *image)
+static void
+machine_kexec_init_argv(struct kimage *image)
{
- int i, argc = 0;
- char *bootloader = "kexec";
- int *kexec_argv = (int *)kexec_args[1];
+ void __user *buf;
+ size_t bufsz;
+ size_t size;
+ int i;
+ bufsz = 0;
for (i = 0; i < image->nr_segments; i++) {
- if (!strncmp(bootloader, (char *)image->segment[i].buf,
- strlen(bootloader))) {
- /*
- * convert command line string to array
- * of parameters (as bootloader does).
- */
- /*
- * Note: we do treat the 1st string "kexec" as an
- * argument ;-) so, argc here is 1.
- */
- char *str = (char *)image->segment[i].buf;
- char *ptr = strchr(str, ' ');
- char *kbuf = (char *)kexec_argv[0];
- /* Whenever --command-line or --append used, "kexec" is copied */
- argc = 1;
- /* Parse the offset */
- while (ptr && (ARGV_MAX_ARGS > argc)) {
- *ptr = '\0';
- if (ptr[1] != ' ' && ptr[1] != '\0') {
- int offt = (int)(ptr - str + 1);
- kexec_argv[argc] = (int)kbuf + offt;
- argc++;
- }
- ptr = strchr(ptr + 1, ' ');
- }
- if (argc > 1) {
- /* Copy to kernel space */
- copy_from_user(kbuf, (char *)image->segment[i].buf, image->segment[i].bufsz);
- fw_arg0 = kexec_args[0] = argc;
- }
- break;
+ struct kexec_segment *seg;
+
+ seg = &image->segment[i];
+ if (seg->bufsz < 6)
+ continue;
+
+ if (strncmp((char *) seg->buf, "kexec", 5))
+ continue;
+
+ /* don't copy "kexec" */
+ buf = seg->buf + 5;
+ bufsz = seg->bufsz - 5;
+ break;
+ }
+
+ if (i >= image->nr_segments)
+ return;
+
+ size = KEXEC_MIPS_ARGV_BUF_SIZE - 1;
+ size = min(size, bufsz);
+ if (size < bufsz)
+ pr_warn("kexec command line truncated to %d bytes\n", size);
+
+ /* Copy to kernel space */
+ copy_from_user(kexec_argv_buf, buf, size);
+}
+
+static void
+machine_kexec_parse_argv(struct kimage *image)
+{
+ char *reboot_code_buffer;
+ int reloc_delta;
+ char *ptr;
+ int argc;
+ int i;
+
+ ptr = kexec_argv_buf;
+ argc = 0;
+
+ /*
+ * convert command line string to array of parameters
+ * (as bootloader does).
+ */
+ while (ptr && *ptr && (KEXEC_MIPS_ARGV_MAX_ARGS > argc)) {
+ if (*ptr == ' ') {
+ *ptr++ = '\0';
+ continue;
}
+
+ kexec_argv[argc++] = (int) ptr;
+ ptr = strchr(ptr, ' ');
}
- pr_info("argc = %lu\n", kexec_args[0]);
- for (i = 0; i < kexec_args[0]; i++)
- pr_info("argv[%d] = %p, %s\n", i, (char *)kexec_argv[i], (char *)kexec_argv[i]);
+ if (!argc)
+ return;
- return 0;
+ kexec_args[0] = argc;
+ kexec_args[1] = (int) kexec_argv;
+ kexec_args[2] = 0;
+ kexec_args[3] = 0;
+
+ reboot_code_buffer = page_address(image->control_code_page);
+ reloc_delta = reboot_code_buffer - (char *) &__start___kexec_relocate;
+
+ kexec_args[1] += reloc_delta;
+ for (i = 0; i < argc; i++)
+ kexec_argv[i] += reloc_delta;
}
int
@@ -95,8 +132,14 @@ machine_kexec_prepare(struct kimage *kim
*
* This can be overrided by _machine_kexec_prepare().
*/
- machine_kexec_init_args();
- machine_kexec_pass_args(kimage);
+
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ machine_kexec_init_argv(kimage);
+ machine_kexec_parse_argv(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
@@ -152,11 +195,13 @@ machine_kexec(struct kimage *image)
pr_info("kexec_indirection_page = %p\n",
(void *)kexec_indirection_page);
+ pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
+ &__start___kexec_relocate, (void *)reboot_code_buffer);
+
memcpy((void *)reboot_code_buffer, &__start___kexec_relocate,
kexec_relocate_size);
- pr_info("Copy kexec_relocate section from %p to reboot_code_buffer: %p\n",
- &__start___kexec_relocate, (void *)reboot_code_buffer);
+ machine_kexec_print_args();
/*
* The generic kexec code builds a page list with physical