| From: Yousong Zhou <yszhou4tech@gmail.com> | 
 | Subject: MIPS: kexec: Accept command line parameters from userspace. | 
 |  | 
 | Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com> | 
 | --- | 
 |  arch/mips/kernel/machine_kexec.c   |  153 +++++++++++++++++++++++++++++++----- | 
 |  arch/mips/kernel/machine_kexec.h   |   20 +++++ | 
 |  arch/mips/kernel/relocate_kernel.S |   21 +++-- | 
 |  3 files changed, 167 insertions(+), 27 deletions(-) | 
 |  create mode 100644 arch/mips/kernel/machine_kexec.h | 
 |  | 
 | --- a/arch/mips/kernel/machine_kexec.c | 
 | +++ b/arch/mips/kernel/machine_kexec.c | 
 | @@ -9,14 +9,11 @@ | 
 |  #include <linux/delay.h> | 
 |  #include <linux/libfdt.h> | 
 |   | 
 | +#include <asm/bootinfo.h> | 
 |  #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; | 
 | +#include <linux/uaccess.h> | 
 | +#include "machine_kexec.h" | 
 |   | 
 |  static unsigned long reboot_code_buffer; | 
 |   | 
 | @@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL | 
 |  void (*_machine_kexec_shutdown)(void) = NULL; | 
 |  void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; | 
 |   | 
 | +static void machine_kexec_print_args(void) | 
 | +{ | 
 | +	unsigned long argc = (int)kexec_args[0]; | 
 | +	int i; | 
 | + | 
 | +	pr_info("kexec_args[0] (argc): %lu\n", argc); | 
 | +	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]); | 
 | + | 
 | +	for (i = 0; i < argc; i++) { | 
 | +		pr_info("kexec_argv[%d] = %p, %s\n", | 
 | +				i, kexec_argv[i], kexec_argv[i]); | 
 | +	} | 
 | +} | 
 | + | 
 | +static void machine_kexec_init_argv(struct kimage *image) | 
 | +{ | 
 | +	void __user *buf = NULL; | 
 | +	size_t bufsz; | 
 | +	size_t size; | 
 | +	int i; | 
 | + | 
 | +	bufsz = 0; | 
 | +	for (i = 0; i < image->nr_segments; i++) { | 
 | +		struct kexec_segment *seg; | 
 | + | 
 | +		seg = &image->segment[i]; | 
 | +		if (seg->bufsz < 6) | 
 | +			continue; | 
 | + | 
 | +		if (strncmp((char *) seg->buf, "kexec ", 6)) | 
 | +			continue; | 
 | + | 
 | +		buf = seg->buf; | 
 | +		bufsz = seg->bufsz; | 
 | +		break; | 
 | +	} | 
 | + | 
 | +	if (!buf) | 
 | +		return; | 
 | + | 
 | +	size = KEXEC_COMMAND_LINE_SIZE; | 
 | +	size = min(size, bufsz); | 
 | +	if (size < bufsz) | 
 | +		pr_warn("kexec command line truncated to %zd bytes\n", size); | 
 | + | 
 | +	/* Copy to kernel space */ | 
 | +	if (copy_from_user(kexec_argv_buf, buf, size)) | 
 | +		pr_warn("kexec command line copy to kernel space failed\n"); | 
 | + | 
 | +	kexec_argv_buf[size - 1] = 0; | 
 | +} | 
 | + | 
 | +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_MAX_ARGC > argc)) { | 
 | +		if (*ptr == ' ') { | 
 | +			*ptr++ = '\0'; | 
 | +			continue; | 
 | +		} | 
 | + | 
 | +		kexec_argv[argc++] = ptr; | 
 | +		ptr = strchr(ptr, ' '); | 
 | +	} | 
 | + | 
 | +	if (!argc) | 
 | +		return; | 
 | + | 
 | +	kexec_args[0] = argc; | 
 | +	kexec_args[1] = (unsigned long)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 *)kexec_relocate_new_kernel; | 
 | + | 
 | +	kexec_args[1] += reloc_delta; | 
 | +	for (i = 0; i < argc; i++) | 
 | +		kexec_argv[i] += reloc_delta; | 
 | +} | 
 | + | 
 |  static void kexec_image_info(const struct kimage *kimage) | 
 |  { | 
 |  	unsigned long i; | 
 | @@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim | 
 |  #endif | 
 |   | 
 |  	kexec_image_info(kimage); | 
 | +	/* | 
 | +	 * Whenever arguments passed from kexec-tools, Init the arguments as | 
 | +	 * the original ones to try avoiding booting failure. | 
 | +	 */ | 
 | + | 
 | +	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); | 
 | @@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r | 
 |  void kexec_nonboot_cpu_jump(void) | 
 |  { | 
 |  	local_flush_icache_range((unsigned long)relocated_kexec_smp_wait, | 
 | -				 reboot_code_buffer + relocate_new_kernel_size); | 
 | +				 reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); | 
 |   | 
 |  	relocated_kexec_smp_wait(NULL); | 
 |  } | 
 | @@ -199,7 +303,7 @@ void kexec_reboot(void) | 
 |  	 * machine_kexec() CPU. | 
 |  	 */ | 
 |  	local_flush_icache_range(reboot_code_buffer, | 
 | -				 reboot_code_buffer + relocate_new_kernel_size); | 
 | +				 reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); | 
 |   | 
 |  	do_kexec = (void *)reboot_code_buffer; | 
 |  	do_kexec(); | 
 | @@ -212,10 +316,12 @@ machine_kexec(struct kimage *image) | 
 |  	unsigned long *ptr; | 
 |   | 
 |  	reboot_code_buffer = | 
 | -	  (unsigned long)page_address(image->control_code_page); | 
 | +		(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 = %p\n", (void *)kexec_start_address); | 
 |   | 
 |  	if (image->type == KEXEC_TYPE_DEFAULT) { | 
 |  		kexec_indirection_page = | 
 | @@ -223,9 +329,19 @@ machine_kexec(struct kimage *image) | 
 |  	} else { | 
 |  		kexec_indirection_page = (unsigned long)&image->head; | 
 |  	} | 
 | +	pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page); | 
 |   | 
 | -	memcpy((void*)reboot_code_buffer, relocate_new_kernel, | 
 | -	       relocate_new_kernel_size); | 
 | +	pr_info("Where is memcpy: %p\n", memcpy); | 
 | +	pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n", | 
 | +		(void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end); | 
 | +	pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE, | 
 | +		(void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer); | 
 | +	memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel, | 
 | +	       KEXEC_RELOCATE_NEW_KERNEL_SIZE); | 
 | + | 
 | +	pr_info("Before _print_args().\n"); | 
 | +	machine_kexec_print_args(); | 
 | +	pr_info("Before eval loop.\n"); | 
 |   | 
 |  	/* | 
 |  	 * The generic kexec code builds a page list with physical | 
 | @@ -256,7 +372,7 @@ machine_kexec(struct kimage *image) | 
 |  #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); | 
 | +		(void *)(kexec_smp_wait - kexec_relocate_new_kernel); | 
 |  	smp_wmb(); | 
 |  	atomic_set(&kexec_ready_to_reboot, 1); | 
 |  #endif | 
 | --- /dev/null | 
 | +++ b/arch/mips/kernel/machine_kexec.h | 
 | @@ -0,0 +1,20 @@ | 
 | +#ifndef _MACHINE_KEXEC_H | 
 | +#define _MACHINE_KEXEC_H | 
 | + | 
 | +#ifndef __ASSEMBLY__ | 
 | +extern const unsigned char kexec_relocate_new_kernel[]; | 
 | +extern unsigned long kexec_relocate_new_kernel_end; | 
 | +extern unsigned long kexec_start_address; | 
 | +extern unsigned long kexec_indirection_page; | 
 | + | 
 | +extern char kexec_argv_buf[]; | 
 | +extern char *kexec_argv[]; | 
 | + | 
 | +#define KEXEC_RELOCATE_NEW_KERNEL_SIZE	((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel) | 
 | +#endif /* !__ASSEMBLY__ */ | 
 | + | 
 | +#define KEXEC_COMMAND_LINE_SIZE		256 | 
 | +#define KEXEC_ARGV_SIZE			(KEXEC_COMMAND_LINE_SIZE / 16) | 
 | +#define KEXEC_MAX_ARGC			(KEXEC_ARGV_SIZE / sizeof(long)) | 
 | + | 
 | +#endif | 
 | --- a/arch/mips/kernel/relocate_kernel.S | 
 | +++ b/arch/mips/kernel/relocate_kernel.S | 
 | @@ -10,8 +10,9 @@ | 
 |  #include <asm/mipsregs.h> | 
 |  #include <asm/stackframe.h> | 
 |  #include <asm/addrspace.h> | 
 | +#include "machine_kexec.h" | 
 |   | 
 | -LEAF(relocate_new_kernel) | 
 | +LEAF(kexec_relocate_new_kernel) | 
 |  	PTR_L a0,	arg0 | 
 |  	PTR_L a1,	arg1 | 
 |  	PTR_L a2,	arg2 | 
 | @@ -96,7 +97,7 @@ done: | 
 |  #endif | 
 |  	/* jump to kexec_start_address */ | 
 |  	j		s1 | 
 | -	END(relocate_new_kernel) | 
 | +	END(kexec_relocate_new_kernel) | 
 |   | 
 |  #ifdef CONFIG_SMP | 
 |  /* | 
 | @@ -182,9 +183,15 @@ kexec_indirection_page: | 
 |  	PTR		0 | 
 |  	.size		kexec_indirection_page, PTRSIZE | 
 |   | 
 | -relocate_new_kernel_end: | 
 | +kexec_argv_buf: | 
 | +	EXPORT(kexec_argv_buf) | 
 | +	.skip		KEXEC_COMMAND_LINE_SIZE | 
 | +	.size		kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE | 
 | + | 
 | +kexec_argv: | 
 | +	EXPORT(kexec_argv) | 
 | +	.skip		KEXEC_ARGV_SIZE | 
 | +	.size		kexec_argv, KEXEC_ARGV_SIZE | 
 |   | 
 | -relocate_new_kernel_size: | 
 | -	EXPORT(relocate_new_kernel_size) | 
 | -	PTR		relocate_new_kernel_end - relocate_new_kernel | 
 | -	.size		relocate_new_kernel_size, PTRSIZE | 
 | +kexec_relocate_new_kernel_end: | 
 | +	EXPORT(kexec_relocate_new_kernel_end) |