Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / arch / ppc / kernel / relocate_kernel.S
1 /*
2  * relocate_kernel.S - put the kernel image in place to boot
3  * Copyright (C) 2002-2003 Eric Biederman  <ebiederm@xmission.com>
4  *
5  * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
6  *
7  * This source code is licensed under the GNU General Public License,
8  * Version 2.  See the file COPYING for more details.
9  */
10
11 #include <asm/reg.h>
12 #include <asm/ppc_asm.h>
13 #include <asm/processor.h>
14
15 #include <asm/kexec.h>
16
17 #define PAGE_SIZE      4096 /* must be same value as in <asm/page.h> */
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
28         li      r0, 0
29
30         /*
31          * Set Machine Status Register to a known status,
32          * switch the MMU off and jump to 1: in a single step.
33          */
34
35         mr      r8, r0
36         ori     r8, r8, MSR_RI|MSR_ME
37         mtspr   SPRN_SRR1, r8
38         addi    r8, r4, 1f - relocate_new_kernel
39         mtspr   SPRN_SRR0, r8
40         sync
41         rfi
42
43 1:
44         /* from this point address translation is turned off */
45         /* and interrupts are disabled */
46
47         /* set a new stack at the bottom of our page... */
48         /* (not really needed now) */
49         addi    r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */
50         stw     r0, 0(r1)
51
52         /* Do the copies */
53         li      r6, 0 /* checksum */
54         mr      r0, r3
55         b       1f
56
57 0:      /* top, read another word for the indirection page */
58         lwzu    r0, 4(r3)
59
60 1:
61         /* is it a destination page? (r8) */
62         rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */
63         beq     2f
64
65         rlwinm  r8, r0, 0, 0, 19 /* clear kexec flags, page align */
66         b       0b
67
68 2:      /* is it an indirection page? (r3) */
69         rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */
70         beq     2f
71
72         rlwinm  r3, r0, 0, 0, 19 /* clear kexec flags, page align */
73         subi    r3, r3, 4
74         b       0b
75
76 2:      /* are we done? */
77         rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */
78         beq     2f
79         b       3f
80
81 2:      /* is it a source page? (r9) */
82         rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */
83         beq     0b
84
85         rlwinm  r9, r0, 0, 0, 19 /* clear kexec flags, page align */
86
87         li      r7, PAGE_SIZE / 4
88         mtctr   r7
89         subi    r9, r9, 4
90         subi    r8, r8, 4
91 9:
92         lwzu    r0, 4(r9)  /* do the copy */
93         xor     r6, r6, r0
94         stwu    r0, 4(r8)
95         dcbst   0, r8
96         sync
97         icbi    0, r8
98         bdnz    9b
99
100         addi    r9, r9, 4
101         addi    r8, r8, 4
102         b       0b
103
104 3:
105
106         /* To be certain of avoiding problems with self-modifying code
107          * execute a serializing instruction here.
108          */
109         isync
110         sync
111
112         /* jump to the entry point, usually the setup routine */
113         mtlr    r5
114         blrl
115
116 1:      b       1b
117
118 relocate_new_kernel_end:
119
120         .globl relocate_new_kernel_size
121 relocate_new_kernel_size:
122         .long relocate_new_kernel_end - relocate_new_kernel
123