Commit | Line | Data |
---|---|---|
9f7bd920 CL |
1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* | |
3 | * This file contains kexec low-level functions. | |
4 | * | |
5 | * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> | |
6 | * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz | |
7 | * PPC44x port. Copyright (C) 2011, IBM Corporation | |
8 | * Author: Suzuki Poulose <suzuki@in.ibm.com> | |
9 | */ | |
10 | ||
11 | #include <asm/reg.h> | |
12 | #include <asm/page.h> | |
13 | #include <asm/mmu.h> | |
14 | #include <asm/ppc_asm.h> | |
15 | #include <asm/kexec.h> | |
16 | ||
17 | .text | |
18 | ||
19 | /* | |
20 | * Must be relocatable PIC code callable as a C function. | |
21 | */ | |
22 | .globl relocate_new_kernel | |
23 | relocate_new_kernel: | |
24 | /* r3 = page_list */ | |
25 | /* r4 = reboot_code_buffer */ | |
26 | /* r5 = start_address */ | |
27 | ||
dfc3095c | 28 | #ifdef CONFIG_PPC_85xx |
9f7bd920 CL |
29 | |
30 | mr r29, r3 | |
31 | mr r30, r4 | |
32 | mr r31, r5 | |
33 | ||
34 | #define ENTRY_MAPPING_KEXEC_SETUP | |
dfc3095c | 35 | #include <kernel/85xx_entry_mapping.S> |
9f7bd920 CL |
36 | #undef ENTRY_MAPPING_KEXEC_SETUP |
37 | ||
38 | mr r3, r29 | |
39 | mr r4, r30 | |
40 | mr r5, r31 | |
41 | ||
42 | li r0, 0 | |
43 | #elif defined(CONFIG_44x) | |
44 | ||
45 | /* Save our parameters */ | |
46 | mr r29, r3 | |
47 | mr r30, r4 | |
48 | mr r31, r5 | |
49 | ||
50 | #ifdef CONFIG_PPC_47x | |
51 | /* Check for 47x cores */ | |
52 | mfspr r3,SPRN_PVR | |
53 | srwi r3,r3,16 | |
54 | cmplwi cr0,r3,PVR_476FPE@h | |
55 | beq setup_map_47x | |
56 | cmplwi cr0,r3,PVR_476@h | |
57 | beq setup_map_47x | |
58 | cmplwi cr0,r3,PVR_476_ISS@h | |
59 | beq setup_map_47x | |
60 | #endif /* CONFIG_PPC_47x */ | |
61 | ||
62 | /* | |
63 | * Code for setting up 1:1 mapping for PPC440x for KEXEC | |
64 | * | |
65 | * We cannot switch off the MMU on PPC44x. | |
66 | * So we: | |
67 | * 1) Invalidate all the mappings except the one we are running from. | |
68 | * 2) Create a tmp mapping for our code in the other address space(TS) and | |
69 | * jump to it. Invalidate the entry we started in. | |
70 | * 3) Create a 1:1 mapping for 0-2GiB in chunks of 256M in original TS. | |
71 | * 4) Jump to the 1:1 mapping in original TS. | |
72 | * 5) Invalidate the tmp mapping. | |
73 | * | |
74 | * - Based on the kexec support code for FSL BookE | |
75 | * | |
76 | */ | |
77 | ||
78 | /* | |
79 | * Load the PID with kernel PID (0). | |
80 | * Also load our MSR_IS and TID to MMUCR for TLB search. | |
81 | */ | |
82 | li r3, 0 | |
83 | mtspr SPRN_PID, r3 | |
84 | mfmsr r4 | |
85 | andi. r4,r4,MSR_IS@l | |
86 | beq wmmucr | |
87 | oris r3,r3,PPC44x_MMUCR_STS@h | |
88 | wmmucr: | |
89 | mtspr SPRN_MMUCR,r3 | |
90 | sync | |
91 | ||
92 | /* | |
93 | * Invalidate all the TLB entries except the current entry | |
94 | * where we are running from | |
95 | */ | |
33e14024 | 96 | bcl 20,31,$+4 /* Find our address */ |
9f7bd920 CL |
97 | 0: mflr r5 /* Make it accessible */ |
98 | tlbsx r23,0,r5 /* Find entry we are in */ | |
99 | li r4,0 /* Start at TLB entry 0 */ | |
100 | li r3,0 /* Set PAGEID inval value */ | |
101 | 1: cmpw r23,r4 /* Is this our entry? */ | |
102 | beq skip /* If so, skip the inval */ | |
103 | tlbwe r3,r4,PPC44x_TLB_PAGEID /* If not, inval the entry */ | |
104 | skip: | |
105 | addi r4,r4,1 /* Increment */ | |
106 | cmpwi r4,64 /* Are we done? */ | |
107 | bne 1b /* If not, repeat */ | |
108 | isync | |
109 | ||
110 | /* Create a temp mapping and jump to it */ | |
111 | andi. r6, r23, 1 /* Find the index to use */ | |
112 | addi r24, r6, 1 /* r24 will contain 1 or 2 */ | |
113 | ||
114 | mfmsr r9 /* get the MSR */ | |
115 | rlwinm r5, r9, 27, 31, 31 /* Extract the MSR[IS] */ | |
116 | xori r7, r5, 1 /* Use the other address space */ | |
117 | ||
118 | /* Read the current mapping entries */ | |
119 | tlbre r3, r23, PPC44x_TLB_PAGEID | |
120 | tlbre r4, r23, PPC44x_TLB_XLAT | |
121 | tlbre r5, r23, PPC44x_TLB_ATTRIB | |
122 | ||
123 | /* Save our current XLAT entry */ | |
124 | mr r25, r4 | |
125 | ||
126 | /* Extract the TLB PageSize */ | |
127 | li r10, 1 /* r10 will hold PageSize */ | |
128 | rlwinm r11, r3, 0, 24, 27 /* bits 24-27 */ | |
129 | ||
130 | /* XXX: As of now we use 256M, 4K pages */ | |
131 | cmpwi r11, PPC44x_TLB_256M | |
132 | bne tlb_4k | |
133 | rotlwi r10, r10, 28 /* r10 = 256M */ | |
134 | b write_out | |
135 | tlb_4k: | |
136 | cmpwi r11, PPC44x_TLB_4K | |
137 | bne default | |
138 | rotlwi r10, r10, 12 /* r10 = 4K */ | |
139 | b write_out | |
140 | default: | |
141 | rotlwi r10, r10, 10 /* r10 = 1K */ | |
142 | ||
143 | write_out: | |
144 | /* | |
145 | * Write out the tmp 1:1 mapping for this code in other address space | |
146 | * Fixup EPN = RPN , TS=other address space | |
147 | */ | |
148 | insrwi r3, r7, 1, 23 /* Bit 23 is TS for PAGEID field */ | |
149 | ||
150 | /* Write out the tmp mapping entries */ | |
151 | tlbwe r3, r24, PPC44x_TLB_PAGEID | |
152 | tlbwe r4, r24, PPC44x_TLB_XLAT | |
153 | tlbwe r5, r24, PPC44x_TLB_ATTRIB | |
154 | ||
155 | subi r11, r10, 1 /* PageOffset Mask = PageSize - 1 */ | |
156 | not r10, r11 /* Mask for PageNum */ | |
157 | ||
158 | /* Switch to other address space in MSR */ | |
159 | insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ | |
160 | ||
33e14024 | 161 | bcl 20,31,$+4 |
9f7bd920 CL |
162 | 1: mflr r8 |
163 | addi r8, r8, (2f-1b) /* Find the target offset */ | |
164 | ||
165 | /* Jump to the tmp mapping */ | |
166 | mtspr SPRN_SRR0, r8 | |
167 | mtspr SPRN_SRR1, r9 | |
168 | rfi | |
169 | ||
170 | 2: | |
171 | /* Invalidate the entry we were executing from */ | |
172 | li r3, 0 | |
173 | tlbwe r3, r23, PPC44x_TLB_PAGEID | |
174 | ||
175 | /* attribute fields. rwx for SUPERVISOR mode */ | |
176 | li r5, 0 | |
177 | ori r5, r5, (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G) | |
178 | ||
179 | /* Create 1:1 mapping in 256M pages */ | |
180 | xori r7, r7, 1 /* Revert back to Original TS */ | |
181 | ||
182 | li r8, 0 /* PageNumber */ | |
183 | li r6, 3 /* TLB Index, start at 3 */ | |
184 | ||
185 | next_tlb: | |
186 | rotlwi r3, r8, 28 /* Create EPN (bits 0-3) */ | |
187 | mr r4, r3 /* RPN = EPN */ | |
188 | ori r3, r3, (PPC44x_TLB_VALID | PPC44x_TLB_256M) /* SIZE = 256M, Valid */ | |
189 | insrwi r3, r7, 1, 23 /* Set TS from r7 */ | |
190 | ||
191 | tlbwe r3, r6, PPC44x_TLB_PAGEID /* PageID field : EPN, V, SIZE */ | |
192 | tlbwe r4, r6, PPC44x_TLB_XLAT /* Address translation : RPN */ | |
193 | tlbwe r5, r6, PPC44x_TLB_ATTRIB /* Attributes */ | |
194 | ||
195 | addi r8, r8, 1 /* Increment PN */ | |
196 | addi r6, r6, 1 /* Increment TLB Index */ | |
197 | cmpwi r8, 8 /* Are we done ? */ | |
198 | bne next_tlb | |
199 | isync | |
200 | ||
201 | /* Jump to the new mapping 1:1 */ | |
202 | li r9,0 | |
203 | insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ | |
204 | ||
33e14024 | 205 | bcl 20,31,$+4 |
9f7bd920 CL |
206 | 1: mflr r8 |
207 | and r8, r8, r11 /* Get our offset within page */ | |
208 | addi r8, r8, (2f-1b) | |
209 | ||
210 | and r5, r25, r10 /* Get our target PageNum */ | |
211 | or r8, r8, r5 /* Target jump address */ | |
212 | ||
213 | mtspr SPRN_SRR0, r8 | |
214 | mtspr SPRN_SRR1, r9 | |
215 | rfi | |
216 | 2: | |
217 | /* Invalidate the tmp entry we used */ | |
218 | li r3, 0 | |
219 | tlbwe r3, r24, PPC44x_TLB_PAGEID | |
220 | sync | |
221 | b ppc44x_map_done | |
222 | ||
223 | #ifdef CONFIG_PPC_47x | |
224 | ||
225 | /* 1:1 mapping for 47x */ | |
226 | ||
227 | setup_map_47x: | |
228 | ||
229 | /* | |
230 | * Load the kernel pid (0) to PID and also to MMUCR[TID]. | |
231 | * Also set the MSR IS->MMUCR STS | |
232 | */ | |
233 | li r3, 0 | |
234 | mtspr SPRN_PID, r3 /* Set PID */ | |
235 | mfmsr r4 /* Get MSR */ | |
236 | andi. r4, r4, MSR_IS@l /* TS=1? */ | |
237 | beq 1f /* If not, leave STS=0 */ | |
238 | oris r3, r3, PPC47x_MMUCR_STS@h /* Set STS=1 */ | |
239 | 1: mtspr SPRN_MMUCR, r3 /* Put MMUCR */ | |
240 | sync | |
241 | ||
242 | /* Find the entry we are running from */ | |
33e14024 | 243 | bcl 20,31,$+4 |
9f7bd920 CL |
244 | 2: mflr r23 |
245 | tlbsx r23, 0, r23 | |
246 | tlbre r24, r23, 0 /* TLB Word 0 */ | |
247 | tlbre r25, r23, 1 /* TLB Word 1 */ | |
248 | tlbre r26, r23, 2 /* TLB Word 2 */ | |
249 | ||
250 | ||
251 | /* | |
252 | * Invalidates all the tlb entries by writing to 256 RPNs(r4) | |
253 | * of 4k page size in all 4 ways (0-3 in r3). | |
254 | * This would invalidate the entire UTLB including the one we are | |
255 | * running from. However the shadow TLB entries would help us | |
256 | * to continue the execution, until we flush them (rfi/isync). | |
257 | */ | |
258 | addis r3, 0, 0x8000 /* specify the way */ | |
259 | addi r4, 0, 0 /* TLB Word0 = (EPN=0, VALID = 0) */ | |
260 | addi r5, 0, 0 | |
261 | b clear_utlb_entry | |
262 | ||
263 | /* Align the loop to speed things up. from head_44x.S */ | |
264 | .align 6 | |
265 | ||
266 | clear_utlb_entry: | |
267 | ||
268 | tlbwe r4, r3, 0 | |
269 | tlbwe r5, r3, 1 | |
270 | tlbwe r5, r3, 2 | |
271 | addis r3, r3, 0x2000 /* Increment the way */ | |
272 | cmpwi r3, 0 | |
273 | bne clear_utlb_entry | |
274 | addis r3, 0, 0x8000 | |
275 | addis r4, r4, 0x100 /* Increment the EPN */ | |
276 | cmpwi r4, 0 | |
277 | bne clear_utlb_entry | |
278 | ||
279 | /* Create the entries in the other address space */ | |
280 | mfmsr r5 | |
281 | rlwinm r7, r5, 27, 31, 31 /* Get the TS (Bit 26) from MSR */ | |
282 | xori r7, r7, 1 /* r7 = !TS */ | |
283 | ||
284 | insrwi r24, r7, 1, 21 /* Change the TS in the saved TLB word 0 */ | |
285 | ||
286 | /* | |
287 | * write out the TLB entries for the tmp mapping | |
288 | * Use way '0' so that we could easily invalidate it later. | |
289 | */ | |
290 | lis r3, 0x8000 /* Way '0' */ | |
291 | ||
292 | tlbwe r24, r3, 0 | |
293 | tlbwe r25, r3, 1 | |
294 | tlbwe r26, r3, 2 | |
295 | ||
296 | /* Update the msr to the new TS */ | |
297 | insrwi r5, r7, 1, 26 | |
298 | ||
33e14024 | 299 | bcl 20,31,$+4 |
9f7bd920 CL |
300 | 1: mflr r6 |
301 | addi r6, r6, (2f-1b) | |
302 | ||
303 | mtspr SPRN_SRR0, r6 | |
304 | mtspr SPRN_SRR1, r5 | |
305 | rfi | |
306 | ||
307 | /* | |
308 | * Now we are in the tmp address space. | |
309 | * Create a 1:1 mapping for 0-2GiB in the original TS. | |
310 | */ | |
311 | 2: | |
312 | li r3, 0 | |
313 | li r4, 0 /* TLB Word 0 */ | |
314 | li r5, 0 /* TLB Word 1 */ | |
315 | li r6, 0 | |
316 | ori r6, r6, PPC47x_TLB2_S_RWX /* TLB word 2 */ | |
317 | ||
318 | li r8, 0 /* PageIndex */ | |
319 | ||
320 | xori r7, r7, 1 /* revert back to original TS */ | |
321 | ||
322 | write_utlb: | |
323 | rotlwi r5, r8, 28 /* RPN = PageIndex * 256M */ | |
324 | /* ERPN = 0 as we don't use memory above 2G */ | |
325 | ||
326 | mr r4, r5 /* EPN = RPN */ | |
327 | ori r4, r4, (PPC47x_TLB0_VALID | PPC47x_TLB0_256M) | |
328 | insrwi r4, r7, 1, 21 /* Insert the TS to Word 0 */ | |
329 | ||
330 | tlbwe r4, r3, 0 /* Write out the entries */ | |
331 | tlbwe r5, r3, 1 | |
332 | tlbwe r6, r3, 2 | |
333 | addi r8, r8, 1 | |
334 | cmpwi r8, 8 /* Have we completed ? */ | |
335 | bne write_utlb | |
336 | ||
337 | /* make sure we complete the TLB write up */ | |
338 | isync | |
339 | ||
340 | /* | |
341 | * Prepare to jump to the 1:1 mapping. | |
342 | * 1) Extract page size of the tmp mapping | |
343 | * DSIZ = TLB_Word0[22:27] | |
344 | * 2) Calculate the physical address of the address | |
345 | * to jump to. | |
346 | */ | |
347 | rlwinm r10, r24, 0, 22, 27 | |
348 | ||
349 | cmpwi r10, PPC47x_TLB0_4K | |
350 | bne 0f | |
351 | li r10, 0x1000 /* r10 = 4k */ | |
352 | bl 1f | |
353 | ||
354 | 0: | |
355 | /* Defaults to 256M */ | |
356 | lis r10, 0x1000 | |
357 | ||
33e14024 | 358 | bcl 20,31,$+4 |
9f7bd920 CL |
359 | 1: mflr r4 |
360 | addi r4, r4, (2f-1b) /* virtual address of 2f */ | |
361 | ||
362 | subi r11, r10, 1 /* offsetmask = Pagesize - 1 */ | |
363 | not r10, r11 /* Pagemask = ~(offsetmask) */ | |
364 | ||
365 | and r5, r25, r10 /* Physical page */ | |
366 | and r6, r4, r11 /* offset within the current page */ | |
367 | ||
368 | or r5, r5, r6 /* Physical address for 2f */ | |
369 | ||
370 | /* Switch the TS in MSR to the original one */ | |
371 | mfmsr r8 | |
372 | insrwi r8, r7, 1, 26 | |
373 | ||
374 | mtspr SPRN_SRR1, r8 | |
375 | mtspr SPRN_SRR0, r5 | |
376 | rfi | |
377 | ||
378 | 2: | |
379 | /* Invalidate the tmp mapping */ | |
380 | lis r3, 0x8000 /* Way '0' */ | |
381 | ||
382 | clrrwi r24, r24, 12 /* Clear the valid bit */ | |
383 | tlbwe r24, r3, 0 | |
384 | tlbwe r25, r3, 1 | |
385 | tlbwe r26, r3, 2 | |
386 | ||
387 | /* Make sure we complete the TLB write and flush the shadow TLB */ | |
388 | isync | |
389 | ||
390 | #endif | |
391 | ||
392 | ppc44x_map_done: | |
393 | ||
394 | ||
395 | /* Restore the parameters */ | |
396 | mr r3, r29 | |
397 | mr r4, r30 | |
398 | mr r5, r31 | |
399 | ||
400 | li r0, 0 | |
401 | #else | |
402 | li r0, 0 | |
403 | ||
404 | /* | |
405 | * Set Machine Status Register to a known status, | |
406 | * switch the MMU off and jump to 1: in a single step. | |
407 | */ | |
408 | ||
409 | mr r8, r0 | |
410 | ori r8, r8, MSR_RI|MSR_ME | |
411 | mtspr SPRN_SRR1, r8 | |
412 | addi r8, r4, 1f - relocate_new_kernel | |
413 | mtspr SPRN_SRR0, r8 | |
414 | sync | |
415 | rfi | |
416 | ||
417 | 1: | |
418 | #endif | |
419 | /* from this point address translation is turned off */ | |
420 | /* and interrupts are disabled */ | |
421 | ||
422 | /* set a new stack at the bottom of our page... */ | |
423 | /* (not really needed now) */ | |
424 | addi r1, r4, KEXEC_CONTROL_PAGE_SIZE - 8 /* for LR Save+Back Chain */ | |
425 | stw r0, 0(r1) | |
426 | ||
427 | /* Do the copies */ | |
428 | li r6, 0 /* checksum */ | |
429 | mr r0, r3 | |
430 | b 1f | |
431 | ||
432 | 0: /* top, read another word for the indirection page */ | |
433 | lwzu r0, 4(r3) | |
434 | ||
435 | 1: | |
436 | /* is it a destination page? (r8) */ | |
437 | rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */ | |
438 | beq 2f | |
439 | ||
440 | rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */ | |
441 | b 0b | |
442 | ||
443 | 2: /* is it an indirection page? (r3) */ | |
444 | rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */ | |
445 | beq 2f | |
446 | ||
447 | rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */ | |
448 | subi r3, r3, 4 | |
449 | b 0b | |
450 | ||
451 | 2: /* are we done? */ | |
452 | rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */ | |
453 | beq 2f | |
454 | b 3f | |
455 | ||
456 | 2: /* is it a source page? (r9) */ | |
457 | rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */ | |
458 | beq 0b | |
459 | ||
460 | rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */ | |
461 | ||
462 | li r7, PAGE_SIZE / 4 | |
463 | mtctr r7 | |
464 | subi r9, r9, 4 | |
465 | subi r8, r8, 4 | |
466 | 9: | |
467 | lwzu r0, 4(r9) /* do the copy */ | |
468 | xor r6, r6, r0 | |
469 | stwu r0, 4(r8) | |
470 | dcbst 0, r8 | |
471 | sync | |
472 | icbi 0, r8 | |
473 | bdnz 9b | |
474 | ||
475 | addi r9, r9, 4 | |
476 | addi r8, r8, 4 | |
477 | b 0b | |
478 | ||
479 | 3: | |
480 | ||
481 | /* To be certain of avoiding problems with self-modifying code | |
482 | * execute a serializing instruction here. | |
483 | */ | |
484 | isync | |
485 | sync | |
486 | ||
487 | mfspr r3, SPRN_PIR /* current core we are running on */ | |
488 | mr r4, r5 /* load physical address of chunk called */ | |
489 | ||
490 | /* jump to the entry point, usually the setup routine */ | |
491 | mtlr r5 | |
492 | blrl | |
493 | ||
494 | 1: b 1b | |
495 | ||
496 | relocate_new_kernel_end: | |
497 | ||
498 | .globl relocate_new_kernel_size | |
499 | relocate_new_kernel_size: | |
500 | .long relocate_new_kernel_end - relocate_new_kernel |