diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-03-29 15:48:23 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2012-05-25 12:48:25 -0400 |
commit | fc0c49f5db640b9dfc7bb801892b5cbb7508a76a (patch) | |
tree | fc2ba1a5353385bf0f4e628ec107588b9e517e61 /arch/tile/kernel | |
parent | cd6f32aa088f4d328e676c35f51b440f2fe5b98c (diff) |
arch/tile: support kexec() for tilegx
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r-- | arch/tile/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/tile/kernel/machine_kexec.c | 35 | ||||
-rw-r--r-- | arch/tile/kernel/relocate_kernel_32.S (renamed from arch/tile/kernel/relocate_kernel.S) | 0 | ||||
-rw-r--r-- | arch/tile/kernel/relocate_kernel_64.S | 260 |
4 files changed, 288 insertions, 9 deletions
diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile index d6261e4670fe..f19116df2c62 100644 --- a/arch/tile/kernel/Makefile +++ b/arch/tile/kernel/Makefile | |||
@@ -13,5 +13,5 @@ obj-$(CONFIG_COMPAT) += compat.o compat_signal.o | |||
13 | obj-$(CONFIG_SMP) += smpboot.o smp.o tlb.o | 13 | obj-$(CONFIG_SMP) += smpboot.o smp.o tlb.o |
14 | obj-$(CONFIG_MODULES) += module.o | 14 | obj-$(CONFIG_MODULES) += module.o |
15 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 15 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
16 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 16 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel_$(BITS).o |
17 | obj-$(CONFIG_PCI) += pci.o | 17 | obj-$(CONFIG_PCI) += pci.o |
diff --git a/arch/tile/kernel/machine_kexec.c b/arch/tile/kernel/machine_kexec.c index b0fa37c1a521..f0b54a934712 100644 --- a/arch/tile/kernel/machine_kexec.c +++ b/arch/tile/kernel/machine_kexec.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <asm/pgalloc.h> | 31 | #include <asm/pgalloc.h> |
32 | #include <asm/cacheflush.h> | 32 | #include <asm/cacheflush.h> |
33 | #include <asm/checksum.h> | 33 | #include <asm/checksum.h> |
34 | #include <asm/tlbflush.h> | ||
35 | #include <asm/homecache.h> | ||
34 | #include <hv/hypervisor.h> | 36 | #include <hv/hypervisor.h> |
35 | 37 | ||
36 | 38 | ||
@@ -222,11 +224,22 @@ struct page *kimage_alloc_pages_arch(gfp_t gfp_mask, unsigned int order) | |||
222 | return alloc_pages_node(0, gfp_mask, order); | 224 | return alloc_pages_node(0, gfp_mask, order); |
223 | } | 225 | } |
224 | 226 | ||
227 | /* | ||
228 | * Address range in which pa=va mapping is set in setup_quasi_va_is_pa(). | ||
229 | * For tilepro, PAGE_OFFSET is used since this is the largest possbile value | ||
230 | * for tilepro, while for tilegx, we limit it to entire middle level page | ||
231 | * table which we assume has been allocated and is undoubtedly large enough. | ||
232 | */ | ||
233 | #ifndef __tilegx__ | ||
234 | #define QUASI_VA_IS_PA_ADDR_RANGE PAGE_OFFSET | ||
235 | #else | ||
236 | #define QUASI_VA_IS_PA_ADDR_RANGE PGDIR_SIZE | ||
237 | #endif | ||
238 | |||
225 | static void setup_quasi_va_is_pa(void) | 239 | static void setup_quasi_va_is_pa(void) |
226 | { | 240 | { |
227 | HV_PTE *pgtable; | ||
228 | HV_PTE pte; | 241 | HV_PTE pte; |
229 | int i; | 242 | unsigned long i; |
230 | 243 | ||
231 | /* | 244 | /* |
232 | * Flush our TLB to prevent conflicts between the previous contents | 245 | * Flush our TLB to prevent conflicts between the previous contents |
@@ -234,16 +247,22 @@ static void setup_quasi_va_is_pa(void) | |||
234 | */ | 247 | */ |
235 | local_flush_tlb_all(); | 248 | local_flush_tlb_all(); |
236 | 249 | ||
237 | /* setup VA is PA, at least up to PAGE_OFFSET */ | 250 | /* |
238 | 251 | * setup VA is PA, at least up to QUASI_VA_IS_PA_ADDR_RANGE. | |
239 | pgtable = (HV_PTE *)current->mm->pgd; | 252 | * Note here we assume that level-1 page table is defined by |
253 | * HPAGE_SIZE. | ||
254 | */ | ||
240 | pte = hv_pte(_PAGE_KERNEL | _PAGE_HUGE_PAGE); | 255 | pte = hv_pte(_PAGE_KERNEL | _PAGE_HUGE_PAGE); |
241 | pte = hv_pte_set_mode(pte, HV_PTE_MODE_CACHE_NO_L3); | 256 | pte = hv_pte_set_mode(pte, HV_PTE_MODE_CACHE_NO_L3); |
242 | 257 | for (i = 0; i < (QUASI_VA_IS_PA_ADDR_RANGE >> HPAGE_SHIFT); i++) { | |
243 | for (i = 0; i < pgd_index(PAGE_OFFSET); i++) { | 258 | unsigned long vaddr = i << HPAGE_SHIFT; |
259 | pgd_t *pgd = pgd_offset(current->mm, vaddr); | ||
260 | pud_t *pud = pud_offset(pgd, vaddr); | ||
261 | pte_t *ptep = (pte_t *) pmd_offset(pud, vaddr); | ||
244 | unsigned long pfn = i << (HPAGE_SHIFT - PAGE_SHIFT); | 262 | unsigned long pfn = i << (HPAGE_SHIFT - PAGE_SHIFT); |
263 | |||
245 | if (pfn_valid(pfn)) | 264 | if (pfn_valid(pfn)) |
246 | __set_pte(&pgtable[i], pfn_pte(pfn, pte)); | 265 | __set_pte(ptep, pfn_pte(pfn, pte)); |
247 | } | 266 | } |
248 | } | 267 | } |
249 | 268 | ||
diff --git a/arch/tile/kernel/relocate_kernel.S b/arch/tile/kernel/relocate_kernel_32.S index 010b418515f8..010b418515f8 100644 --- a/arch/tile/kernel/relocate_kernel.S +++ b/arch/tile/kernel/relocate_kernel_32.S | |||
diff --git a/arch/tile/kernel/relocate_kernel_64.S b/arch/tile/kernel/relocate_kernel_64.S new file mode 100644 index 000000000000..1c09a4f5a4ea --- /dev/null +++ b/arch/tile/kernel/relocate_kernel_64.S | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * copy new kernel into place and then call hv_reexec | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | #include <arch/chip.h> | ||
20 | #include <asm/page.h> | ||
21 | #include <hv/hypervisor.h> | ||
22 | |||
23 | #undef RELOCATE_NEW_KERNEL_VERBOSE | ||
24 | |||
25 | STD_ENTRY(relocate_new_kernel) | ||
26 | |||
27 | move r30, r0 /* page list */ | ||
28 | move r31, r1 /* address of page we are on */ | ||
29 | move r32, r2 /* start address of new kernel */ | ||
30 | |||
31 | shrui r1, r1, PAGE_SHIFT | ||
32 | addi r1, r1, 1 | ||
33 | shli sp, r1, PAGE_SHIFT | ||
34 | addi sp, sp, -8 | ||
35 | /* we now have a stack (whether we need one or not) */ | ||
36 | |||
37 | moveli r40, hw2_last(hv_console_putc) | ||
38 | shl16insli r40, r40, hw1(hv_console_putc) | ||
39 | shl16insli r40, r40, hw0(hv_console_putc) | ||
40 | |||
41 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE | ||
42 | moveli r0, 'r' | ||
43 | jalr r40 | ||
44 | |||
45 | moveli r0, '_' | ||
46 | jalr r40 | ||
47 | |||
48 | moveli r0, 'n' | ||
49 | jalr r40 | ||
50 | |||
51 | moveli r0, '_' | ||
52 | jalr r40 | ||
53 | |||
54 | moveli r0, 'k' | ||
55 | jalr r40 | ||
56 | |||
57 | moveli r0, '\n' | ||
58 | jalr r40 | ||
59 | #endif | ||
60 | |||
61 | /* | ||
62 | * Throughout this code r30 is pointer to the element of page | ||
63 | * list we are working on. | ||
64 | * | ||
65 | * Normally we get to the next element of the page list by | ||
66 | * incrementing r30 by eight. The exception is if the element | ||
67 | * on the page list is an IND_INDIRECTION in which case we use | ||
68 | * the element with the low bits masked off as the new value | ||
69 | * of r30. | ||
70 | * | ||
71 | * To get this started, we need the value passed to us (which | ||
72 | * will always be an IND_INDIRECTION) in memory somewhere with | ||
73 | * r30 pointing at it. To do that, we push the value passed | ||
74 | * to us on the stack and make r30 point to it. | ||
75 | */ | ||
76 | |||
77 | st sp, r30 | ||
78 | move r30, sp | ||
79 | addi sp, sp, -16 | ||
80 | |||
81 | #if CHIP_HAS_CBOX_HOME_MAP() | ||
82 | /* | ||
83 | * On TILE-GX, we need to flush all tiles' caches, since we may | ||
84 | * have been doing hash-for-home caching there. Note that we | ||
85 | * must do this _after_ we're completely done modifying any memory | ||
86 | * other than our output buffer (which we know is locally cached). | ||
87 | * We want the caches to be fully clean when we do the reexec, | ||
88 | * because the hypervisor is going to do this flush again at that | ||
89 | * point, and we don't want that second flush to overwrite any memory. | ||
90 | */ | ||
91 | { | ||
92 | move r0, zero /* cache_pa */ | ||
93 | moveli r1, hw2_last(HV_FLUSH_EVICT_L2) | ||
94 | } | ||
95 | { | ||
96 | shl16insli r1, r1, hw1(HV_FLUSH_EVICT_L2) | ||
97 | movei r2, -1 /* cache_cpumask; -1 means all client tiles */ | ||
98 | } | ||
99 | { | ||
100 | shl16insli r1, r1, hw0(HV_FLUSH_EVICT_L2) /* cache_control */ | ||
101 | move r3, zero /* tlb_va */ | ||
102 | } | ||
103 | { | ||
104 | move r4, zero /* tlb_length */ | ||
105 | move r5, zero /* tlb_pgsize */ | ||
106 | } | ||
107 | { | ||
108 | move r6, zero /* tlb_cpumask */ | ||
109 | move r7, zero /* asids */ | ||
110 | } | ||
111 | { | ||
112 | moveli r20, hw2_last(hv_flush_remote) | ||
113 | move r8, zero /* asidcount */ | ||
114 | } | ||
115 | shl16insli r20, r20, hw1(hv_flush_remote) | ||
116 | shl16insli r20, r20, hw0(hv_flush_remote) | ||
117 | |||
118 | jalr r20 | ||
119 | #endif | ||
120 | |||
121 | /* r33 is destination pointer, default to zero */ | ||
122 | |||
123 | moveli r33, 0 | ||
124 | |||
125 | .Lloop: ld r10, r30 | ||
126 | |||
127 | andi r9, r10, 0xf /* low 4 bits tell us what type it is */ | ||
128 | xor r10, r10, r9 /* r10 is now value with low 4 bits stripped */ | ||
129 | |||
130 | cmpeqi r0, r9, 0x1 /* IND_DESTINATION */ | ||
131 | beqzt r0, .Ltry2 | ||
132 | |||
133 | move r33, r10 | ||
134 | |||
135 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE | ||
136 | moveli r0, 'd' | ||
137 | jalr r40 | ||
138 | #endif | ||
139 | |||
140 | addi r30, r30, 8 | ||
141 | j .Lloop | ||
142 | |||
143 | .Ltry2: | ||
144 | cmpeqi r0, r9, 0x2 /* IND_INDIRECTION */ | ||
145 | beqzt r0, .Ltry4 | ||
146 | |||
147 | move r30, r10 | ||
148 | |||
149 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE | ||
150 | moveli r0, 'i' | ||
151 | jalr r40 | ||
152 | #endif | ||
153 | |||
154 | j .Lloop | ||
155 | |||
156 | .Ltry4: | ||
157 | cmpeqi r0, r9, 0x4 /* IND_DONE */ | ||
158 | beqzt r0, .Ltry8 | ||
159 | |||
160 | mf | ||
161 | |||
162 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE | ||
163 | moveli r0, 'D' | ||
164 | jalr r40 | ||
165 | moveli r0, '\n' | ||
166 | jalr r40 | ||
167 | #endif | ||
168 | |||
169 | move r0, r32 | ||
170 | |||
171 | moveli r41, hw2_last(hv_reexec) | ||
172 | shl16insli r41, r41, hw1(hv_reexec) | ||
173 | shl16insli r41, r41, hw0(hv_reexec) | ||
174 | |||
175 | jalr r41 | ||
176 | |||
177 | /* we should not get here */ | ||
178 | |||
179 | moveli r0, '?' | ||
180 | jalr r40 | ||
181 | moveli r0, '\n' | ||
182 | jalr r40 | ||
183 | |||
184 | j .Lhalt | ||
185 | |||
186 | .Ltry8: cmpeqi r0, r9, 0x8 /* IND_SOURCE */ | ||
187 | beqz r0, .Lerr /* unknown type */ | ||
188 | |||
189 | /* copy page at r10 to page at r33 */ | ||
190 | |||
191 | move r11, r33 | ||
192 | |||
193 | moveli r0, hw2_last(PAGE_SIZE) | ||
194 | shl16insli r0, r0, hw1(PAGE_SIZE) | ||
195 | shl16insli r0, r0, hw0(PAGE_SIZE) | ||
196 | add r33, r33, r0 | ||
197 | |||
198 | /* copy word at r10 to word at r11 until r11 equals r33 */ | ||
199 | |||
200 | /* We know page size must be multiple of 8, so we can unroll | ||
201 | * 8 times safely without any edge case checking. | ||
202 | * | ||
203 | * Issue a flush of the destination every 8 words to avoid | ||
204 | * incoherence when starting the new kernel. (Now this is | ||
205 | * just good paranoia because the hv_reexec call will also | ||
206 | * take care of this.) | ||
207 | */ | ||
208 | |||
209 | 1: | ||
210 | { ld r0, r10; addi r10, r10, 8 } | ||
211 | { st r11, r0; addi r11, r11, 8 } | ||
212 | { ld r0, r10; addi r10, r10, 8 } | ||
213 | { st r11, r0; addi r11, r11, 8 } | ||
214 | { ld r0, r10; addi r10, r10, 8 } | ||
215 | { st r11, r0; addi r11, r11, 8 } | ||
216 | { ld r0, r10; addi r10, r10, 8 } | ||
217 | { st r11, r0; addi r11, r11, 8 } | ||
218 | { ld r0, r10; addi r10, r10, 8 } | ||
219 | { st r11, r0; addi r11, r11, 8 } | ||
220 | { ld r0, r10; addi r10, r10, 8 } | ||
221 | { st r11, r0; addi r11, r11, 8 } | ||
222 | { ld r0, r10; addi r10, r10, 8 } | ||
223 | { st r11, r0; addi r11, r11, 8 } | ||
224 | { ld r0, r10; addi r10, r10, 8 } | ||
225 | { st r11, r0 } | ||
226 | { flush r11 ; addi r11, r11, 8 } | ||
227 | |||
228 | cmpeq r0, r33, r11 | ||
229 | beqzt r0, 1b | ||
230 | |||
231 | #ifdef RELOCATE_NEW_KERNEL_VERBOSE | ||
232 | moveli r0, 's' | ||
233 | jalr r40 | ||
234 | #endif | ||
235 | |||
236 | addi r30, r30, 8 | ||
237 | j .Lloop | ||
238 | |||
239 | |||
240 | .Lerr: moveli r0, 'e' | ||
241 | jalr r40 | ||
242 | moveli r0, 'r' | ||
243 | jalr r40 | ||
244 | moveli r0, 'r' | ||
245 | jalr r40 | ||
246 | moveli r0, '\n' | ||
247 | jalr r40 | ||
248 | .Lhalt: | ||
249 | moveli r41, hw2_last(hv_halt) | ||
250 | shl16insli r41, r41, hw1(hv_halt) | ||
251 | shl16insli r41, r41, hw0(hv_halt) | ||
252 | |||
253 | jalr r41 | ||
254 | STD_ENDPROC(relocate_new_kernel) | ||
255 | |||
256 | .section .rodata,"a" | ||
257 | |||
258 | .globl relocate_new_kernel_size | ||
259 | relocate_new_kernel_size: | ||
260 | .long .Lend_relocate_new_kernel - relocate_new_kernel | ||