machine_kexec.c 3.45 KB
Newer Older
1
2
3
4
5
6
7
8
/*
 * machine_kexec.c - handle transition of Linux booting another kernel
 */

#include <linux/mm.h>
#include <linux/kexec.h>
#include <linux/delay.h>
#include <linux/reboot.h>
9
#include <linux/io.h>
10
#include <linux/irq.h>
11
12
13
14
15
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
#include <asm/mach-types.h>
16
#include <asm/system.h>
17

18
19
extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
20
21
22
23

extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
extern unsigned long kexec_mach_type;
24
extern unsigned long kexec_boot_atags;
25

26
27
static atomic_t waiting_for_crash_ipi;

28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
 * Provide a dummy crash_notes definition while crash dump arrives to arm.
 * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
 */

int machine_kexec_prepare(struct kimage *image)
{
	return 0;
}

void machine_kexec_cleanup(struct kimage *image)
{
}

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
void machine_crash_nonpanic_core(void *unused)
{
	struct pt_regs regs;

	crash_setup_regs(&regs, NULL);
	printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n",
	       smp_processor_id());
	crash_save_cpu(&regs, smp_processor_id());
	flush_cache_all();

	atomic_dec(&waiting_for_crash_ipi);
	while (1)
		cpu_relax();
}

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
static void machine_kexec_mask_interrupts(void)
{
	unsigned int i;
	struct irq_desc *desc;

	for_each_irq_desc(i, desc) {
		struct irq_chip *chip;

		chip = irq_desc_get_chip(desc);
		if (!chip)
			continue;

		if (chip->irq_eoi && irqd_irq_inprogress(&desc->irq_data))
			chip->irq_eoi(&desc->irq_data);

		if (chip->irq_mask)
			chip->irq_mask(&desc->irq_data);

		if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
			chip->irq_disable(&desc->irq_data);
	}
}

80
81
void machine_crash_shutdown(struct pt_regs *regs)
{
82
83
	unsigned long msecs;

84
	local_irq_disable();
85
86
87
88
89
90
91
92
93
94
95

	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
	smp_call_function(machine_crash_nonpanic_core, NULL, false);
	msecs = 1000; /* Wait at most a second for the other cpus to stop */
	while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
		mdelay(1);
		msecs--;
	}
	if (atomic_read(&waiting_for_crash_ipi) > 0)
		printk(KERN_WARNING "Non-crashing CPUs did not react to IPI\n");

96
	crash_save_cpu(regs, smp_processor_id());
97
	machine_kexec_mask_interrupts();
98
99

	printk(KERN_INFO "Loading crashdump kernel...\n");
100
101
}

102
103
104
105
106
/*
 * Function pointer to optional machine-specific reinitialization
 */
void (*kexec_reinit)(void);

107
108
void machine_kexec(struct kimage *image)
{
109
	unsigned long page_list;
110
111
112
	unsigned long reboot_code_buffer_phys;
	void *reboot_code_buffer;

113
114
115

	page_list = image->head & PAGE_MASK;

116
117
118
119
120
	/* we need both effective and real address here */
	reboot_code_buffer_phys =
	    page_to_pfn(image->control_code_page) << PAGE_SHIFT;
	reboot_code_buffer = page_address(image->control_code_page);

121
122
123
124
125
126
127
128
129
130
131
132
133
	/* Prepare parameters for reboot_code_buffer*/
	kexec_start_address = image->start;
	kexec_indirection_page = page_list;
	kexec_mach_type = machine_arch_type;
	kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;

	/* copy our kernel relocation code to the control code page */
	memcpy(reboot_code_buffer,
	       relocate_new_kernel, relocate_new_kernel_size);


	flush_icache_range((unsigned long) reboot_code_buffer,
			   (unsigned long) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE);
134
135
	printk(KERN_INFO "Bye!\n");

136
137
	if (kexec_reinit)
		kexec_reinit();
138
139

	soft_restart(reboot_code_buffer_phys);
140
}