diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2012-01-07 16:29:16 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2012-01-07 16:29:16 +0000 |
commit | 1f6caadd7062d46a8780f5c761030739109fd0c7 (patch) | |
tree | fea5a7ecead9e3e4bb44cb019e8caad46453fd00 /target/linux/generic/patches-3.0 | |
parent | 363361428e486763d1d6f7d6e5f9ceb7852ef701 (diff) | |
download | upstream-1f6caadd7062d46a8780f5c761030739109fd0c7.tar.gz upstream-1f6caadd7062d46a8780f5c761030739109fd0c7.tar.bz2 upstream-1f6caadd7062d46a8780f5c761030739109fd0c7.zip |
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).
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@29674 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/patches-3.0')
6 files changed, 1184 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.0/330-mips-add-crash-and-kdump-support.patch b/target/linux/generic/patches-3.0/330-mips-add-crash-and-kdump-support.patch new file mode 100644 index 0000000000..28621c4572 --- /dev/null +++ b/target/linux/generic/patches-3.0/330-mips-add-crash-and-kdump-support.patch @@ -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 */ diff --git a/target/linux/generic/patches-3.0/331-mips-kexec-enhanche-the-support.patch b/target/linux/generic/patches-3.0/331-mips-kexec-enhanche-the-support.patch new file mode 100644 index 0000000000..5ffc2e29bd --- /dev/null +++ b/target/linux/generic/patches-3.0/331-mips-kexec-enhanche-the-support.patch @@ -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 */ + diff --git a/target/linux/generic/patches-3.0/332-mips-kexec-init-the-arguments-for-the-new-kernel-image.patch b/target/linux/generic/patches-3.0/332-mips-kexec-init-the-arguments-for-the-new-kernel-image.patch new file mode 100644 index 0000000000..5507dde64d --- /dev/null +++ b/target/linux/generic/patches-3.0/332-mips-kexec-init-the-arguments-for-the-new-kernel-image.patch @@ -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; diff --git a/target/linux/generic/patches-3.0/333-mips-kexec-get-kernel-parameters-from-kexec-tools.patch b/target/linux/generic/patches-3.0/333-mips-kexec-get-kernel-parameters-from-kexec-tools.patch new file mode 100644 index 0000000000..9da936314c --- /dev/null +++ b/target/linux/generic/patches-3.0/333-mips-kexec-get-kernel-parameters-from-kexec-tools.patch @@ -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); diff --git a/target/linux/generic/patches-3.0/334-mips-fix-compiling-failure-of-relocate_kernel.patch b/target/linux/generic/patches-3.0/334-mips-fix-compiling-failure-of-relocate_kernel.patch new file mode 100644 index 0000000000..46a7395d5d --- /dev/null +++ b/target/linux/generic/patches-3.0/334-mips-fix-compiling-failure-of-relocate_kernel.patch @@ -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 */ diff --git a/target/linux/generic/patches-3.0/335-mips-kexec-cleanup-kexec-tools-parameter-handling.patch b/target/linux/generic/patches-3.0/335-mips-kexec-cleanup-kexec-tools-parameter-handling.patch new file mode 100644 index 0000000000..abc89712ba --- /dev/null +++ b/target/linux/generic/patches-3.0/335-mips-kexec-cleanup-kexec-tools-parameter-handling.patch @@ -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 |