diff options
Diffstat (limited to 'arch/microblaze/kernel')
-rw-r--r-- | arch/microblaze/kernel/Makefile | 7 | ||||
-rw-r--r-- | arch/microblaze/kernel/cpu/mb.c | 1 | ||||
-rw-r--r-- | arch/microblaze/kernel/entry-nommu.S | 28 | ||||
-rw-r--r-- | arch/microblaze/kernel/entry.S | 607 | ||||
-rw-r--r-- | arch/microblaze/kernel/exceptions.c | 9 | ||||
-rw-r--r-- | arch/microblaze/kernel/head.S | 4 | ||||
-rw-r--r-- | arch/microblaze/kernel/hw_exception_handler.S | 4 | ||||
-rw-r--r-- | arch/microblaze/kernel/irq.c | 16 | ||||
-rw-r--r-- | arch/microblaze/kernel/kgdb.c | 147 | ||||
-rw-r--r-- | arch/microblaze/kernel/misc.S | 2 | ||||
-rw-r--r-- | arch/microblaze/kernel/of_device.c | 112 | ||||
-rw-r--r-- | arch/microblaze/kernel/of_platform.c | 200 | ||||
-rw-r--r-- | arch/microblaze/kernel/process.c | 7 | ||||
-rw-r--r-- | arch/microblaze/kernel/prom_parse.c | 877 | ||||
-rw-r--r-- | arch/microblaze/kernel/ptrace.c | 17 | ||||
-rw-r--r-- | arch/microblaze/kernel/reset.c | 12 | ||||
-rw-r--r-- | arch/microblaze/kernel/setup.c | 6 | ||||
-rw-r--r-- | arch/microblaze/kernel/stacktrace.c | 44 | ||||
-rw-r--r-- | arch/microblaze/kernel/timer.c | 24 | ||||
-rw-r--r-- | arch/microblaze/kernel/traps.c | 91 | ||||
-rw-r--r-- | arch/microblaze/kernel/unwind.c | 318 | ||||
-rw-r--r-- | arch/microblaze/kernel/vmlinux.lds.S | 16 |
22 files changed, 857 insertions, 1692 deletions
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index e51bc1520825..f0cb5c26c81c 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile | |||
@@ -15,9 +15,9 @@ endif | |||
15 | extra-y := head.o vmlinux.lds | 15 | extra-y := head.o vmlinux.lds |
16 | 16 | ||
17 | obj-y += dma.o exceptions.o \ | 17 | obj-y += dma.o exceptions.o \ |
18 | hw_exception_handler.o init_task.o intc.o irq.o of_device.o \ | 18 | hw_exception_handler.o init_task.o intc.o irq.o \ |
19 | of_platform.o process.o prom.o prom_parse.o ptrace.o \ | 19 | process.o prom.o prom_parse.o ptrace.o \ |
20 | setup.o signal.o sys_microblaze.o timer.o traps.o reset.o | 20 | reset.o setup.o signal.o sys_microblaze.o timer.o traps.o unwind.o |
21 | 21 | ||
22 | obj-y += cpu/ | 22 | obj-y += cpu/ |
23 | 23 | ||
@@ -28,5 +28,6 @@ obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o | |||
28 | obj-$(CONFIG_MMU) += misc.o | 28 | obj-$(CONFIG_MMU) += misc.o |
29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
30 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o | 30 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o |
31 | obj-$(CONFIG_KGDB) += kgdb.o | ||
31 | 32 | ||
32 | obj-y += entry$(MMU).o | 33 | obj-y += entry$(MMU).o |
diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c index 4216eb1eaa32..7086e3564281 100644 --- a/arch/microblaze/kernel/cpu/mb.c +++ b/arch/microblaze/kernel/cpu/mb.c | |||
@@ -126,6 +126,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
126 | cpuinfo.pvr_user1, | 126 | cpuinfo.pvr_user1, |
127 | cpuinfo.pvr_user2); | 127 | cpuinfo.pvr_user2); |
128 | 128 | ||
129 | count += seq_printf(m, "Page size:\t%lu\n", PAGE_SIZE); | ||
129 | return 0; | 130 | return 0; |
130 | } | 131 | } |
131 | 132 | ||
diff --git a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S index 8cc18cd2cce6..ca84368570b6 100644 --- a/arch/microblaze/kernel/entry-nommu.S +++ b/arch/microblaze/kernel/entry-nommu.S | |||
@@ -588,3 +588,31 @@ sys_rt_sigsuspend_wrapper: | |||
588 | #include "syscall_table.S" | 588 | #include "syscall_table.S" |
589 | 589 | ||
590 | syscall_table_size=(.-sys_call_table) | 590 | syscall_table_size=(.-sys_call_table) |
591 | |||
592 | type_SYSCALL: | ||
593 | .ascii "SYSCALL\0" | ||
594 | type_IRQ: | ||
595 | .ascii "IRQ\0" | ||
596 | type_IRQ_PREEMPT: | ||
597 | .ascii "IRQ (PREEMPTED)\0" | ||
598 | type_SYSCALL_PREEMPT: | ||
599 | .ascii " SYSCALL (PREEMPTED)\0" | ||
600 | |||
601 | /* | ||
602 | * Trap decoding for stack unwinder | ||
603 | * Tuples are (start addr, end addr, string) | ||
604 | * If return address lies on [start addr, end addr], | ||
605 | * unwinder displays 'string' | ||
606 | */ | ||
607 | |||
608 | .align 4 | ||
609 | .global microblaze_trap_handlers | ||
610 | microblaze_trap_handlers: | ||
611 | /* Exact matches come first */ | ||
612 | .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL | ||
613 | .word ret_from_intr; .word ret_from_intr ; .word type_IRQ | ||
614 | /* Fuzzy matches go here */ | ||
615 | .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT | ||
616 | .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT | ||
617 | /* End of table */ | ||
618 | .word 0 ; .word 0 ; .word 0 | ||
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S index c0ede25c5b99..304882e56459 100644 --- a/arch/microblaze/kernel/entry.S +++ b/arch/microblaze/kernel/entry.S | |||
@@ -48,128 +48,107 @@ | |||
48 | */ | 48 | */ |
49 | #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR | 49 | #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR |
50 | .macro clear_bip | 50 | .macro clear_bip |
51 | msrclr r11, MSR_BIP | 51 | msrclr r0, MSR_BIP |
52 | nop | ||
53 | .endm | 52 | .endm |
54 | 53 | ||
55 | .macro set_bip | 54 | .macro set_bip |
56 | msrset r11, MSR_BIP | 55 | msrset r0, MSR_BIP |
57 | nop | ||
58 | .endm | 56 | .endm |
59 | 57 | ||
60 | .macro clear_eip | 58 | .macro clear_eip |
61 | msrclr r11, MSR_EIP | 59 | msrclr r0, MSR_EIP |
62 | nop | ||
63 | .endm | 60 | .endm |
64 | 61 | ||
65 | .macro set_ee | 62 | .macro set_ee |
66 | msrset r11, MSR_EE | 63 | msrset r0, MSR_EE |
67 | nop | ||
68 | .endm | 64 | .endm |
69 | 65 | ||
70 | .macro disable_irq | 66 | .macro disable_irq |
71 | msrclr r11, MSR_IE | 67 | msrclr r0, MSR_IE |
72 | nop | ||
73 | .endm | 68 | .endm |
74 | 69 | ||
75 | .macro enable_irq | 70 | .macro enable_irq |
76 | msrset r11, MSR_IE | 71 | msrset r0, MSR_IE |
77 | nop | ||
78 | .endm | 72 | .endm |
79 | 73 | ||
80 | .macro set_ums | 74 | .macro set_ums |
81 | msrset r11, MSR_UMS | 75 | msrset r0, MSR_UMS |
82 | nop | 76 | msrclr r0, MSR_VMS |
83 | msrclr r11, MSR_VMS | ||
84 | nop | ||
85 | .endm | 77 | .endm |
86 | 78 | ||
87 | .macro set_vms | 79 | .macro set_vms |
88 | msrclr r11, MSR_UMS | 80 | msrclr r0, MSR_UMS |
89 | nop | 81 | msrset r0, MSR_VMS |
90 | msrset r11, MSR_VMS | 82 | .endm |
91 | nop | 83 | |
84 | .macro clear_ums | ||
85 | msrclr r0, MSR_UMS | ||
92 | .endm | 86 | .endm |
93 | 87 | ||
94 | .macro clear_vms_ums | 88 | .macro clear_vms_ums |
95 | msrclr r11, MSR_VMS | 89 | msrclr r0, MSR_VMS | MSR_UMS |
96 | nop | ||
97 | msrclr r11, MSR_UMS | ||
98 | nop | ||
99 | .endm | 90 | .endm |
100 | #else | 91 | #else |
101 | .macro clear_bip | 92 | .macro clear_bip |
102 | mfs r11, rmsr | 93 | mfs r11, rmsr |
103 | nop | ||
104 | andi r11, r11, ~MSR_BIP | 94 | andi r11, r11, ~MSR_BIP |
105 | mts rmsr, r11 | 95 | mts rmsr, r11 |
106 | nop | ||
107 | .endm | 96 | .endm |
108 | 97 | ||
109 | .macro set_bip | 98 | .macro set_bip |
110 | mfs r11, rmsr | 99 | mfs r11, rmsr |
111 | nop | ||
112 | ori r11, r11, MSR_BIP | 100 | ori r11, r11, MSR_BIP |
113 | mts rmsr, r11 | 101 | mts rmsr, r11 |
114 | nop | ||
115 | .endm | 102 | .endm |
116 | 103 | ||
117 | .macro clear_eip | 104 | .macro clear_eip |
118 | mfs r11, rmsr | 105 | mfs r11, rmsr |
119 | nop | ||
120 | andi r11, r11, ~MSR_EIP | 106 | andi r11, r11, ~MSR_EIP |
121 | mts rmsr, r11 | 107 | mts rmsr, r11 |
122 | nop | ||
123 | .endm | 108 | .endm |
124 | 109 | ||
125 | .macro set_ee | 110 | .macro set_ee |
126 | mfs r11, rmsr | 111 | mfs r11, rmsr |
127 | nop | ||
128 | ori r11, r11, MSR_EE | 112 | ori r11, r11, MSR_EE |
129 | mts rmsr, r11 | 113 | mts rmsr, r11 |
130 | nop | ||
131 | .endm | 114 | .endm |
132 | 115 | ||
133 | .macro disable_irq | 116 | .macro disable_irq |
134 | mfs r11, rmsr | 117 | mfs r11, rmsr |
135 | nop | ||
136 | andi r11, r11, ~MSR_IE | 118 | andi r11, r11, ~MSR_IE |
137 | mts rmsr, r11 | 119 | mts rmsr, r11 |
138 | nop | ||
139 | .endm | 120 | .endm |
140 | 121 | ||
141 | .macro enable_irq | 122 | .macro enable_irq |
142 | mfs r11, rmsr | 123 | mfs r11, rmsr |
143 | nop | ||
144 | ori r11, r11, MSR_IE | 124 | ori r11, r11, MSR_IE |
145 | mts rmsr, r11 | 125 | mts rmsr, r11 |
146 | nop | ||
147 | .endm | 126 | .endm |
148 | 127 | ||
149 | .macro set_ums | 128 | .macro set_ums |
150 | mfs r11, rmsr | 129 | mfs r11, rmsr |
151 | nop | ||
152 | ori r11, r11, MSR_VMS | 130 | ori r11, r11, MSR_VMS |
153 | andni r11, r11, MSR_UMS | 131 | andni r11, r11, MSR_UMS |
154 | mts rmsr, r11 | 132 | mts rmsr, r11 |
155 | nop | ||
156 | .endm | 133 | .endm |
157 | 134 | ||
158 | .macro set_vms | 135 | .macro set_vms |
159 | mfs r11, rmsr | 136 | mfs r11, rmsr |
160 | nop | ||
161 | ori r11, r11, MSR_VMS | 137 | ori r11, r11, MSR_VMS |
162 | andni r11, r11, MSR_UMS | 138 | andni r11, r11, MSR_UMS |
163 | mts rmsr, r11 | 139 | mts rmsr, r11 |
164 | nop | 140 | .endm |
141 | |||
142 | .macro clear_ums | ||
143 | mfs r11, rmsr | ||
144 | andni r11, r11, MSR_UMS | ||
145 | mts rmsr,r11 | ||
165 | .endm | 146 | .endm |
166 | 147 | ||
167 | .macro clear_vms_ums | 148 | .macro clear_vms_ums |
168 | mfs r11, rmsr | 149 | mfs r11, rmsr |
169 | nop | ||
170 | andni r11, r11, (MSR_VMS|MSR_UMS) | 150 | andni r11, r11, (MSR_VMS|MSR_UMS) |
171 | mts rmsr,r11 | 151 | mts rmsr,r11 |
172 | nop | ||
173 | .endm | 152 | .endm |
174 | #endif | 153 | #endif |
175 | 154 | ||
@@ -180,18 +159,22 @@ | |||
180 | 159 | ||
181 | /* turn on virtual protected mode save */ | 160 | /* turn on virtual protected mode save */ |
182 | #define VM_ON \ | 161 | #define VM_ON \ |
183 | set_ums; \ | 162 | set_ums; \ |
184 | rted r0, 2f; \ | 163 | rted r0, 2f; \ |
185 | 2: nop; | 164 | nop; \ |
165 | 2: | ||
186 | 166 | ||
187 | /* turn off virtual protected mode save and user mode save*/ | 167 | /* turn off virtual protected mode save and user mode save*/ |
188 | #define VM_OFF \ | 168 | #define VM_OFF \ |
189 | clear_vms_ums; \ | 169 | clear_vms_ums; \ |
190 | rted r0, TOPHYS(1f); \ | 170 | rted r0, TOPHYS(1f); \ |
191 | 1: nop; | 171 | nop; \ |
172 | 1: | ||
192 | 173 | ||
193 | #define SAVE_REGS \ | 174 | #define SAVE_REGS \ |
194 | swi r2, r1, PTO+PT_R2; /* Save SDA */ \ | 175 | swi r2, r1, PTO+PT_R2; /* Save SDA */ \ |
176 | swi r3, r1, PTO+PT_R3; \ | ||
177 | swi r4, r1, PTO+PT_R4; \ | ||
195 | swi r5, r1, PTO+PT_R5; \ | 178 | swi r5, r1, PTO+PT_R5; \ |
196 | swi r6, r1, PTO+PT_R6; \ | 179 | swi r6, r1, PTO+PT_R6; \ |
197 | swi r7, r1, PTO+PT_R7; \ | 180 | swi r7, r1, PTO+PT_R7; \ |
@@ -218,14 +201,14 @@ | |||
218 | swi r30, r1, PTO+PT_R30; \ | 201 | swi r30, r1, PTO+PT_R30; \ |
219 | swi r31, r1, PTO+PT_R31; /* Save current task reg */ \ | 202 | swi r31, r1, PTO+PT_R31; /* Save current task reg */ \ |
220 | mfs r11, rmsr; /* save MSR */ \ | 203 | mfs r11, rmsr; /* save MSR */ \ |
221 | nop; \ | ||
222 | swi r11, r1, PTO+PT_MSR; | 204 | swi r11, r1, PTO+PT_MSR; |
223 | 205 | ||
224 | #define RESTORE_REGS \ | 206 | #define RESTORE_REGS \ |
225 | lwi r11, r1, PTO+PT_MSR; \ | 207 | lwi r11, r1, PTO+PT_MSR; \ |
226 | mts rmsr , r11; \ | 208 | mts rmsr , r11; \ |
227 | nop; \ | ||
228 | lwi r2, r1, PTO+PT_R2; /* restore SDA */ \ | 209 | lwi r2, r1, PTO+PT_R2; /* restore SDA */ \ |
210 | lwi r3, r1, PTO+PT_R3; \ | ||
211 | lwi r4, r1, PTO+PT_R4; \ | ||
229 | lwi r5, r1, PTO+PT_R5; \ | 212 | lwi r5, r1, PTO+PT_R5; \ |
230 | lwi r6, r1, PTO+PT_R6; \ | 213 | lwi r6, r1, PTO+PT_R6; \ |
231 | lwi r7, r1, PTO+PT_R7; \ | 214 | lwi r7, r1, PTO+PT_R7; \ |
@@ -252,6 +235,39 @@ | |||
252 | lwi r30, r1, PTO+PT_R30; \ | 235 | lwi r30, r1, PTO+PT_R30; \ |
253 | lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */ | 236 | lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */ |
254 | 237 | ||
238 | #define SAVE_STATE \ | ||
239 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */ \ | ||
240 | /* See if already in kernel mode.*/ \ | ||
241 | mfs r1, rmsr; \ | ||
242 | andi r1, r1, MSR_UMS; \ | ||
243 | bnei r1, 1f; \ | ||
244 | /* Kernel-mode state save. */ \ | ||
245 | /* Reload kernel stack-ptr. */ \ | ||
246 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ | ||
247 | /* FIXME: I can add these two lines to one */ \ | ||
248 | /* tophys(r1,r1); */ \ | ||
249 | /* addik r1, r1, -STATE_SAVE_SIZE; */ \ | ||
250 | addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \ | ||
251 | SAVE_REGS \ | ||
252 | brid 2f; \ | ||
253 | swi r1, r1, PTO+PT_MODE; \ | ||
254 | 1: /* User-mode state save. */ \ | ||
255 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ | ||
256 | tophys(r1,r1); \ | ||
257 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ | ||
258 | /* MS these three instructions can be added to one */ \ | ||
259 | /* addik r1, r1, THREAD_SIZE; */ \ | ||
260 | /* tophys(r1,r1); */ \ | ||
261 | /* addik r1, r1, -STATE_SAVE_SIZE; */ \ | ||
262 | addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \ | ||
263 | SAVE_REGS \ | ||
264 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ | ||
265 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ \ | ||
266 | swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ \ | ||
267 | /* MS: I am clearing UMS even in case when I come from kernel space */ \ | ||
268 | clear_ums; \ | ||
269 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | ||
270 | |||
255 | .text | 271 | .text |
256 | 272 | ||
257 | /* | 273 | /* |
@@ -267,45 +283,23 @@ | |||
267 | * are masked. This is nice, means we don't have to CLI before state save | 283 | * are masked. This is nice, means we don't have to CLI before state save |
268 | */ | 284 | */ |
269 | C_ENTRY(_user_exception): | 285 | C_ENTRY(_user_exception): |
270 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ | ||
271 | addi r14, r14, 4 /* return address is 4 byte after call */ | 286 | addi r14, r14, 4 /* return address is 4 byte after call */ |
272 | swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ | 287 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ |
273 | |||
274 | lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ | ||
275 | beqi r11, 1f; /* Jump ahead if coming from user */ | ||
276 | /* Kernel-mode state save. */ | ||
277 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ | ||
278 | tophys(r1,r11); | ||
279 | swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ | ||
280 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ | ||
281 | |||
282 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ | ||
283 | SAVE_REGS | ||
284 | |||
285 | addi r11, r0, 1; /* Was in kernel-mode. */ | ||
286 | swi r11, r1, PTO+PT_MODE; /* pt_regs -> kernel mode */ | ||
287 | brid 2f; | ||
288 | nop; /* Fill delay slot */ | ||
289 | 288 | ||
290 | /* User-mode state save. */ | ||
291 | 1: | ||
292 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ | ||
293 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ | 289 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ |
294 | tophys(r1,r1); | 290 | tophys(r1,r1); |
295 | lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ | 291 | lwi r1, r1, TS_THREAD_INFO; /* get stack from task_struct */ |
296 | /* calculate kernel stack pointer from task struct 8k */ | 292 | /* MS these three instructions can be added to one */ |
297 | addik r1, r1, THREAD_SIZE; | 293 | /* addik r1, r1, THREAD_SIZE; */ |
298 | tophys(r1,r1); | 294 | /* tophys(r1,r1); */ |
299 | 295 | /* addik r1, r1, -STATE_SAVE_SIZE; */ | |
300 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ | 296 | addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; |
301 | SAVE_REGS | 297 | SAVE_REGS |
302 | 298 | ||
303 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ | ||
304 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | 299 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); |
305 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ | 300 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ |
306 | addi r11, r0, 1; | 301 | clear_ums; |
307 | swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ | 302 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); |
308 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | ||
309 | /* Save away the syscall number. */ | 303 | /* Save away the syscall number. */ |
310 | swi r12, r1, PTO+PT_R0; | 304 | swi r12, r1, PTO+PT_R0; |
311 | tovirt(r1,r1) | 305 | tovirt(r1,r1) |
@@ -316,10 +310,8 @@ C_ENTRY(_user_exception): | |||
316 | * register should point to the location where | 310 | * register should point to the location where |
317 | * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ | 311 | * the called function should return. [note that MAKE_SYS_CALL uses label 1] */ |
318 | 312 | ||
319 | # Step into virtual mode. | 313 | /* Step into virtual mode */ |
320 | set_vms; | 314 | rtbd r0, 3f |
321 | addik r11, r0, 3f | ||
322 | rtid r11, 0 | ||
323 | nop | 315 | nop |
324 | 3: | 316 | 3: |
325 | lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ | 317 | lwi r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */ |
@@ -363,24 +355,17 @@ C_ENTRY(_user_exception): | |||
363 | # Find and jump into the syscall handler. | 355 | # Find and jump into the syscall handler. |
364 | lwi r12, r12, sys_call_table | 356 | lwi r12, r12, sys_call_table |
365 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ | 357 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ |
366 | la r15, r0, ret_from_trap-8 | 358 | addi r15, r0, ret_from_trap-8 |
367 | bra r12 | 359 | bra r12 |
368 | 360 | ||
369 | /* The syscall number is invalid, return an error. */ | 361 | /* The syscall number is invalid, return an error. */ |
370 | 5: | 362 | 5: |
363 | rtsd r15, 8; /* looks like a normal subroutine return */ | ||
371 | addi r3, r0, -ENOSYS; | 364 | addi r3, r0, -ENOSYS; |
372 | rtsd r15,8; /* looks like a normal subroutine return */ | ||
373 | or r0, r0, r0 | ||
374 | |||
375 | 365 | ||
376 | /* Entry point used to return from a syscall/trap */ | 366 | /* Entry point used to return from a syscall/trap */ |
377 | /* We re-enable BIP bit before state restore */ | 367 | /* We re-enable BIP bit before state restore */ |
378 | C_ENTRY(ret_from_trap): | 368 | C_ENTRY(ret_from_trap): |
379 | set_bip; /* Ints masked for state restore*/ | ||
380 | lwi r11, r1, PTO+PT_MODE; | ||
381 | /* See if returning to kernel mode, if so, skip resched &c. */ | ||
382 | bnei r11, 2f; | ||
383 | |||
384 | swi r3, r1, PTO + PT_R3 | 369 | swi r3, r1, PTO + PT_R3 |
385 | swi r4, r1, PTO + PT_R4 | 370 | swi r4, r1, PTO + PT_R4 |
386 | 371 | ||
@@ -413,32 +398,19 @@ C_ENTRY(ret_from_trap): | |||
413 | andi r11, r11, _TIF_SIGPENDING; | 398 | andi r11, r11, _TIF_SIGPENDING; |
414 | beqi r11, 1f; /* Signals to handle, handle them */ | 399 | beqi r11, 1f; /* Signals to handle, handle them */ |
415 | 400 | ||
416 | la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ | 401 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ |
417 | addi r7, r0, 1; /* Arg 3: int in_syscall */ | 402 | addi r7, r0, 1; /* Arg 3: int in_syscall */ |
418 | bralid r15, do_signal; /* Handle any signals */ | 403 | bralid r15, do_signal; /* Handle any signals */ |
419 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ | 404 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ |
420 | 405 | ||
421 | /* Finally, return to user state. */ | 406 | /* Finally, return to user state. */ |
422 | 1: | 407 | 1: set_bip; /* Ints masked for state restore */ |
423 | lwi r3, r1, PTO + PT_R3; /* restore syscall result */ | ||
424 | lwi r4, r1, PTO + PT_R4; | ||
425 | |||
426 | swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ | ||
427 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ | 408 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ |
428 | VM_OFF; | 409 | VM_OFF; |
429 | tophys(r1,r1); | 410 | tophys(r1,r1); |
430 | RESTORE_REGS; | 411 | RESTORE_REGS; |
431 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 412 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ |
432 | lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ | 413 | lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */ |
433 | bri 6f; | ||
434 | |||
435 | /* Return to kernel state. */ | ||
436 | 2: VM_OFF; | ||
437 | tophys(r1,r1); | ||
438 | RESTORE_REGS; | ||
439 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | ||
440 | tovirt(r1,r1); | ||
441 | 6: | ||
442 | TRAP_return: /* Make global symbol for debugging */ | 414 | TRAP_return: /* Make global symbol for debugging */ |
443 | rtbd r14, 0; /* Instructions to return from an IRQ */ | 415 | rtbd r14, 0; /* Instructions to return from an IRQ */ |
444 | nop; | 416 | nop; |
@@ -450,12 +422,11 @@ TRAP_return: /* Make global symbol for debugging */ | |||
450 | C_ENTRY(sys_fork_wrapper): | 422 | C_ENTRY(sys_fork_wrapper): |
451 | addi r5, r0, SIGCHLD /* Arg 0: flags */ | 423 | addi r5, r0, SIGCHLD /* Arg 0: flags */ |
452 | lwi r6, r1, PTO+PT_R1 /* Arg 1: child SP (use parent's) */ | 424 | lwi r6, r1, PTO+PT_R1 /* Arg 1: child SP (use parent's) */ |
453 | la r7, r1, PTO /* Arg 2: parent context */ | 425 | addik r7, r1, PTO /* Arg 2: parent context */ |
454 | add r8. r0, r0 /* Arg 3: (unused) */ | 426 | add r8. r0, r0 /* Arg 3: (unused) */ |
455 | add r9, r0, r0; /* Arg 4: (unused) */ | 427 | add r9, r0, r0; /* Arg 4: (unused) */ |
456 | add r10, r0, r0; /* Arg 5: (unused) */ | ||
457 | brid do_fork /* Do real work (tail-call) */ | 428 | brid do_fork /* Do real work (tail-call) */ |
458 | nop; | 429 | add r10, r0, r0; /* Arg 5: (unused) */ |
459 | 430 | ||
460 | /* This the initial entry point for a new child thread, with an appropriate | 431 | /* This the initial entry point for a new child thread, with an appropriate |
461 | stack in place that makes it look the the child is in the middle of an | 432 | stack in place that makes it look the the child is in the middle of an |
@@ -466,35 +437,31 @@ C_ENTRY(ret_from_fork): | |||
466 | bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ | 437 | bralid r15, schedule_tail; /* ...which is schedule_tail's arg */ |
467 | add r3, r5, r0; /* switch_thread returns the prev task */ | 438 | add r3, r5, r0; /* switch_thread returns the prev task */ |
468 | /* ( in the delay slot ) */ | 439 | /* ( in the delay slot ) */ |
469 | add r3, r0, r0; /* Child's fork call should return 0. */ | ||
470 | brid ret_from_trap; /* Do normal trap return */ | 440 | brid ret_from_trap; /* Do normal trap return */ |
471 | nop; | 441 | add r3, r0, r0; /* Child's fork call should return 0. */ |
472 | 442 | ||
473 | C_ENTRY(sys_vfork): | 443 | C_ENTRY(sys_vfork): |
474 | brid microblaze_vfork /* Do real work (tail-call) */ | 444 | brid microblaze_vfork /* Do real work (tail-call) */ |
475 | la r5, r1, PTO | 445 | addik r5, r1, PTO |
476 | 446 | ||
477 | C_ENTRY(sys_clone): | 447 | C_ENTRY(sys_clone): |
478 | bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ | 448 | bnei r6, 1f; /* See if child SP arg (arg 1) is 0. */ |
479 | lwi r6, r1, PTO+PT_R1; /* If so, use paret's stack ptr */ | 449 | lwi r6, r1, PTO + PT_R1; /* If so, use paret's stack ptr */ |
480 | 1: la r7, r1, PTO; /* Arg 2: parent context */ | 450 | 1: addik r7, r1, PTO; /* Arg 2: parent context */ |
481 | add r8, r0, r0; /* Arg 3: (unused) */ | 451 | add r8, r0, r0; /* Arg 3: (unused) */ |
482 | add r9, r0, r0; /* Arg 4: (unused) */ | 452 | add r9, r0, r0; /* Arg 4: (unused) */ |
483 | add r10, r0, r0; /* Arg 5: (unused) */ | ||
484 | brid do_fork /* Do real work (tail-call) */ | 453 | brid do_fork /* Do real work (tail-call) */ |
485 | nop; | 454 | add r10, r0, r0; /* Arg 5: (unused) */ |
486 | 455 | ||
487 | C_ENTRY(sys_execve): | 456 | C_ENTRY(sys_execve): |
488 | la r8, r1, PTO; /* add user context as 4th arg */ | ||
489 | brid microblaze_execve; /* Do real work (tail-call).*/ | 457 | brid microblaze_execve; /* Do real work (tail-call).*/ |
490 | nop; | 458 | addik r8, r1, PTO; /* add user context as 4th arg */ |
491 | 459 | ||
492 | C_ENTRY(sys_rt_sigreturn_wrapper): | 460 | C_ENTRY(sys_rt_sigreturn_wrapper): |
493 | swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | 461 | swi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ |
494 | swi r4, r1, PTO+PT_R4; | 462 | swi r4, r1, PTO+PT_R4; |
495 | la r5, r1, PTO; /* add user context as 1st arg */ | ||
496 | brlid r15, sys_rt_sigreturn /* Do real work */ | 463 | brlid r15, sys_rt_sigreturn /* Do real work */ |
497 | nop; | 464 | addik r5, r1, PTO; /* add user context as 1st arg */ |
498 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | 465 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ |
499 | lwi r4, r1, PTO+PT_R4; | 466 | lwi r4, r1, PTO+PT_R4; |
500 | bri ret_from_trap /* fall through will not work here due to align */ | 467 | bri ret_from_trap /* fall through will not work here due to align */ |
@@ -503,83 +470,23 @@ C_ENTRY(sys_rt_sigreturn_wrapper): | |||
503 | /* | 470 | /* |
504 | * HW EXCEPTION rutine start | 471 | * HW EXCEPTION rutine start |
505 | */ | 472 | */ |
506 | |||
507 | #define SAVE_STATE \ | ||
508 | swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ \ | ||
509 | set_bip; /*equalize initial state for all possible entries*/\ | ||
510 | clear_eip; \ | ||
511 | enable_irq; \ | ||
512 | set_ee; \ | ||
513 | /* See if already in kernel mode.*/ \ | ||
514 | lwi r11, r0, TOPHYS(PER_CPU(KM)); \ | ||
515 | beqi r11, 1f; /* Jump ahead if coming from user */\ | ||
516 | /* Kernel-mode state save. */ \ | ||
517 | /* Reload kernel stack-ptr. */ \ | ||
518 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ | ||
519 | tophys(r1,r11); \ | ||
520 | swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ \ | ||
521 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ | ||
522 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ | ||
523 | /* store return registers separately because \ | ||
524 | * this macros is use for others exceptions */ \ | ||
525 | swi r3, r1, PTO + PT_R3; \ | ||
526 | swi r4, r1, PTO + PT_R4; \ | ||
527 | SAVE_REGS \ | ||
528 | /* PC, before IRQ/trap - this is one instruction above */ \ | ||
529 | swi r17, r1, PTO+PT_PC; \ | ||
530 | \ | ||
531 | addi r11, r0, 1; /* Was in kernel-mode. */ \ | ||
532 | swi r11, r1, PTO+PT_MODE; \ | ||
533 | brid 2f; \ | ||
534 | nop; /* Fill delay slot */ \ | ||
535 | 1: /* User-mode state save. */ \ | ||
536 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */\ | ||
537 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ | ||
538 | tophys(r1,r1); \ | ||
539 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ \ | ||
540 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */\ | ||
541 | tophys(r1,r1); \ | ||
542 | \ | ||
543 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */\ | ||
544 | /* store return registers separately because this macros \ | ||
545 | * is use for others exceptions */ \ | ||
546 | swi r3, r1, PTO + PT_R3; \ | ||
547 | swi r4, r1, PTO + PT_R4; \ | ||
548 | SAVE_REGS \ | ||
549 | /* PC, before IRQ/trap - this is one instruction above FIXME*/ \ | ||
550 | swi r17, r1, PTO+PT_PC; \ | ||
551 | \ | ||
552 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ \ | ||
553 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \ | ||
554 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ \ | ||
555 | addi r11, r0, 1; \ | ||
556 | swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\ | ||
557 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); \ | ||
558 | /* Save away the syscall number. */ \ | ||
559 | swi r0, r1, PTO+PT_R0; \ | ||
560 | tovirt(r1,r1) | ||
561 | |||
562 | C_ENTRY(full_exception_trap): | 473 | C_ENTRY(full_exception_trap): |
563 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ | ||
564 | /* adjust exception address for privileged instruction | 474 | /* adjust exception address for privileged instruction |
565 | * for finding where is it */ | 475 | * for finding where is it */ |
566 | addik r17, r17, -4 | 476 | addik r17, r17, -4 |
567 | SAVE_STATE /* Save registers */ | 477 | SAVE_STATE /* Save registers */ |
478 | /* PC, before IRQ/trap - this is one instruction above */ | ||
479 | swi r17, r1, PTO+PT_PC; | ||
480 | tovirt(r1,r1) | ||
568 | /* FIXME this can be store directly in PT_ESR reg. | 481 | /* FIXME this can be store directly in PT_ESR reg. |
569 | * I tested it but there is a fault */ | 482 | * I tested it but there is a fault */ |
570 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ | 483 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ |
571 | la r15, r0, ret_from_exc - 8 | 484 | addik r15, r0, ret_from_exc - 8 |
572 | la r5, r1, PTO /* parameter struct pt_regs * regs */ | ||
573 | mfs r6, resr | 485 | mfs r6, resr |
574 | nop | ||
575 | mfs r7, rfsr; /* save FSR */ | 486 | mfs r7, rfsr; /* save FSR */ |
576 | nop | ||
577 | mts rfsr, r0; /* Clear sticky fsr */ | 487 | mts rfsr, r0; /* Clear sticky fsr */ |
578 | nop | 488 | rted r0, full_exception |
579 | la r12, r0, full_exception | 489 | addik r5, r1, PTO /* parameter struct pt_regs * regs */ |
580 | set_vms; | ||
581 | rtbd r12, 0; | ||
582 | nop; | ||
583 | 490 | ||
584 | /* | 491 | /* |
585 | * Unaligned data trap. | 492 | * Unaligned data trap. |
@@ -592,19 +499,27 @@ C_ENTRY(full_exception_trap): | |||
592 | * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S" | 499 | * The assembler routine is in "arch/microblaze/kernel/hw_exception_handler.S" |
593 | */ | 500 | */ |
594 | C_ENTRY(unaligned_data_trap): | 501 | C_ENTRY(unaligned_data_trap): |
595 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ | 502 | /* MS: I have to save r11 value and then restore it because |
503 | * set_bit, clear_eip, set_ee use r11 as temp register if MSR | ||
504 | * instructions are not used. We don't need to do if MSR instructions | ||
505 | * are used and they use r0 instead of r11. | ||
506 | * I am using ENTRY_SP which should be primary used only for stack | ||
507 | * pointer saving. */ | ||
508 | swi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | ||
509 | set_bip; /* equalize initial state for all possible entries */ | ||
510 | clear_eip; | ||
511 | set_ee; | ||
512 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | ||
596 | SAVE_STATE /* Save registers.*/ | 513 | SAVE_STATE /* Save registers.*/ |
514 | /* PC, before IRQ/trap - this is one instruction above */ | ||
515 | swi r17, r1, PTO+PT_PC; | ||
516 | tovirt(r1,r1) | ||
597 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ | 517 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ |
598 | la r15, r0, ret_from_exc-8 | 518 | addik r15, r0, ret_from_exc-8 |
599 | mfs r3, resr /* ESR */ | 519 | mfs r3, resr /* ESR */ |
600 | nop | ||
601 | mfs r4, rear /* EAR */ | 520 | mfs r4, rear /* EAR */ |
602 | nop | 521 | rtbd r0, _unaligned_data_exception |
603 | la r7, r1, PTO /* parameter struct pt_regs * regs */ | 522 | addik r7, r1, PTO /* parameter struct pt_regs * regs */ |
604 | la r12, r0, _unaligned_data_exception | ||
605 | set_vms; | ||
606 | rtbd r12, 0; /* interrupts enabled */ | ||
607 | nop; | ||
608 | 523 | ||
609 | /* | 524 | /* |
610 | * Page fault traps. | 525 | * Page fault traps. |
@@ -625,38 +540,32 @@ C_ENTRY(unaligned_data_trap): | |||
625 | */ | 540 | */ |
626 | /* data and intruction trap - which is choose is resolved int fault.c */ | 541 | /* data and intruction trap - which is choose is resolved int fault.c */ |
627 | C_ENTRY(page_fault_data_trap): | 542 | C_ENTRY(page_fault_data_trap): |
628 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ | ||
629 | SAVE_STATE /* Save registers.*/ | 543 | SAVE_STATE /* Save registers.*/ |
544 | /* PC, before IRQ/trap - this is one instruction above */ | ||
545 | swi r17, r1, PTO+PT_PC; | ||
546 | tovirt(r1,r1) | ||
630 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ | 547 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ |
631 | la r15, r0, ret_from_exc-8 | 548 | addik r15, r0, ret_from_exc-8 |
632 | la r5, r1, PTO /* parameter struct pt_regs * regs */ | ||
633 | mfs r6, rear /* parameter unsigned long address */ | 549 | mfs r6, rear /* parameter unsigned long address */ |
634 | nop | ||
635 | mfs r7, resr /* parameter unsigned long error_code */ | 550 | mfs r7, resr /* parameter unsigned long error_code */ |
636 | nop | 551 | rted r0, do_page_fault |
637 | la r12, r0, do_page_fault | 552 | addik r5, r1, PTO /* parameter struct pt_regs * regs */ |
638 | set_vms; | ||
639 | rtbd r12, 0; /* interrupts enabled */ | ||
640 | nop; | ||
641 | 553 | ||
642 | C_ENTRY(page_fault_instr_trap): | 554 | C_ENTRY(page_fault_instr_trap): |
643 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */ | ||
644 | SAVE_STATE /* Save registers.*/ | 555 | SAVE_STATE /* Save registers.*/ |
556 | /* PC, before IRQ/trap - this is one instruction above */ | ||
557 | swi r17, r1, PTO+PT_PC; | ||
558 | tovirt(r1,r1) | ||
645 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ | 559 | /* where the trap should return need -8 to adjust for rtsd r15, 8 */ |
646 | la r15, r0, ret_from_exc-8 | 560 | addik r15, r0, ret_from_exc-8 |
647 | la r5, r1, PTO /* parameter struct pt_regs * regs */ | ||
648 | mfs r6, rear /* parameter unsigned long address */ | 561 | mfs r6, rear /* parameter unsigned long address */ |
649 | nop | ||
650 | ori r7, r0, 0 /* parameter unsigned long error_code */ | 562 | ori r7, r0, 0 /* parameter unsigned long error_code */ |
651 | la r12, r0, do_page_fault | 563 | rted r0, do_page_fault |
652 | set_vms; | 564 | addik r5, r1, PTO /* parameter struct pt_regs * regs */ |
653 | rtbd r12, 0; /* interrupts enabled */ | ||
654 | nop; | ||
655 | 565 | ||
656 | /* Entry point used to return from an exception. */ | 566 | /* Entry point used to return from an exception. */ |
657 | C_ENTRY(ret_from_exc): | 567 | C_ENTRY(ret_from_exc): |
658 | set_bip; /* Ints masked for state restore*/ | 568 | lwi r11, r1, PTO + PT_MODE; |
659 | lwi r11, r1, PTO+PT_MODE; | ||
660 | bnei r11, 2f; /* See if returning to kernel mode, */ | 569 | bnei r11, 2f; /* See if returning to kernel mode, */ |
661 | /* ... if so, skip resched &c. */ | 570 | /* ... if so, skip resched &c. */ |
662 | 571 | ||
@@ -687,32 +596,27 @@ C_ENTRY(ret_from_exc): | |||
687 | * traps), but signal handlers may want to examine or change the | 596 | * traps), but signal handlers may want to examine or change the |
688 | * complete register state. Here we save anything not saved by | 597 | * complete register state. Here we save anything not saved by |
689 | * the normal entry sequence, so that it may be safely restored | 598 | * the normal entry sequence, so that it may be safely restored |
690 | * (in a possibly modified form) after do_signal returns. | 599 | * (in a possibly modified form) after do_signal returns. */ |
691 | * store return registers separately because this macros is use | 600 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ |
692 | * for others exceptions */ | ||
693 | la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ | ||
694 | addi r7, r0, 0; /* Arg 3: int in_syscall */ | 601 | addi r7, r0, 0; /* Arg 3: int in_syscall */ |
695 | bralid r15, do_signal; /* Handle any signals */ | 602 | bralid r15, do_signal; /* Handle any signals */ |
696 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ | 603 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ |
697 | 604 | ||
698 | /* Finally, return to user state. */ | 605 | /* Finally, return to user state. */ |
699 | 1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ | 606 | 1: set_bip; /* Ints masked for state restore */ |
700 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ | 607 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ |
701 | VM_OFF; | 608 | VM_OFF; |
702 | tophys(r1,r1); | 609 | tophys(r1,r1); |
703 | 610 | ||
704 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | ||
705 | lwi r4, r1, PTO+PT_R4; | ||
706 | RESTORE_REGS; | 611 | RESTORE_REGS; |
707 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 612 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ |
708 | 613 | ||
709 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */ | 614 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */ |
710 | bri 6f; | 615 | bri 6f; |
711 | /* Return to kernel state. */ | 616 | /* Return to kernel state. */ |
712 | 2: VM_OFF; | 617 | 2: set_bip; /* Ints masked for state restore */ |
618 | VM_OFF; | ||
713 | tophys(r1,r1); | 619 | tophys(r1,r1); |
714 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | ||
715 | lwi r4, r1, PTO+PT_R4; | ||
716 | RESTORE_REGS; | 620 | RESTORE_REGS; |
717 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 621 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ |
718 | 622 | ||
@@ -736,36 +640,23 @@ C_ENTRY(_interrupt): | |||
736 | /* MS: we are in physical address */ | 640 | /* MS: we are in physical address */ |
737 | /* Save registers, switch to proper stack, convert SP to virtual.*/ | 641 | /* Save registers, switch to proper stack, convert SP to virtual.*/ |
738 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) | 642 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) |
739 | swi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); | ||
740 | /* MS: See if already in kernel mode. */ | 643 | /* MS: See if already in kernel mode. */ |
741 | lwi r11, r0, TOPHYS(PER_CPU(KM)); | 644 | mfs r1, rmsr |
742 | beqi r11, 1f; /* MS: Jump ahead if coming from user */ | 645 | nop |
646 | andi r1, r1, MSR_UMS | ||
647 | bnei r1, 1f | ||
743 | 648 | ||
744 | /* Kernel-mode state save. */ | 649 | /* Kernel-mode state save. */ |
745 | or r11, r1, r0 | 650 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) |
746 | tophys(r1,r11); /* MS: I have in r1 physical address where stack is */ | 651 | tophys(r1,r1); /* MS: I have in r1 physical address where stack is */ |
747 | /* MS: Save original SP - position PT_R1 to next stack frame 4 *1 - 152*/ | ||
748 | swi r11, r1, (PT_R1 - PT_SIZE); | ||
749 | /* MS: restore r11 because of saving in SAVE_REGS */ | ||
750 | lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); | ||
751 | /* save registers */ | 652 | /* save registers */ |
752 | /* MS: Make room on the stack -> activation record */ | 653 | /* MS: Make room on the stack -> activation record */ |
753 | addik r1, r1, -STATE_SAVE_SIZE; | 654 | addik r1, r1, -STATE_SAVE_SIZE; |
754 | /* MS: store return registers separately because | ||
755 | * this macros is use for others exceptions */ | ||
756 | swi r3, r1, PTO + PT_R3; | ||
757 | swi r4, r1, PTO + PT_R4; | ||
758 | SAVE_REGS | 655 | SAVE_REGS |
759 | /* MS: store mode */ | ||
760 | addi r11, r0, 1; /* MS: Was in kernel-mode. */ | ||
761 | swi r11, r1, PTO + PT_MODE; /* MS: and save it */ | ||
762 | brid 2f; | 656 | brid 2f; |
763 | nop; /* MS: Fill delay slot */ | 657 | swi r1, r1, PTO + PT_MODE; /* 0 - user mode, 1 - kernel mode */ |
764 | |||
765 | 1: | 658 | 1: |
766 | /* User-mode state save. */ | 659 | /* User-mode state save. */ |
767 | /* MS: restore r11 -> FIXME move before SAVE_REG */ | ||
768 | lwi r11, r0, TOPHYS(PER_CPU(R11_SAVE)); | ||
769 | /* MS: get the saved current */ | 660 | /* MS: get the saved current */ |
770 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | 661 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); |
771 | tophys(r1,r1); | 662 | tophys(r1,r1); |
@@ -774,27 +665,18 @@ C_ENTRY(_interrupt): | |||
774 | tophys(r1,r1); | 665 | tophys(r1,r1); |
775 | /* save registers */ | 666 | /* save registers */ |
776 | addik r1, r1, -STATE_SAVE_SIZE; | 667 | addik r1, r1, -STATE_SAVE_SIZE; |
777 | swi r3, r1, PTO+PT_R3; | ||
778 | swi r4, r1, PTO+PT_R4; | ||
779 | SAVE_REGS | 668 | SAVE_REGS |
780 | /* calculate mode */ | 669 | /* calculate mode */ |
781 | swi r0, r1, PTO + PT_MODE; | 670 | swi r0, r1, PTO + PT_MODE; |
782 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | 671 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); |
783 | swi r11, r1, PTO+PT_R1; | 672 | swi r11, r1, PTO+PT_R1; |
784 | /* setup kernel mode to KM */ | 673 | clear_ums; |
785 | addi r11, r0, 1; | ||
786 | swi r11, r0, TOPHYS(PER_CPU(KM)); | ||
787 | |||
788 | 2: | 674 | 2: |
789 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | 675 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); |
790 | swi r0, r1, PTO + PT_R0; | ||
791 | tovirt(r1,r1) | 676 | tovirt(r1,r1) |
792 | la r5, r1, PTO; | 677 | addik r15, r0, irq_call; |
793 | set_vms; | 678 | irq_call:rtbd r0, do_IRQ; |
794 | la r11, r0, do_IRQ; | 679 | addik r5, r1, PTO; |
795 | la r15, r0, irq_call; | ||
796 | irq_call:rtbd r11, 0; | ||
797 | nop; | ||
798 | 680 | ||
799 | /* MS: we are in virtual mode */ | 681 | /* MS: we are in virtual mode */ |
800 | ret_from_irq: | 682 | ret_from_irq: |
@@ -815,7 +697,7 @@ ret_from_irq: | |||
815 | beqid r11, no_intr_resched | 697 | beqid r11, no_intr_resched |
816 | /* Handle a signal return; Pending signals should be in r18. */ | 698 | /* Handle a signal return; Pending signals should be in r18. */ |
817 | addi r7, r0, 0; /* Arg 3: int in_syscall */ | 699 | addi r7, r0, 0; /* Arg 3: int in_syscall */ |
818 | la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ | 700 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ |
819 | bralid r15, do_signal; /* Handle any signals */ | 701 | bralid r15, do_signal; /* Handle any signals */ |
820 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ | 702 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ |
821 | 703 | ||
@@ -823,12 +705,9 @@ ret_from_irq: | |||
823 | no_intr_resched: | 705 | no_intr_resched: |
824 | /* Disable interrupts, we are now committed to the state restore */ | 706 | /* Disable interrupts, we are now committed to the state restore */ |
825 | disable_irq | 707 | disable_irq |
826 | swi r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */ | ||
827 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); | 708 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); |
828 | VM_OFF; | 709 | VM_OFF; |
829 | tophys(r1,r1); | 710 | tophys(r1,r1); |
830 | lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ | ||
831 | lwi r4, r1, PTO + PT_R4; | ||
832 | RESTORE_REGS | 711 | RESTORE_REGS |
833 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ | 712 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ |
834 | lwi r1, r1, PT_R1 - PT_SIZE; | 713 | lwi r1, r1, PT_R1 - PT_SIZE; |
@@ -857,8 +736,6 @@ restore: | |||
857 | #endif | 736 | #endif |
858 | VM_OFF /* MS: turn off MMU */ | 737 | VM_OFF /* MS: turn off MMU */ |
859 | tophys(r1,r1) | 738 | tophys(r1,r1) |
860 | lwi r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ | ||
861 | lwi r4, r1, PTO + PT_R4; | ||
862 | RESTORE_REGS | 739 | RESTORE_REGS |
863 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ | 740 | addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */ |
864 | tovirt(r1,r1); | 741 | tovirt(r1,r1); |
@@ -868,86 +745,91 @@ IRQ_return: /* MS: Make global symbol for debugging */ | |||
868 | nop | 745 | nop |
869 | 746 | ||
870 | /* | 747 | /* |
871 | * `Debug' trap | 748 | * Debug trap for KGDB. Enter to _debug_exception by brki r16, 0x18 |
872 | * We enter dbtrap in "BIP" (breakpoint) mode. | 749 | * and call handling function with saved pt_regs |
873 | * So we exit the breakpoint mode with an 'rtbd' and proceed with the | ||
874 | * original dbtrap. | ||
875 | * however, wait to save state first | ||
876 | */ | 750 | */ |
877 | C_ENTRY(_debug_exception): | 751 | C_ENTRY(_debug_exception): |
878 | /* BIP bit is set on entry, no interrupts can occur */ | 752 | /* BIP bit is set on entry, no interrupts can occur */ |
879 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) | 753 | swi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) |
880 | 754 | ||
881 | swi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* Save r11 */ | 755 | mfs r1, rmsr |
882 | set_bip; /*equalize initial state for all possible entries*/ | 756 | nop |
883 | clear_eip; | 757 | andi r1, r1, MSR_UMS |
884 | enable_irq; | 758 | bnei r1, 1f |
885 | lwi r11, r0, TOPHYS(PER_CPU(KM));/* See if already in kernel mode.*/ | 759 | /* MS: Kernel-mode state save - kgdb */ |
886 | beqi r11, 1f; /* Jump ahead if coming from user */ | 760 | lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ |
887 | /* Kernel-mode state save. */ | ||
888 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/ | ||
889 | tophys(r1,r11); | ||
890 | swi r11, r1, (PT_R1-PT_SIZE); /* Save original SP. */ | ||
891 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ | ||
892 | 761 | ||
893 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ | 762 | /* BIP bit is set on entry, no interrupts can occur */ |
894 | swi r3, r1, PTO + PT_R3; | 763 | addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; |
895 | swi r4, r1, PTO + PT_R4; | ||
896 | SAVE_REGS; | 764 | SAVE_REGS; |
765 | /* save all regs to pt_reg structure */ | ||
766 | swi r0, r1, PTO+PT_R0; /* R0 must be saved too */ | ||
767 | swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */ | ||
768 | swi r16, r1, PTO+PT_R16 | ||
769 | swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */ | ||
770 | swi r17, r1, PTO+PT_R17 | ||
771 | /* save special purpose registers to pt_regs */ | ||
772 | mfs r11, rear; | ||
773 | swi r11, r1, PTO+PT_EAR; | ||
774 | mfs r11, resr; | ||
775 | swi r11, r1, PTO+PT_ESR; | ||
776 | mfs r11, rfsr; | ||
777 | swi r11, r1, PTO+PT_FSR; | ||
778 | |||
779 | /* stack pointer is in physical address at it is decrease | ||
780 | * by STATE_SAVE_SIZE but we need to get correct R1 value */ | ||
781 | addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE; | ||
782 | swi r11, r1, PTO+PT_R1 | ||
783 | /* MS: r31 - current pointer isn't changed */ | ||
784 | tovirt(r1,r1) | ||
785 | #ifdef CONFIG_KGDB | ||
786 | addi r5, r1, PTO /* pass pt_reg address as the first arg */ | ||
787 | la r15, r0, dbtrap_call; /* return address */ | ||
788 | rtbd r0, microblaze_kgdb_break | ||
789 | nop; | ||
790 | #endif | ||
791 | /* MS: Place handler for brki from kernel space if KGDB is OFF. | ||
792 | * It is very unlikely that another brki instruction is called. */ | ||
793 | bri 0 | ||
897 | 794 | ||
898 | addi r11, r0, 1; /* Was in kernel-mode. */ | 795 | /* MS: User-mode state save - gdb */ |
899 | swi r11, r1, PTO + PT_MODE; | 796 | 1: lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ |
900 | brid 2f; | ||
901 | nop; /* Fill delay slot */ | ||
902 | 1: /* User-mode state save. */ | ||
903 | lwi r11, r0, TOPHYS(r0_ram + PTO + PT_R11); /* restore r11 */ | ||
904 | lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ | ||
905 | tophys(r1,r1); | 797 | tophys(r1,r1); |
906 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ | 798 | lwi r1, r1, TS_THREAD_INFO; /* get the thread info */ |
907 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ | 799 | addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */ |
908 | tophys(r1,r1); | 800 | tophys(r1,r1); |
909 | 801 | ||
910 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ | 802 | addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */ |
911 | swi r3, r1, PTO + PT_R3; | ||
912 | swi r4, r1, PTO + PT_R4; | ||
913 | SAVE_REGS; | 803 | SAVE_REGS; |
914 | 804 | swi r17, r1, PTO+PT_R17; | |
915 | swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ | 805 | swi r16, r1, PTO+PT_R16; |
806 | swi r16, r1, PTO+PT_PC; /* Save LP */ | ||
807 | swi r0, r1, PTO + PT_MODE; /* Was in user-mode. */ | ||
916 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); | 808 | lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); |
917 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ | 809 | swi r11, r1, PTO+PT_R1; /* Store user SP. */ |
918 | addi r11, r0, 1; | 810 | lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); |
919 | swi r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode. */ | ||
920 | 2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); | ||
921 | /* Save away the syscall number. */ | ||
922 | swi r0, r1, PTO+PT_R0; | ||
923 | tovirt(r1,r1) | 811 | tovirt(r1,r1) |
924 | |||
925 | addi r5, r0, SIGTRAP /* send the trap signal */ | ||
926 | add r6, r0, CURRENT_TASK; /* Get current task ptr into r11 */ | ||
927 | addk r7, r0, r0 /* 3rd param zero */ | ||
928 | |||
929 | set_vms; | 812 | set_vms; |
930 | la r11, r0, send_sig; | 813 | addik r5, r1, PTO; |
931 | la r15, r0, dbtrap_call; | 814 | addik r15, r0, dbtrap_call; |
932 | dbtrap_call: rtbd r11, 0; | 815 | dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */ |
933 | nop; | 816 | rtbd r0, sw_exception |
817 | nop | ||
934 | 818 | ||
935 | set_bip; /* Ints masked for state restore*/ | 819 | /* MS: The first instruction for the second part of the gdb/kgdb */ |
936 | lwi r11, r1, PTO+PT_MODE; | 820 | set_bip; /* Ints masked for state restore */ |
821 | lwi r11, r1, PTO + PT_MODE; | ||
937 | bnei r11, 2f; | 822 | bnei r11, 2f; |
938 | 823 | /* MS: Return to user space - gdb */ | |
939 | /* Get current task ptr into r11 */ | 824 | /* Get current task ptr into r11 */ |
940 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ | 825 | lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ |
941 | lwi r11, r11, TI_FLAGS; /* get flags in thread info */ | 826 | lwi r11, r11, TI_FLAGS; /* get flags in thread info */ |
942 | andi r11, r11, _TIF_NEED_RESCHED; | 827 | andi r11, r11, _TIF_NEED_RESCHED; |
943 | beqi r11, 5f; | 828 | beqi r11, 5f; |
944 | 829 | ||
945 | /* Call the scheduler before returning from a syscall/trap. */ | 830 | /* Call the scheduler before returning from a syscall/trap. */ |
946 | |||
947 | bralid r15, schedule; /* Call scheduler */ | 831 | bralid r15, schedule; /* Call scheduler */ |
948 | nop; /* delay slot */ | 832 | nop; /* delay slot */ |
949 | /* XXX Is PT_DTRACE handling needed here? */ | ||
950 | /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here. */ | ||
951 | 833 | ||
952 | /* Maybe handle a signal */ | 834 | /* Maybe handle a signal */ |
953 | 5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ | 835 | 5: lwi r11, CURRENT_TASK, TS_THREAD_INFO; /* get thread info */ |
@@ -955,54 +837,40 @@ dbtrap_call: rtbd r11, 0; | |||
955 | andi r11, r11, _TIF_SIGPENDING; | 837 | andi r11, r11, _TIF_SIGPENDING; |
956 | beqi r11, 1f; /* Signals to handle, handle them */ | 838 | beqi r11, 1f; /* Signals to handle, handle them */ |
957 | 839 | ||
958 | /* Handle a signal return; Pending signals should be in r18. */ | 840 | addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ |
959 | /* Not all registers are saved by the normal trap/interrupt entry | ||
960 | points (for instance, call-saved registers (because the normal | ||
961 | C-compiler calling sequence in the kernel makes sure they're | ||
962 | preserved), and call-clobbered registers in the case of | ||
963 | traps), but signal handlers may want to examine or change the | ||
964 | complete register state. Here we save anything not saved by | ||
965 | the normal entry sequence, so that it may be safely restored | ||
966 | (in a possibly modified form) after do_signal returns. */ | ||
967 | |||
968 | la r5, r1, PTO; /* Arg 1: struct pt_regs *regs */ | ||
969 | addi r7, r0, 0; /* Arg 3: int in_syscall */ | 841 | addi r7, r0, 0; /* Arg 3: int in_syscall */ |
970 | bralid r15, do_signal; /* Handle any signals */ | 842 | bralid r15, do_signal; /* Handle any signals */ |
971 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ | 843 | add r6, r0, r0; /* Arg 2: sigset_t *oldset */ |
972 | 844 | ||
973 | |||
974 | /* Finally, return to user state. */ | 845 | /* Finally, return to user state. */ |
975 | 1: swi r0, r0, PER_CPU(KM); /* Now officially in user state. */ | 846 | 1: swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ |
976 | swi CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */ | ||
977 | VM_OFF; | 847 | VM_OFF; |
978 | tophys(r1,r1); | 848 | tophys(r1,r1); |
979 | 849 | /* MS: Restore all regs */ | |
980 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | ||
981 | lwi r4, r1, PTO+PT_R4; | ||
982 | RESTORE_REGS | 850 | RESTORE_REGS |
983 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 851 | lwi r17, r1, PTO+PT_R17; |
984 | 852 | lwi r16, r1, PTO+PT_R16; | |
985 | 853 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */ | |
986 | lwi r1, r1, PT_R1 - PT_SIZE; | 854 | lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */ |
987 | /* Restore user stack pointer. */ | 855 | DBTRAP_return_user: /* MS: Make global symbol for debugging */ |
988 | bri 6f; | 856 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */ |
857 | nop; | ||
989 | 858 | ||
990 | /* Return to kernel state. */ | 859 | /* MS: Return to kernel state - kgdb */ |
991 | 2: VM_OFF; | 860 | 2: VM_OFF; |
992 | tophys(r1,r1); | 861 | tophys(r1,r1); |
993 | lwi r3, r1, PTO+PT_R3; /* restore saved r3, r4 registers */ | 862 | /* MS: Restore all regs */ |
994 | lwi r4, r1, PTO+PT_R4; | ||
995 | RESTORE_REGS | 863 | RESTORE_REGS |
996 | addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */ | 864 | lwi r14, r1, PTO+PT_R14; |
997 | 865 | lwi r16, r1, PTO+PT_PC; | |
866 | lwi r17, r1, PTO+PT_R17; | ||
867 | addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */ | ||
998 | tovirt(r1,r1); | 868 | tovirt(r1,r1); |
999 | 6: | 869 | DBTRAP_return_kernel: /* MS: Make global symbol for debugging */ |
1000 | DBTRAP_return: /* Make global symbol for debugging */ | 870 | rtbd r16, 0; /* MS: Instructions to return from a debug trap */ |
1001 | rtbd r14, 0; /* Instructions to return from an IRQ */ | ||
1002 | nop; | 871 | nop; |
1003 | 872 | ||
1004 | 873 | ||
1005 | |||
1006 | ENTRY(_switch_to) | 874 | ENTRY(_switch_to) |
1007 | /* prepare return value */ | 875 | /* prepare return value */ |
1008 | addk r3, r0, CURRENT_TASK | 876 | addk r3, r0, CURRENT_TASK |
@@ -1037,16 +905,12 @@ ENTRY(_switch_to) | |||
1037 | swi r30, r11, CC_R30 | 905 | swi r30, r11, CC_R30 |
1038 | /* special purpose registers */ | 906 | /* special purpose registers */ |
1039 | mfs r12, rmsr | 907 | mfs r12, rmsr |
1040 | nop | ||
1041 | swi r12, r11, CC_MSR | 908 | swi r12, r11, CC_MSR |
1042 | mfs r12, rear | 909 | mfs r12, rear |
1043 | nop | ||
1044 | swi r12, r11, CC_EAR | 910 | swi r12, r11, CC_EAR |
1045 | mfs r12, resr | 911 | mfs r12, resr |
1046 | nop | ||
1047 | swi r12, r11, CC_ESR | 912 | swi r12, r11, CC_ESR |
1048 | mfs r12, rfsr | 913 | mfs r12, rfsr |
1049 | nop | ||
1050 | swi r12, r11, CC_FSR | 914 | swi r12, r11, CC_FSR |
1051 | 915 | ||
1052 | /* update r31, the current-give me pointer to task which will be next */ | 916 | /* update r31, the current-give me pointer to task which will be next */ |
@@ -1085,10 +949,8 @@ ENTRY(_switch_to) | |||
1085 | /* special purpose registers */ | 949 | /* special purpose registers */ |
1086 | lwi r12, r11, CC_FSR | 950 | lwi r12, r11, CC_FSR |
1087 | mts rfsr, r12 | 951 | mts rfsr, r12 |
1088 | nop | ||
1089 | lwi r12, r11, CC_MSR | 952 | lwi r12, r11, CC_MSR |
1090 | mts rmsr, r12 | 953 | mts rmsr, r12 |
1091 | nop | ||
1092 | 954 | ||
1093 | rtsd r15, 8 | 955 | rtsd r15, 8 |
1094 | nop | 956 | nop |
@@ -1096,15 +958,6 @@ ENTRY(_switch_to) | |||
1096 | ENTRY(_reset) | 958 | ENTRY(_reset) |
1097 | brai 0x70; /* Jump back to FS-boot */ | 959 | brai 0x70; /* Jump back to FS-boot */ |
1098 | 960 | ||
1099 | ENTRY(_break) | ||
1100 | mfs r5, rmsr | ||
1101 | nop | ||
1102 | swi r5, r0, 0x250 + TOPHYS(r0_ram) | ||
1103 | mfs r5, resr | ||
1104 | nop | ||
1105 | swi r5, r0, 0x254 + TOPHYS(r0_ram) | ||
1106 | bri 0 | ||
1107 | |||
1108 | /* These are compiled and loaded into high memory, then | 961 | /* These are compiled and loaded into high memory, then |
1109 | * copied into place in mach_early_setup */ | 962 | * copied into place in mach_early_setup */ |
1110 | .section .init.ivt, "ax" | 963 | .section .init.ivt, "ax" |
@@ -1116,14 +969,38 @@ ENTRY(_break) | |||
1116 | nop | 969 | nop |
1117 | brai TOPHYS(_user_exception); /* syscall handler */ | 970 | brai TOPHYS(_user_exception); /* syscall handler */ |
1118 | brai TOPHYS(_interrupt); /* Interrupt handler */ | 971 | brai TOPHYS(_interrupt); /* Interrupt handler */ |
1119 | brai TOPHYS(_break); /* nmi trap handler */ | 972 | brai TOPHYS(_debug_exception); /* debug trap handler */ |
1120 | brai TOPHYS(_hw_exception_handler); /* HW exception handler */ | 973 | brai TOPHYS(_hw_exception_handler); /* HW exception handler */ |
1121 | 974 | ||
1122 | .org 0x60 | ||
1123 | brai TOPHYS(_debug_exception); /* debug trap handler*/ | ||
1124 | |||
1125 | .section .rodata,"a" | 975 | .section .rodata,"a" |
1126 | #include "syscall_table.S" | 976 | #include "syscall_table.S" |
1127 | 977 | ||
1128 | syscall_table_size=(.-sys_call_table) | 978 | syscall_table_size=(.-sys_call_table) |
1129 | 979 | ||
980 | type_SYSCALL: | ||
981 | .ascii "SYSCALL\0" | ||
982 | type_IRQ: | ||
983 | .ascii "IRQ\0" | ||
984 | type_IRQ_PREEMPT: | ||
985 | .ascii "IRQ (PREEMPTED)\0" | ||
986 | type_SYSCALL_PREEMPT: | ||
987 | .ascii " SYSCALL (PREEMPTED)\0" | ||
988 | |||
989 | /* | ||
990 | * Trap decoding for stack unwinder | ||
991 | * Tuples are (start addr, end addr, string) | ||
992 | * If return address lies on [start addr, end addr], | ||
993 | * unwinder displays 'string' | ||
994 | */ | ||
995 | |||
996 | .align 4 | ||
997 | .global microblaze_trap_handlers | ||
998 | microblaze_trap_handlers: | ||
999 | /* Exact matches come first */ | ||
1000 | .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL | ||
1001 | .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ | ||
1002 | /* Fuzzy matches go here */ | ||
1003 | .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT | ||
1004 | .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT | ||
1005 | /* End of table */ | ||
1006 | .word 0 ; .word 0 ; .word 0 | ||
diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index 02cbdfe5aa8d..b98ee8d0c1cd 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c | |||
@@ -48,12 +48,17 @@ void die(const char *str, struct pt_regs *fp, long err) | |||
48 | do_exit(err); | 48 | do_exit(err); |
49 | } | 49 | } |
50 | 50 | ||
51 | /* for user application debugging */ | ||
52 | void sw_exception(struct pt_regs *regs) | ||
53 | { | ||
54 | _exception(SIGTRAP, regs, TRAP_BRKPT, regs->r16); | ||
55 | } | ||
56 | |||
51 | void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) | 57 | void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) |
52 | { | 58 | { |
53 | siginfo_t info; | 59 | siginfo_t info; |
54 | 60 | ||
55 | if (kernel_mode(regs)) { | 61 | if (kernel_mode(regs)) { |
56 | debugger(regs); | ||
57 | die("Exception in kernel mode", regs, signr); | 62 | die("Exception in kernel mode", regs, signr); |
58 | } | 63 | } |
59 | info.si_signo = signr; | 64 | info.si_signo = signr; |
@@ -143,7 +148,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, | |||
143 | #ifdef CONFIG_MMU | 148 | #ifdef CONFIG_MMU |
144 | case MICROBLAZE_PRIVILEGED_EXCEPTION: | 149 | case MICROBLAZE_PRIVILEGED_EXCEPTION: |
145 | pr_debug(KERN_WARNING "Privileged exception\n"); | 150 | pr_debug(KERN_WARNING "Privileged exception\n"); |
146 | /* "brk r0,r0" - used as debug breakpoint */ | 151 | /* "brk r0,r0" - used as debug breakpoint - old toolchain */ |
147 | if (get_user(code, (unsigned long *)regs->pc) == 0 | 152 | if (get_user(code, (unsigned long *)regs->pc) == 0 |
148 | && code == 0x980c0000) { | 153 | && code == 0x980c0000) { |
149 | _exception(SIGTRAP, regs, TRAP_BRKPT, addr); | 154 | _exception(SIGTRAP, regs, TRAP_BRKPT, addr); |
diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index 1bf739888260..42434008209e 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S | |||
@@ -43,10 +43,10 @@ | |||
43 | .global empty_zero_page | 43 | .global empty_zero_page |
44 | .align 12 | 44 | .align 12 |
45 | empty_zero_page: | 45 | empty_zero_page: |
46 | .space 4096 | 46 | .space PAGE_SIZE |
47 | .global swapper_pg_dir | 47 | .global swapper_pg_dir |
48 | swapper_pg_dir: | 48 | swapper_pg_dir: |
49 | .space 4096 | 49 | .space PAGE_SIZE |
50 | 50 | ||
51 | #endif /* CONFIG_MMU */ | 51 | #endif /* CONFIG_MMU */ |
52 | 52 | ||
diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index 995a2123635b..781195438ee6 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S | |||
@@ -78,9 +78,6 @@ | |||
78 | #include <asm/asm-offsets.h> | 78 | #include <asm/asm-offsets.h> |
79 | 79 | ||
80 | /* Helpful Macros */ | 80 | /* Helpful Macros */ |
81 | #ifndef CONFIG_MMU | ||
82 | #define EX_HANDLER_STACK_SIZ (4*19) | ||
83 | #endif | ||
84 | #define NUM_TO_REG(num) r ## num | 81 | #define NUM_TO_REG(num) r ## num |
85 | 82 | ||
86 | #ifdef CONFIG_MMU | 83 | #ifdef CONFIG_MMU |
@@ -988,6 +985,7 @@ ex_unaligned_fixup: | |||
988 | .end _unaligned_data_exception | 985 | .end _unaligned_data_exception |
989 | #endif /* CONFIG_MMU */ | 986 | #endif /* CONFIG_MMU */ |
990 | 987 | ||
988 | .global ex_handler_unhandled | ||
991 | ex_handler_unhandled: | 989 | ex_handler_unhandled: |
992 | /* FIXME add handle function for unhandled exception - dump register */ | 990 | /* FIXME add handle function for unhandled exception - dump register */ |
993 | bri 0 | 991 | bri 0 |
diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c index 8f120aca123d..a9345fb4906a 100644 --- a/arch/microblaze/kernel/irq.c +++ b/arch/microblaze/kernel/irq.c | |||
@@ -17,26 +17,17 @@ | |||
17 | #include <linux/seq_file.h> | 17 | #include <linux/seq_file.h> |
18 | #include <linux/kernel_stat.h> | 18 | #include <linux/kernel_stat.h> |
19 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
20 | #include <linux/of_irq.h> | ||
20 | 21 | ||
21 | #include <asm/prom.h> | 22 | #include <asm/prom.h> |
22 | 23 | ||
23 | unsigned int irq_of_parse_and_map(struct device_node *dev, int index) | ||
24 | { | ||
25 | struct of_irq oirq; | ||
26 | |||
27 | if (of_irq_map_one(dev, index, &oirq)) | ||
28 | return NO_IRQ; | ||
29 | |||
30 | return oirq.specifier[0]; | ||
31 | } | ||
32 | EXPORT_SYMBOL_GPL(irq_of_parse_and_map); | ||
33 | |||
34 | static u32 concurrent_irq; | 24 | static u32 concurrent_irq; |
35 | 25 | ||
36 | void __irq_entry do_IRQ(struct pt_regs *regs) | 26 | void __irq_entry do_IRQ(struct pt_regs *regs) |
37 | { | 27 | { |
38 | unsigned int irq; | 28 | unsigned int irq; |
39 | struct pt_regs *old_regs = set_irq_regs(regs); | 29 | struct pt_regs *old_regs = set_irq_regs(regs); |
30 | trace_hardirqs_off(); | ||
40 | 31 | ||
41 | irq_enter(); | 32 | irq_enter(); |
42 | irq = get_irq(regs); | 33 | irq = get_irq(regs); |
@@ -53,6 +44,7 @@ next_irq: | |||
53 | 44 | ||
54 | irq_exit(); | 45 | irq_exit(); |
55 | set_irq_regs(old_regs); | 46 | set_irq_regs(old_regs); |
47 | trace_hardirqs_on(); | ||
56 | } | 48 | } |
57 | 49 | ||
58 | int show_interrupts(struct seq_file *p, void *v) | 50 | int show_interrupts(struct seq_file *p, void *v) |
@@ -104,7 +96,7 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) | |||
104 | EXPORT_SYMBOL_GPL(irq_create_mapping); | 96 | EXPORT_SYMBOL_GPL(irq_create_mapping); |
105 | 97 | ||
106 | unsigned int irq_create_of_mapping(struct device_node *controller, | 98 | unsigned int irq_create_of_mapping(struct device_node *controller, |
107 | u32 *intspec, unsigned int intsize) | 99 | const u32 *intspec, unsigned int intsize) |
108 | { | 100 | { |
109 | return intspec[0]; | 101 | return intspec[0]; |
110 | } | 102 | } |
diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c new file mode 100644 index 000000000000..bfc006b7f2d8 --- /dev/null +++ b/arch/microblaze/kernel/kgdb.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Microblaze KGDB support | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kgdb.h> | ||
10 | #include <linux/kdebug.h> | ||
11 | #include <linux/irq.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <asm/cacheflush.h> | ||
14 | #include <asm/asm-offsets.h> | ||
15 | #include <asm/pvr.h> | ||
16 | |||
17 | #define GDB_REG 0 | ||
18 | #define GDB_PC 32 | ||
19 | #define GDB_MSR 33 | ||
20 | #define GDB_EAR 34 | ||
21 | #define GDB_ESR 35 | ||
22 | #define GDB_FSR 36 | ||
23 | #define GDB_BTR 37 | ||
24 | #define GDB_PVR 38 | ||
25 | #define GDB_REDR 50 | ||
26 | #define GDB_RPID 51 | ||
27 | #define GDB_RZPR 52 | ||
28 | #define GDB_RTLBX 53 | ||
29 | #define GDB_RTLBSX 54 /* mfs can't read it */ | ||
30 | #define GDB_RTLBLO 55 | ||
31 | #define GDB_RTLBHI 56 | ||
32 | |||
33 | /* keep pvr separately because it is unchangeble */ | ||
34 | struct pvr_s pvr; | ||
35 | |||
36 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
37 | { | ||
38 | int i; | ||
39 | unsigned long *pt_regb = (unsigned long *)regs; | ||
40 | int temp; | ||
41 | /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ | ||
42 | for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
43 | gdb_regs[i] = pt_regb[i]; | ||
44 | |||
45 | /* Branch target register can't be changed */ | ||
46 | __asm__ __volatile__ ("mfs %0, rbtr;" : "=r"(temp) : ); | ||
47 | gdb_regs[GDB_BTR] = temp; | ||
48 | |||
49 | /* pvr part - we have 11 pvr regs */ | ||
50 | for (i = 0; i < sizeof(struct pvr_s)/4; i++) | ||
51 | gdb_regs[GDB_PVR + i] = pvr.pvr[i]; | ||
52 | |||
53 | /* read special registers - can't be changed */ | ||
54 | __asm__ __volatile__ ("mfs %0, redr;" : "=r"(temp) : ); | ||
55 | gdb_regs[GDB_REDR] = temp; | ||
56 | __asm__ __volatile__ ("mfs %0, rpid;" : "=r"(temp) : ); | ||
57 | gdb_regs[GDB_RPID] = temp; | ||
58 | __asm__ __volatile__ ("mfs %0, rzpr;" : "=r"(temp) : ); | ||
59 | gdb_regs[GDB_RZPR] = temp; | ||
60 | __asm__ __volatile__ ("mfs %0, rtlbx;" : "=r"(temp) : ); | ||
61 | gdb_regs[GDB_RTLBX] = temp; | ||
62 | __asm__ __volatile__ ("mfs %0, rtlblo;" : "=r"(temp) : ); | ||
63 | gdb_regs[GDB_RTLBLO] = temp; | ||
64 | __asm__ __volatile__ ("mfs %0, rtlbhi;" : "=r"(temp) : ); | ||
65 | gdb_regs[GDB_RTLBHI] = temp; | ||
66 | } | ||
67 | |||
68 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
69 | { | ||
70 | int i; | ||
71 | unsigned long *pt_regb = (unsigned long *)regs; | ||
72 | |||
73 | /* pt_regs and gdb_regs have the same 37 values. | ||
74 | * The rest of gdb_regs are unused and can't be changed. | ||
75 | * r0 register value can't be changed too. */ | ||
76 | for (i = 1; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
77 | pt_regb[i] = gdb_regs[i]; | ||
78 | } | ||
79 | |||
80 | void microblaze_kgdb_break(struct pt_regs *regs) | ||
81 | { | ||
82 | if (kgdb_handle_exception(1, SIGTRAP, 0, regs) != 0) | ||
83 | return 0; | ||
84 | |||
85 | /* Jump over the first arch_kgdb_breakpoint which is barrier to | ||
86 | * get kgdb work. The same solution is used for powerpc */ | ||
87 | if (*(u32 *) (regs->pc) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr)) | ||
88 | regs->pc += BREAK_INSTR_SIZE; | ||
89 | } | ||
90 | |||
91 | /* untested */ | ||
92 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
93 | { | ||
94 | int i; | ||
95 | unsigned long *pt_regb = (unsigned long *)(p->thread.regs); | ||
96 | |||
97 | /* registers r0 - r31, pc, msr, ear, esr, fsr + do not save pt_mode */ | ||
98 | for (i = 0; i < (sizeof(struct pt_regs) / 4) - 1; i++) | ||
99 | gdb_regs[i] = pt_regb[i]; | ||
100 | |||
101 | /* pvr part - we have 11 pvr regs */ | ||
102 | for (i = 0; i < sizeof(struct pvr_s)/4; i++) | ||
103 | gdb_regs[GDB_PVR + i] = pvr.pvr[i]; | ||
104 | } | ||
105 | |||
106 | void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) | ||
107 | { | ||
108 | regs->pc = ip; | ||
109 | } | ||
110 | |||
111 | int kgdb_arch_handle_exception(int vector, int signo, int err_code, | ||
112 | char *remcom_in_buffer, char *remcom_out_buffer, | ||
113 | struct pt_regs *regs) | ||
114 | { | ||
115 | char *ptr; | ||
116 | unsigned long address; | ||
117 | int cpu = smp_processor_id(); | ||
118 | |||
119 | switch (remcom_in_buffer[0]) { | ||
120 | case 'c': | ||
121 | /* handle the optional parameter */ | ||
122 | ptr = &remcom_in_buffer[1]; | ||
123 | if (kgdb_hex2long(&ptr, &address)) | ||
124 | regs->pc = address; | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | return -1; /* this means that we do not want to exit from the handler */ | ||
129 | } | ||
130 | |||
131 | int kgdb_arch_init(void) | ||
132 | { | ||
133 | get_pvr(&pvr); /* Fill PVR structure */ | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | void kgdb_arch_exit(void) | ||
138 | { | ||
139 | /* Nothing to do */ | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Global data | ||
144 | */ | ||
145 | struct kgdb_arch arch_kgdb_ops = { | ||
146 | .gdb_bpt_instr = {0xba, 0x0c, 0x00, 0x18}, /* brki r16, 0x18 */ | ||
147 | }; | ||
diff --git a/arch/microblaze/kernel/misc.S b/arch/microblaze/kernel/misc.S index 0fb5fc6c1fc2..206da3da361f 100644 --- a/arch/microblaze/kernel/misc.S +++ b/arch/microblaze/kernel/misc.S | |||
@@ -76,7 +76,7 @@ early_console_reg_tlb_alloc: | |||
76 | * the UARTs nice and early. We use a 4k real==virtual mapping. | 76 | * the UARTs nice and early. We use a 4k real==virtual mapping. |
77 | */ | 77 | */ |
78 | ori r4, r0, MICROBLAZE_TLB_SIZE - 1 | 78 | ori r4, r0, MICROBLAZE_TLB_SIZE - 1 |
79 | mts rtlbx, r4 /* TLB slot 2 */ | 79 | mts rtlbx, r4 /* TLB slot 63 */ |
80 | 80 | ||
81 | or r4,r5,r0 | 81 | or r4,r5,r0 |
82 | andi r4,r4,0xfffff000 | 82 | andi r4,r4,0xfffff000 |
diff --git a/arch/microblaze/kernel/of_device.c b/arch/microblaze/kernel/of_device.c deleted file mode 100644 index b372787886ed..000000000000 --- a/arch/microblaze/kernel/of_device.c +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | #include <linux/string.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/of.h> | ||
4 | #include <linux/init.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <linux/mod_devicetable.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/of_device.h> | ||
9 | |||
10 | #include <linux/errno.h> | ||
11 | |||
12 | void of_device_make_bus_id(struct of_device *dev) | ||
13 | { | ||
14 | static atomic_t bus_no_reg_magic; | ||
15 | struct device_node *node = dev->dev.of_node; | ||
16 | const u32 *reg; | ||
17 | u64 addr; | ||
18 | int magic; | ||
19 | |||
20 | /* | ||
21 | * For MMIO, get the physical address | ||
22 | */ | ||
23 | reg = of_get_property(node, "reg", NULL); | ||
24 | if (reg) { | ||
25 | addr = of_translate_address(node, reg); | ||
26 | if (addr != OF_BAD_ADDR) { | ||
27 | dev_set_name(&dev->dev, "%llx.%s", | ||
28 | (unsigned long long)addr, node->name); | ||
29 | return; | ||
30 | } | ||
31 | } | ||
32 | |||
33 | /* | ||
34 | * No BusID, use the node name and add a globally incremented | ||
35 | * counter (and pray...) | ||
36 | */ | ||
37 | magic = atomic_add_return(1, &bus_no_reg_magic); | ||
38 | dev_set_name(&dev->dev, "%s.%d", node->name, magic - 1); | ||
39 | } | ||
40 | EXPORT_SYMBOL(of_device_make_bus_id); | ||
41 | |||
42 | struct of_device *of_device_alloc(struct device_node *np, | ||
43 | const char *bus_id, | ||
44 | struct device *parent) | ||
45 | { | ||
46 | struct of_device *dev; | ||
47 | |||
48 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
49 | if (!dev) | ||
50 | return NULL; | ||
51 | |||
52 | dev->dev.of_node = of_node_get(np); | ||
53 | dev->dev.dma_mask = &dev->archdata.dma_mask; | ||
54 | dev->dev.parent = parent; | ||
55 | dev->dev.release = of_release_dev; | ||
56 | |||
57 | if (bus_id) | ||
58 | dev_set_name(&dev->dev, bus_id); | ||
59 | else | ||
60 | of_device_make_bus_id(dev); | ||
61 | |||
62 | return dev; | ||
63 | } | ||
64 | EXPORT_SYMBOL(of_device_alloc); | ||
65 | |||
66 | int of_device_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
67 | { | ||
68 | struct of_device *ofdev; | ||
69 | const char *compat; | ||
70 | int seen = 0, cplen, sl; | ||
71 | |||
72 | if (!dev) | ||
73 | return -ENODEV; | ||
74 | |||
75 | ofdev = to_of_device(dev); | ||
76 | |||
77 | if (add_uevent_var(env, "OF_NAME=%s", ofdev->dev.of_node->name)) | ||
78 | return -ENOMEM; | ||
79 | |||
80 | if (add_uevent_var(env, "OF_TYPE=%s", ofdev->dev.of_node->type)) | ||
81 | return -ENOMEM; | ||
82 | |||
83 | /* Since the compatible field can contain pretty much anything | ||
84 | * it's not really legal to split it out with commas. We split it | ||
85 | * up using a number of environment variables instead. */ | ||
86 | |||
87 | compat = of_get_property(ofdev->dev.of_node, "compatible", &cplen); | ||
88 | while (compat && *compat && cplen > 0) { | ||
89 | if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat)) | ||
90 | return -ENOMEM; | ||
91 | |||
92 | sl = strlen(compat) + 1; | ||
93 | compat += sl; | ||
94 | cplen -= sl; | ||
95 | seen++; | ||
96 | } | ||
97 | |||
98 | if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen)) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | /* modalias is trickier, we add it in 2 steps */ | ||
102 | if (add_uevent_var(env, "MODALIAS=")) | ||
103 | return -ENOMEM; | ||
104 | sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1], | ||
105 | sizeof(env->buf) - env->buflen); | ||
106 | if (sl >= (sizeof(env->buf) - env->buflen)) | ||
107 | return -ENOMEM; | ||
108 | env->buflen += sl; | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | EXPORT_SYMBOL(of_device_uevent); | ||
diff --git a/arch/microblaze/kernel/of_platform.c b/arch/microblaze/kernel/of_platform.c deleted file mode 100644 index ccf6f4257f4b..000000000000 --- a/arch/microblaze/kernel/of_platform.c +++ /dev/null | |||
@@ -1,200 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. | ||
3 | * <benh@kernel.crashing.org> | ||
4 | * and Arnd Bergmann, IBM Corp. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #undef DEBUG | ||
14 | |||
15 | #include <linux/string.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mod_devicetable.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | |||
25 | #include <linux/errno.h> | ||
26 | #include <linux/topology.h> | ||
27 | #include <asm/atomic.h> | ||
28 | |||
29 | struct bus_type of_platform_bus_type = { | ||
30 | .uevent = of_device_uevent, | ||
31 | }; | ||
32 | EXPORT_SYMBOL(of_platform_bus_type); | ||
33 | |||
34 | static int __init of_bus_driver_init(void) | ||
35 | { | ||
36 | return of_bus_type_init(&of_platform_bus_type, "of_platform"); | ||
37 | } | ||
38 | postcore_initcall(of_bus_driver_init); | ||
39 | |||
40 | struct of_device *of_platform_device_create(struct device_node *np, | ||
41 | const char *bus_id, | ||
42 | struct device *parent) | ||
43 | { | ||
44 | struct of_device *dev; | ||
45 | |||
46 | dev = of_device_alloc(np, bus_id, parent); | ||
47 | if (!dev) | ||
48 | return NULL; | ||
49 | |||
50 | dev->archdata.dma_mask = 0xffffffffUL; | ||
51 | dev->dev.bus = &of_platform_bus_type; | ||
52 | |||
53 | /* We do not fill the DMA ops for platform devices by default. | ||
54 | * This is currently the responsibility of the platform code | ||
55 | * to do such, possibly using a device notifier | ||
56 | */ | ||
57 | |||
58 | if (of_device_register(dev) != 0) { | ||
59 | of_device_free(dev); | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | return dev; | ||
64 | } | ||
65 | EXPORT_SYMBOL(of_platform_device_create); | ||
66 | |||
67 | /** | ||
68 | * of_platform_bus_create - Create an OF device for a bus node and all its | ||
69 | * children. Optionally recursively instanciate matching busses. | ||
70 | * @bus: device node of the bus to instanciate | ||
71 | * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to | ||
72 | * disallow recursive creation of child busses | ||
73 | */ | ||
74 | static int of_platform_bus_create(const struct device_node *bus, | ||
75 | const struct of_device_id *matches, | ||
76 | struct device *parent) | ||
77 | { | ||
78 | struct device_node *child; | ||
79 | struct of_device *dev; | ||
80 | int rc = 0; | ||
81 | |||
82 | for_each_child_of_node(bus, child) { | ||
83 | pr_debug(" create child: %s\n", child->full_name); | ||
84 | dev = of_platform_device_create(child, NULL, parent); | ||
85 | if (dev == NULL) | ||
86 | rc = -ENOMEM; | ||
87 | else if (!of_match_node(matches, child)) | ||
88 | continue; | ||
89 | if (rc == 0) { | ||
90 | pr_debug(" and sub busses\n"); | ||
91 | rc = of_platform_bus_create(child, matches, &dev->dev); | ||
92 | } | ||
93 | if (rc) { | ||
94 | of_node_put(child); | ||
95 | break; | ||
96 | } | ||
97 | } | ||
98 | return rc; | ||
99 | } | ||
100 | |||
101 | |||
102 | /** | ||
103 | * of_platform_bus_probe - Probe the device-tree for platform busses | ||
104 | * @root: parent of the first level to probe or NULL for the root of the tree | ||
105 | * @matches: match table, NULL to use the default | ||
106 | * @parent: parent to hook devices from, NULL for toplevel | ||
107 | * | ||
108 | * Note that children of the provided root are not instanciated as devices | ||
109 | * unless the specified root itself matches the bus list and is not NULL. | ||
110 | */ | ||
111 | |||
112 | int of_platform_bus_probe(struct device_node *root, | ||
113 | const struct of_device_id *matches, | ||
114 | struct device *parent) | ||
115 | { | ||
116 | struct device_node *child; | ||
117 | struct of_device *dev; | ||
118 | int rc = 0; | ||
119 | |||
120 | if (matches == NULL) | ||
121 | matches = of_default_bus_ids; | ||
122 | if (matches == OF_NO_DEEP_PROBE) | ||
123 | return -EINVAL; | ||
124 | if (root == NULL) | ||
125 | root = of_find_node_by_path("/"); | ||
126 | else | ||
127 | of_node_get(root); | ||
128 | |||
129 | pr_debug("of_platform_bus_probe()\n"); | ||
130 | pr_debug(" starting at: %s\n", root->full_name); | ||
131 | |||
132 | /* Do a self check of bus type, if there's a match, create | ||
133 | * children | ||
134 | */ | ||
135 | if (of_match_node(matches, root)) { | ||
136 | pr_debug(" root match, create all sub devices\n"); | ||
137 | dev = of_platform_device_create(root, NULL, parent); | ||
138 | if (dev == NULL) { | ||
139 | rc = -ENOMEM; | ||
140 | goto bail; | ||
141 | } | ||
142 | pr_debug(" create all sub busses\n"); | ||
143 | rc = of_platform_bus_create(root, matches, &dev->dev); | ||
144 | goto bail; | ||
145 | } | ||
146 | for_each_child_of_node(root, child) { | ||
147 | if (!of_match_node(matches, child)) | ||
148 | continue; | ||
149 | |||
150 | pr_debug(" match: %s\n", child->full_name); | ||
151 | dev = of_platform_device_create(child, NULL, parent); | ||
152 | if (dev == NULL) | ||
153 | rc = -ENOMEM; | ||
154 | else | ||
155 | rc = of_platform_bus_create(child, matches, &dev->dev); | ||
156 | if (rc) { | ||
157 | of_node_put(child); | ||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | bail: | ||
162 | of_node_put(root); | ||
163 | return rc; | ||
164 | } | ||
165 | EXPORT_SYMBOL(of_platform_bus_probe); | ||
166 | |||
167 | static int of_dev_node_match(struct device *dev, void *data) | ||
168 | { | ||
169 | return to_of_device(dev)->dev.of_node == data; | ||
170 | } | ||
171 | |||
172 | struct of_device *of_find_device_by_node(struct device_node *np) | ||
173 | { | ||
174 | struct device *dev; | ||
175 | |||
176 | dev = bus_find_device(&of_platform_bus_type, | ||
177 | NULL, np, of_dev_node_match); | ||
178 | if (dev) | ||
179 | return to_of_device(dev); | ||
180 | return NULL; | ||
181 | } | ||
182 | EXPORT_SYMBOL(of_find_device_by_node); | ||
183 | |||
184 | static int of_dev_phandle_match(struct device *dev, void *data) | ||
185 | { | ||
186 | phandle *ph = data; | ||
187 | return to_of_device(dev)->dev.of_node->phandle == *ph; | ||
188 | } | ||
189 | |||
190 | struct of_device *of_find_device_by_phandle(phandle ph) | ||
191 | { | ||
192 | struct device *dev; | ||
193 | |||
194 | dev = bus_find_device(&of_platform_bus_type, | ||
195 | NULL, &ph, of_dev_phandle_match); | ||
196 | if (dev) | ||
197 | return to_of_device(dev); | ||
198 | return NULL; | ||
199 | } | ||
200 | EXPORT_SYMBOL(of_find_device_by_phandle); | ||
diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 09bed44dfcd3..ba7c4b16ed35 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c | |||
@@ -76,8 +76,11 @@ __setup("hlt", hlt_setup); | |||
76 | void default_idle(void) | 76 | void default_idle(void) |
77 | { | 77 | { |
78 | if (likely(hlt_counter)) { | 78 | if (likely(hlt_counter)) { |
79 | while (!need_resched()) | 79 | local_irq_disable(); |
80 | cpu_relax(); | 80 | stop_critical_timings(); |
81 | cpu_relax(); | ||
82 | start_critical_timings(); | ||
83 | local_irq_enable(); | ||
81 | } else { | 84 | } else { |
82 | clear_thread_flag(TIF_POLLING_NRFLAG); | 85 | clear_thread_flag(TIF_POLLING_NRFLAG); |
83 | smp_mb__after_clear_bit(); | 86 | smp_mb__after_clear_bit(); |
diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c index bf7e6c27e318..d33ba17601fa 100644 --- a/arch/microblaze/kernel/prom_parse.c +++ b/arch/microblaze/kernel/prom_parse.c | |||
@@ -6,219 +6,11 @@ | |||
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
7 | #include <linux/ioport.h> | 7 | #include <linux/ioport.h> |
8 | #include <linux/etherdevice.h> | 8 | #include <linux/etherdevice.h> |
9 | #include <linux/of_address.h> | ||
9 | #include <asm/prom.h> | 10 | #include <asm/prom.h> |
10 | #include <asm/pci-bridge.h> | 11 | #include <asm/pci-bridge.h> |
11 | 12 | ||
12 | #define PRu64 "%llx" | ||
13 | |||
14 | /* Max address size we deal with */ | ||
15 | #define OF_MAX_ADDR_CELLS 4 | ||
16 | #define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ | ||
17 | (ns) > 0) | ||
18 | |||
19 | static struct of_bus *of_match_bus(struct device_node *np); | ||
20 | static int __of_address_to_resource(struct device_node *dev, | ||
21 | const u32 *addrp, u64 size, unsigned int flags, | ||
22 | struct resource *r); | ||
23 | |||
24 | /* Debug utility */ | ||
25 | #ifdef DEBUG | ||
26 | static void of_dump_addr(const char *s, const u32 *addr, int na) | ||
27 | { | ||
28 | printk(KERN_INFO "%s", s); | ||
29 | while (na--) | ||
30 | printk(KERN_INFO " %08x", *(addr++)); | ||
31 | printk(KERN_INFO "\n"); | ||
32 | } | ||
33 | #else | ||
34 | static void of_dump_addr(const char *s, const u32 *addr, int na) { } | ||
35 | #endif | ||
36 | |||
37 | /* Callbacks for bus specific translators */ | ||
38 | struct of_bus { | ||
39 | const char *name; | ||
40 | const char *addresses; | ||
41 | int (*match)(struct device_node *parent); | ||
42 | void (*count_cells)(struct device_node *child, | ||
43 | int *addrc, int *sizec); | ||
44 | u64 (*map)(u32 *addr, const u32 *range, | ||
45 | int na, int ns, int pna); | ||
46 | int (*translate)(u32 *addr, u64 offset, int na); | ||
47 | unsigned int (*get_flags)(const u32 *addr); | ||
48 | }; | ||
49 | |||
50 | /* | ||
51 | * Default translator (generic bus) | ||
52 | */ | ||
53 | |||
54 | static void of_bus_default_count_cells(struct device_node *dev, | ||
55 | int *addrc, int *sizec) | ||
56 | { | ||
57 | if (addrc) | ||
58 | *addrc = of_n_addr_cells(dev); | ||
59 | if (sizec) | ||
60 | *sizec = of_n_size_cells(dev); | ||
61 | } | ||
62 | |||
63 | static u64 of_bus_default_map(u32 *addr, const u32 *range, | ||
64 | int na, int ns, int pna) | ||
65 | { | ||
66 | u64 cp, s, da; | ||
67 | |||
68 | cp = of_read_number(range, na); | ||
69 | s = of_read_number(range + na + pna, ns); | ||
70 | da = of_read_number(addr, na); | ||
71 | |||
72 | pr_debug("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n", | ||
73 | cp, s, da); | ||
74 | |||
75 | if (da < cp || da >= (cp + s)) | ||
76 | return OF_BAD_ADDR; | ||
77 | return da - cp; | ||
78 | } | ||
79 | |||
80 | static int of_bus_default_translate(u32 *addr, u64 offset, int na) | ||
81 | { | ||
82 | u64 a = of_read_number(addr, na); | ||
83 | memset(addr, 0, na * 4); | ||
84 | a += offset; | ||
85 | if (na > 1) | ||
86 | addr[na - 2] = a >> 32; | ||
87 | addr[na - 1] = a & 0xffffffffu; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static unsigned int of_bus_default_get_flags(const u32 *addr) | ||
93 | { | ||
94 | return IORESOURCE_MEM; | ||
95 | } | ||
96 | |||
97 | #ifdef CONFIG_PCI | 13 | #ifdef CONFIG_PCI |
98 | /* | ||
99 | * PCI bus specific translator | ||
100 | */ | ||
101 | |||
102 | static int of_bus_pci_match(struct device_node *np) | ||
103 | { | ||
104 | /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */ | ||
105 | return !strcmp(np->type, "pci") || !strcmp(np->type, "vci"); | ||
106 | } | ||
107 | |||
108 | static void of_bus_pci_count_cells(struct device_node *np, | ||
109 | int *addrc, int *sizec) | ||
110 | { | ||
111 | if (addrc) | ||
112 | *addrc = 3; | ||
113 | if (sizec) | ||
114 | *sizec = 2; | ||
115 | } | ||
116 | |||
117 | static u64 of_bus_pci_map(u32 *addr, const u32 *range, int na, int ns, int pna) | ||
118 | { | ||
119 | u64 cp, s, da; | ||
120 | |||
121 | /* Check address type match */ | ||
122 | if ((addr[0] ^ range[0]) & 0x03000000) | ||
123 | return OF_BAD_ADDR; | ||
124 | |||
125 | /* Read address values, skipping high cell */ | ||
126 | cp = of_read_number(range + 1, na - 1); | ||
127 | s = of_read_number(range + na + pna, ns); | ||
128 | da = of_read_number(addr + 1, na - 1); | ||
129 | |||
130 | pr_debug("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); | ||
131 | |||
132 | if (da < cp || da >= (cp + s)) | ||
133 | return OF_BAD_ADDR; | ||
134 | return da - cp; | ||
135 | } | ||
136 | |||
137 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) | ||
138 | { | ||
139 | return of_bus_default_translate(addr + 1, offset, na - 1); | ||
140 | } | ||
141 | |||
142 | static unsigned int of_bus_pci_get_flags(const u32 *addr) | ||
143 | { | ||
144 | unsigned int flags = 0; | ||
145 | u32 w = addr[0]; | ||
146 | |||
147 | switch ((w >> 24) & 0x03) { | ||
148 | case 0x01: | ||
149 | flags |= IORESOURCE_IO; | ||
150 | break; | ||
151 | case 0x02: /* 32 bits */ | ||
152 | case 0x03: /* 64 bits */ | ||
153 | flags |= IORESOURCE_MEM; | ||
154 | break; | ||
155 | } | ||
156 | if (w & 0x40000000) | ||
157 | flags |= IORESOURCE_PREFETCH; | ||
158 | return flags; | ||
159 | } | ||
160 | |||
161 | const u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, | ||
162 | unsigned int *flags) | ||
163 | { | ||
164 | const u32 *prop; | ||
165 | unsigned int psize; | ||
166 | struct device_node *parent; | ||
167 | struct of_bus *bus; | ||
168 | int onesize, i, na, ns; | ||
169 | |||
170 | /* Get parent & match bus type */ | ||
171 | parent = of_get_parent(dev); | ||
172 | if (parent == NULL) | ||
173 | return NULL; | ||
174 | bus = of_match_bus(parent); | ||
175 | if (strcmp(bus->name, "pci")) { | ||
176 | of_node_put(parent); | ||
177 | return NULL; | ||
178 | } | ||
179 | bus->count_cells(dev, &na, &ns); | ||
180 | of_node_put(parent); | ||
181 | if (!OF_CHECK_COUNTS(na, ns)) | ||
182 | return NULL; | ||
183 | |||
184 | /* Get "reg" or "assigned-addresses" property */ | ||
185 | prop = of_get_property(dev, bus->addresses, &psize); | ||
186 | if (prop == NULL) | ||
187 | return NULL; | ||
188 | psize /= 4; | ||
189 | |||
190 | onesize = na + ns; | ||
191 | for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) | ||
192 | if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { | ||
193 | if (size) | ||
194 | *size = of_read_number(prop + na, ns); | ||
195 | if (flags) | ||
196 | *flags = bus->get_flags(prop); | ||
197 | return prop; | ||
198 | } | ||
199 | return NULL; | ||
200 | } | ||
201 | EXPORT_SYMBOL(of_get_pci_address); | ||
202 | |||
203 | int of_pci_address_to_resource(struct device_node *dev, int bar, | ||
204 | struct resource *r) | ||
205 | { | ||
206 | const u32 *addrp; | ||
207 | u64 size; | ||
208 | unsigned int flags; | ||
209 | |||
210 | addrp = of_get_pci_address(dev, bar, &size, &flags); | ||
211 | if (addrp == NULL) | ||
212 | return -EINVAL; | ||
213 | return __of_address_to_resource(dev, addrp, size, flags, r); | ||
214 | } | ||
215 | EXPORT_SYMBOL_GPL(of_pci_address_to_resource); | ||
216 | |||
217 | static u8 of_irq_pci_swizzle(u8 slot, u8 pin) | ||
218 | { | ||
219 | return (((pin - 1) + slot) % 4) + 1; | ||
220 | } | ||
221 | |||
222 | int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) | 14 | int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) |
223 | { | 15 | { |
224 | struct device_node *dn, *ppnode; | 16 | struct device_node *dn, *ppnode; |
@@ -293,331 +85,6 @@ int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) | |||
293 | EXPORT_SYMBOL_GPL(of_irq_map_pci); | 85 | EXPORT_SYMBOL_GPL(of_irq_map_pci); |
294 | #endif /* CONFIG_PCI */ | 86 | #endif /* CONFIG_PCI */ |
295 | 87 | ||
296 | /* | ||
297 | * ISA bus specific translator | ||
298 | */ | ||
299 | |||
300 | static int of_bus_isa_match(struct device_node *np) | ||
301 | { | ||
302 | return !strcmp(np->name, "isa"); | ||
303 | } | ||
304 | |||
305 | static void of_bus_isa_count_cells(struct device_node *child, | ||
306 | int *addrc, int *sizec) | ||
307 | { | ||
308 | if (addrc) | ||
309 | *addrc = 2; | ||
310 | if (sizec) | ||
311 | *sizec = 1; | ||
312 | } | ||
313 | |||
314 | static u64 of_bus_isa_map(u32 *addr, const u32 *range, int na, int ns, int pna) | ||
315 | { | ||
316 | u64 cp, s, da; | ||
317 | |||
318 | /* Check address type match */ | ||
319 | if ((addr[0] ^ range[0]) & 0x00000001) | ||
320 | return OF_BAD_ADDR; | ||
321 | |||
322 | /* Read address values, skipping high cell */ | ||
323 | cp = of_read_number(range + 1, na - 1); | ||
324 | s = of_read_number(range + na + pna, ns); | ||
325 | da = of_read_number(addr + 1, na - 1); | ||
326 | |||
327 | pr_debug("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da); | ||
328 | |||
329 | if (da < cp || da >= (cp + s)) | ||
330 | return OF_BAD_ADDR; | ||
331 | return da - cp; | ||
332 | } | ||
333 | |||
334 | static int of_bus_isa_translate(u32 *addr, u64 offset, int na) | ||
335 | { | ||
336 | return of_bus_default_translate(addr + 1, offset, na - 1); | ||
337 | } | ||
338 | |||
339 | static unsigned int of_bus_isa_get_flags(const u32 *addr) | ||
340 | { | ||
341 | unsigned int flags = 0; | ||
342 | u32 w = addr[0]; | ||
343 | |||
344 | if (w & 1) | ||
345 | flags |= IORESOURCE_IO; | ||
346 | else | ||
347 | flags |= IORESOURCE_MEM; | ||
348 | return flags; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * Array of bus specific translators | ||
353 | */ | ||
354 | |||
355 | static struct of_bus of_busses[] = { | ||
356 | #ifdef CONFIG_PCI | ||
357 | /* PCI */ | ||
358 | { | ||
359 | .name = "pci", | ||
360 | .addresses = "assigned-addresses", | ||
361 | .match = of_bus_pci_match, | ||
362 | .count_cells = of_bus_pci_count_cells, | ||
363 | .map = of_bus_pci_map, | ||
364 | .translate = of_bus_pci_translate, | ||
365 | .get_flags = of_bus_pci_get_flags, | ||
366 | }, | ||
367 | #endif /* CONFIG_PCI */ | ||
368 | /* ISA */ | ||
369 | { | ||
370 | .name = "isa", | ||
371 | .addresses = "reg", | ||
372 | .match = of_bus_isa_match, | ||
373 | .count_cells = of_bus_isa_count_cells, | ||
374 | .map = of_bus_isa_map, | ||
375 | .translate = of_bus_isa_translate, | ||
376 | .get_flags = of_bus_isa_get_flags, | ||
377 | }, | ||
378 | /* Default */ | ||
379 | { | ||
380 | .name = "default", | ||
381 | .addresses = "reg", | ||
382 | .match = NULL, | ||
383 | .count_cells = of_bus_default_count_cells, | ||
384 | .map = of_bus_default_map, | ||
385 | .translate = of_bus_default_translate, | ||
386 | .get_flags = of_bus_default_get_flags, | ||
387 | }, | ||
388 | }; | ||
389 | |||
390 | static struct of_bus *of_match_bus(struct device_node *np) | ||
391 | { | ||
392 | int i; | ||
393 | |||
394 | for (i = 0; i < ARRAY_SIZE(of_busses); i++) | ||
395 | if (!of_busses[i].match || of_busses[i].match(np)) | ||
396 | return &of_busses[i]; | ||
397 | BUG(); | ||
398 | return NULL; | ||
399 | } | ||
400 | |||
401 | static int of_translate_one(struct device_node *parent, struct of_bus *bus, | ||
402 | struct of_bus *pbus, u32 *addr, | ||
403 | int na, int ns, int pna) | ||
404 | { | ||
405 | const u32 *ranges; | ||
406 | unsigned int rlen; | ||
407 | int rone; | ||
408 | u64 offset = OF_BAD_ADDR; | ||
409 | |||
410 | /* Normally, an absence of a "ranges" property means we are | ||
411 | * crossing a non-translatable boundary, and thus the addresses | ||
412 | * below the current not cannot be converted to CPU physical ones. | ||
413 | * Unfortunately, while this is very clear in the spec, it's not | ||
414 | * what Apple understood, and they do have things like /uni-n or | ||
415 | * /ht nodes with no "ranges" property and a lot of perfectly | ||
416 | * useable mapped devices below them. Thus we treat the absence of | ||
417 | * "ranges" as equivalent to an empty "ranges" property which means | ||
418 | * a 1:1 translation at that level. It's up to the caller not to try | ||
419 | * to translate addresses that aren't supposed to be translated in | ||
420 | * the first place. --BenH. | ||
421 | */ | ||
422 | ranges = of_get_property(parent, "ranges", (int *) &rlen); | ||
423 | if (ranges == NULL || rlen == 0) { | ||
424 | offset = of_read_number(addr, na); | ||
425 | memset(addr, 0, pna * 4); | ||
426 | pr_debug("OF: no ranges, 1:1 translation\n"); | ||
427 | goto finish; | ||
428 | } | ||
429 | |||
430 | pr_debug("OF: walking ranges...\n"); | ||
431 | |||
432 | /* Now walk through the ranges */ | ||
433 | rlen /= 4; | ||
434 | rone = na + pna + ns; | ||
435 | for (; rlen >= rone; rlen -= rone, ranges += rone) { | ||
436 | offset = bus->map(addr, ranges, na, ns, pna); | ||
437 | if (offset != OF_BAD_ADDR) | ||
438 | break; | ||
439 | } | ||
440 | if (offset == OF_BAD_ADDR) { | ||
441 | pr_debug("OF: not found !\n"); | ||
442 | return 1; | ||
443 | } | ||
444 | memcpy(addr, ranges + na, 4 * pna); | ||
445 | |||
446 | finish: | ||
447 | of_dump_addr("OF: parent translation for:", addr, pna); | ||
448 | pr_debug("OF: with offset: "PRu64"\n", offset); | ||
449 | |||
450 | /* Translate it into parent bus space */ | ||
451 | return pbus->translate(addr, offset, pna); | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Translate an address from the device-tree into a CPU physical address, | ||
456 | * this walks up the tree and applies the various bus mappings on the | ||
457 | * way. | ||
458 | * | ||
459 | * Note: We consider that crossing any level with #size-cells == 0 to mean | ||
460 | * that translation is impossible (that is we are not dealing with a value | ||
461 | * that can be mapped to a cpu physical address). This is not really specified | ||
462 | * that way, but this is traditionally the way IBM at least do things | ||
463 | */ | ||
464 | u64 of_translate_address(struct device_node *dev, const u32 *in_addr) | ||
465 | { | ||
466 | struct device_node *parent = NULL; | ||
467 | struct of_bus *bus, *pbus; | ||
468 | u32 addr[OF_MAX_ADDR_CELLS]; | ||
469 | int na, ns, pna, pns; | ||
470 | u64 result = OF_BAD_ADDR; | ||
471 | |||
472 | pr_debug("OF: ** translation for device %s **\n", dev->full_name); | ||
473 | |||
474 | /* Increase refcount at current level */ | ||
475 | of_node_get(dev); | ||
476 | |||
477 | /* Get parent & match bus type */ | ||
478 | parent = of_get_parent(dev); | ||
479 | if (parent == NULL) | ||
480 | goto bail; | ||
481 | bus = of_match_bus(parent); | ||
482 | |||
483 | /* Cound address cells & copy address locally */ | ||
484 | bus->count_cells(dev, &na, &ns); | ||
485 | if (!OF_CHECK_COUNTS(na, ns)) { | ||
486 | printk(KERN_ERR "prom_parse: Bad cell count for %s\n", | ||
487 | dev->full_name); | ||
488 | goto bail; | ||
489 | } | ||
490 | memcpy(addr, in_addr, na * 4); | ||
491 | |||
492 | pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", | ||
493 | bus->name, na, ns, parent->full_name); | ||
494 | of_dump_addr("OF: translating address:", addr, na); | ||
495 | |||
496 | /* Translate */ | ||
497 | for (;;) { | ||
498 | /* Switch to parent bus */ | ||
499 | of_node_put(dev); | ||
500 | dev = parent; | ||
501 | parent = of_get_parent(dev); | ||
502 | |||
503 | /* If root, we have finished */ | ||
504 | if (parent == NULL) { | ||
505 | pr_debug("OF: reached root node\n"); | ||
506 | result = of_read_number(addr, na); | ||
507 | break; | ||
508 | } | ||
509 | |||
510 | /* Get new parent bus and counts */ | ||
511 | pbus = of_match_bus(parent); | ||
512 | pbus->count_cells(dev, &pna, &pns); | ||
513 | if (!OF_CHECK_COUNTS(pna, pns)) { | ||
514 | printk(KERN_ERR "prom_parse: Bad cell count for %s\n", | ||
515 | dev->full_name); | ||
516 | break; | ||
517 | } | ||
518 | |||
519 | pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", | ||
520 | pbus->name, pna, pns, parent->full_name); | ||
521 | |||
522 | /* Apply bus translation */ | ||
523 | if (of_translate_one(dev, bus, pbus, addr, na, ns, pna)) | ||
524 | break; | ||
525 | |||
526 | /* Complete the move up one level */ | ||
527 | na = pna; | ||
528 | ns = pns; | ||
529 | bus = pbus; | ||
530 | |||
531 | of_dump_addr("OF: one level translation:", addr, na); | ||
532 | } | ||
533 | bail: | ||
534 | of_node_put(parent); | ||
535 | of_node_put(dev); | ||
536 | |||
537 | return result; | ||
538 | } | ||
539 | EXPORT_SYMBOL(of_translate_address); | ||
540 | |||
541 | const u32 *of_get_address(struct device_node *dev, int index, u64 *size, | ||
542 | unsigned int *flags) | ||
543 | { | ||
544 | const u32 *prop; | ||
545 | unsigned int psize; | ||
546 | struct device_node *parent; | ||
547 | struct of_bus *bus; | ||
548 | int onesize, i, na, ns; | ||
549 | |||
550 | /* Get parent & match bus type */ | ||
551 | parent = of_get_parent(dev); | ||
552 | if (parent == NULL) | ||
553 | return NULL; | ||
554 | bus = of_match_bus(parent); | ||
555 | bus->count_cells(dev, &na, &ns); | ||
556 | of_node_put(parent); | ||
557 | if (!OF_CHECK_COUNTS(na, ns)) | ||
558 | return NULL; | ||
559 | |||
560 | /* Get "reg" or "assigned-addresses" property */ | ||
561 | prop = of_get_property(dev, bus->addresses, (int *) &psize); | ||
562 | if (prop == NULL) | ||
563 | return NULL; | ||
564 | psize /= 4; | ||
565 | |||
566 | onesize = na + ns; | ||
567 | for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) | ||
568 | if (i == index) { | ||
569 | if (size) | ||
570 | *size = of_read_number(prop + na, ns); | ||
571 | if (flags) | ||
572 | *flags = bus->get_flags(prop); | ||
573 | return prop; | ||
574 | } | ||
575 | return NULL; | ||
576 | } | ||
577 | EXPORT_SYMBOL(of_get_address); | ||
578 | |||
579 | static int __of_address_to_resource(struct device_node *dev, const u32 *addrp, | ||
580 | u64 size, unsigned int flags, | ||
581 | struct resource *r) | ||
582 | { | ||
583 | u64 taddr; | ||
584 | |||
585 | if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) | ||
586 | return -EINVAL; | ||
587 | taddr = of_translate_address(dev, addrp); | ||
588 | if (taddr == OF_BAD_ADDR) | ||
589 | return -EINVAL; | ||
590 | memset(r, 0, sizeof(struct resource)); | ||
591 | if (flags & IORESOURCE_IO) { | ||
592 | unsigned long port; | ||
593 | port = -1; /* pci_address_to_pio(taddr); */ | ||
594 | if (port == (unsigned long)-1) | ||
595 | return -EINVAL; | ||
596 | r->start = port; | ||
597 | r->end = port + size - 1; | ||
598 | } else { | ||
599 | r->start = taddr; | ||
600 | r->end = taddr + size - 1; | ||
601 | } | ||
602 | r->flags = flags; | ||
603 | r->name = dev->name; | ||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | int of_address_to_resource(struct device_node *dev, int index, | ||
608 | struct resource *r) | ||
609 | { | ||
610 | const u32 *addrp; | ||
611 | u64 size; | ||
612 | unsigned int flags; | ||
613 | |||
614 | addrp = of_get_address(dev, index, &size, &flags); | ||
615 | if (addrp == NULL) | ||
616 | return -EINVAL; | ||
617 | return __of_address_to_resource(dev, addrp, size, flags, r); | ||
618 | } | ||
619 | EXPORT_SYMBOL_GPL(of_address_to_resource); | ||
620 | |||
621 | void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, | 88 | void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, |
622 | unsigned long *busno, unsigned long *phys, unsigned long *size) | 89 | unsigned long *busno, unsigned long *phys, unsigned long *size) |
623 | { | 90 | { |
@@ -644,308 +111,6 @@ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, | |||
644 | *size = of_read_number(dma_window, cells); | 111 | *size = of_read_number(dma_window, cells); |
645 | } | 112 | } |
646 | 113 | ||
647 | /* | ||
648 | * Interrupt remapper | ||
649 | */ | ||
650 | |||
651 | static unsigned int of_irq_workarounds; | ||
652 | static struct device_node *of_irq_dflt_pic; | ||
653 | |||
654 | static struct device_node *of_irq_find_parent(struct device_node *child) | ||
655 | { | ||
656 | struct device_node *p; | ||
657 | const phandle *parp; | ||
658 | |||
659 | if (!of_node_get(child)) | ||
660 | return NULL; | ||
661 | |||
662 | do { | ||
663 | parp = of_get_property(child, "interrupt-parent", NULL); | ||
664 | if (parp == NULL) | ||
665 | p = of_get_parent(child); | ||
666 | else { | ||
667 | if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) | ||
668 | p = of_node_get(of_irq_dflt_pic); | ||
669 | else | ||
670 | p = of_find_node_by_phandle(*parp); | ||
671 | } | ||
672 | of_node_put(child); | ||
673 | child = p; | ||
674 | } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL); | ||
675 | |||
676 | return p; | ||
677 | } | ||
678 | |||
679 | /* This doesn't need to be called if you don't have any special workaround | ||
680 | * flags to pass | ||
681 | */ | ||
682 | void of_irq_map_init(unsigned int flags) | ||
683 | { | ||
684 | of_irq_workarounds = flags; | ||
685 | |||
686 | /* OldWorld, don't bother looking at other things */ | ||
687 | if (flags & OF_IMAP_OLDWORLD_MAC) | ||
688 | return; | ||
689 | |||
690 | /* If we don't have phandles, let's try to locate a default interrupt | ||
691 | * controller (happens when booting with BootX). We do a first match | ||
692 | * here, hopefully, that only ever happens on machines with one | ||
693 | * controller. | ||
694 | */ | ||
695 | if (flags & OF_IMAP_NO_PHANDLE) { | ||
696 | struct device_node *np; | ||
697 | |||
698 | for (np = NULL; (np = of_find_all_nodes(np)) != NULL;) { | ||
699 | if (of_get_property(np, "interrupt-controller", NULL) | ||
700 | == NULL) | ||
701 | continue; | ||
702 | /* Skip /chosen/interrupt-controller */ | ||
703 | if (strcmp(np->name, "chosen") == 0) | ||
704 | continue; | ||
705 | /* It seems like at least one person on this planet | ||
706 | * wants to use BootX on a machine with an AppleKiwi | ||
707 | * controller which happens to pretend to be an | ||
708 | * interrupt controller too. | ||
709 | */ | ||
710 | if (strcmp(np->name, "AppleKiwi") == 0) | ||
711 | continue; | ||
712 | /* I think we found one ! */ | ||
713 | of_irq_dflt_pic = np; | ||
714 | break; | ||
715 | } | ||
716 | } | ||
717 | |||
718 | } | ||
719 | |||
720 | int of_irq_map_raw(struct device_node *parent, const u32 *intspec, u32 ointsize, | ||
721 | const u32 *addr, struct of_irq *out_irq) | ||
722 | { | ||
723 | struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; | ||
724 | const u32 *tmp, *imap, *imask; | ||
725 | u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; | ||
726 | int imaplen, match, i; | ||
727 | |||
728 | pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...]," | ||
729 | "ointsize=%d\n", | ||
730 | parent->full_name, intspec[0], intspec[1], ointsize); | ||
731 | |||
732 | ipar = of_node_get(parent); | ||
733 | |||
734 | /* First get the #interrupt-cells property of the current cursor | ||
735 | * that tells us how to interpret the passed-in intspec. If there | ||
736 | * is none, we are nice and just walk up the tree | ||
737 | */ | ||
738 | do { | ||
739 | tmp = of_get_property(ipar, "#interrupt-cells", NULL); | ||
740 | if (tmp != NULL) { | ||
741 | intsize = *tmp; | ||
742 | break; | ||
743 | } | ||
744 | tnode = ipar; | ||
745 | ipar = of_irq_find_parent(ipar); | ||
746 | of_node_put(tnode); | ||
747 | } while (ipar); | ||
748 | if (ipar == NULL) { | ||
749 | pr_debug(" -> no parent found !\n"); | ||
750 | goto fail; | ||
751 | } | ||
752 | |||
753 | pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", | ||
754 | ipar->full_name, intsize); | ||
755 | |||
756 | if (ointsize != intsize) | ||
757 | return -EINVAL; | ||
758 | |||
759 | /* Look for this #address-cells. We have to implement the old linux | ||
760 | * trick of looking for the parent here as some device-trees rely on it | ||
761 | */ | ||
762 | old = of_node_get(ipar); | ||
763 | do { | ||
764 | tmp = of_get_property(old, "#address-cells", NULL); | ||
765 | tnode = of_get_parent(old); | ||
766 | of_node_put(old); | ||
767 | old = tnode; | ||
768 | } while (old && tmp == NULL); | ||
769 | of_node_put(old); | ||
770 | old = NULL; | ||
771 | addrsize = (tmp == NULL) ? 2 : *tmp; | ||
772 | |||
773 | pr_debug(" -> addrsize=%d\n", addrsize); | ||
774 | |||
775 | /* Now start the actual "proper" walk of the interrupt tree */ | ||
776 | while (ipar != NULL) { | ||
777 | /* Now check if cursor is an interrupt-controller and if it is | ||
778 | * then we are done | ||
779 | */ | ||
780 | if (of_get_property(ipar, "interrupt-controller", NULL) != | ||
781 | NULL) { | ||
782 | pr_debug(" -> got it !\n"); | ||
783 | memcpy(out_irq->specifier, intspec, | ||
784 | intsize * sizeof(u32)); | ||
785 | out_irq->size = intsize; | ||
786 | out_irq->controller = ipar; | ||
787 | of_node_put(old); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | /* Now look for an interrupt-map */ | ||
792 | imap = of_get_property(ipar, "interrupt-map", &imaplen); | ||
793 | /* No interrupt map, check for an interrupt parent */ | ||
794 | if (imap == NULL) { | ||
795 | pr_debug(" -> no map, getting parent\n"); | ||
796 | newpar = of_irq_find_parent(ipar); | ||
797 | goto skiplevel; | ||
798 | } | ||
799 | imaplen /= sizeof(u32); | ||
800 | |||
801 | /* Look for a mask */ | ||
802 | imask = of_get_property(ipar, "interrupt-map-mask", NULL); | ||
803 | |||
804 | /* If we were passed no "reg" property and we attempt to parse | ||
805 | * an interrupt-map, then #address-cells must be 0. | ||
806 | * Fail if it's not. | ||
807 | */ | ||
808 | if (addr == NULL && addrsize != 0) { | ||
809 | pr_debug(" -> no reg passed in when needed !\n"); | ||
810 | goto fail; | ||
811 | } | ||
812 | |||
813 | /* Parse interrupt-map */ | ||
814 | match = 0; | ||
815 | while (imaplen > (addrsize + intsize + 1) && !match) { | ||
816 | /* Compare specifiers */ | ||
817 | match = 1; | ||
818 | for (i = 0; i < addrsize && match; ++i) { | ||
819 | u32 mask = imask ? imask[i] : 0xffffffffu; | ||
820 | match = ((addr[i] ^ imap[i]) & mask) == 0; | ||
821 | } | ||
822 | for (; i < (addrsize + intsize) && match; ++i) { | ||
823 | u32 mask = imask ? imask[i] : 0xffffffffu; | ||
824 | match = | ||
825 | ((intspec[i-addrsize] ^ imap[i]) | ||
826 | & mask) == 0; | ||
827 | } | ||
828 | imap += addrsize + intsize; | ||
829 | imaplen -= addrsize + intsize; | ||
830 | |||
831 | pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); | ||
832 | |||
833 | /* Get the interrupt parent */ | ||
834 | if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) | ||
835 | newpar = of_node_get(of_irq_dflt_pic); | ||
836 | else | ||
837 | newpar = | ||
838 | of_find_node_by_phandle((phandle)*imap); | ||
839 | imap++; | ||
840 | --imaplen; | ||
841 | |||
842 | /* Check if not found */ | ||
843 | if (newpar == NULL) { | ||
844 | pr_debug(" -> imap parent not found !\n"); | ||
845 | goto fail; | ||
846 | } | ||
847 | |||
848 | /* Get #interrupt-cells and #address-cells of new | ||
849 | * parent | ||
850 | */ | ||
851 | tmp = of_get_property(newpar, "#interrupt-cells", NULL); | ||
852 | if (tmp == NULL) { | ||
853 | pr_debug(" -> parent lacks " | ||
854 | "#interrupt-cells!\n"); | ||
855 | goto fail; | ||
856 | } | ||
857 | newintsize = *tmp; | ||
858 | tmp = of_get_property(newpar, "#address-cells", NULL); | ||
859 | newaddrsize = (tmp == NULL) ? 0 : *tmp; | ||
860 | |||
861 | pr_debug(" -> newintsize=%d, newaddrsize=%d\n", | ||
862 | newintsize, newaddrsize); | ||
863 | |||
864 | /* Check for malformed properties */ | ||
865 | if (imaplen < (newaddrsize + newintsize)) | ||
866 | goto fail; | ||
867 | |||
868 | imap += newaddrsize + newintsize; | ||
869 | imaplen -= newaddrsize + newintsize; | ||
870 | |||
871 | pr_debug(" -> imaplen=%d\n", imaplen); | ||
872 | } | ||
873 | if (!match) | ||
874 | goto fail; | ||
875 | |||
876 | of_node_put(old); | ||
877 | old = of_node_get(newpar); | ||
878 | addrsize = newaddrsize; | ||
879 | intsize = newintsize; | ||
880 | intspec = imap - intsize; | ||
881 | addr = intspec - addrsize; | ||
882 | |||
883 | skiplevel: | ||
884 | /* Iterate again with new parent */ | ||
885 | pr_debug(" -> new parent: %s\n", | ||
886 | newpar ? newpar->full_name : "<>"); | ||
887 | of_node_put(ipar); | ||
888 | ipar = newpar; | ||
889 | newpar = NULL; | ||
890 | } | ||
891 | fail: | ||
892 | of_node_put(ipar); | ||
893 | of_node_put(old); | ||
894 | of_node_put(newpar); | ||
895 | |||
896 | return -EINVAL; | ||
897 | } | ||
898 | EXPORT_SYMBOL_GPL(of_irq_map_raw); | ||
899 | |||
900 | int of_irq_map_one(struct device_node *device, | ||
901 | int index, struct of_irq *out_irq) | ||
902 | { | ||
903 | struct device_node *p; | ||
904 | const u32 *intspec, *tmp, *addr; | ||
905 | u32 intsize, intlen; | ||
906 | int res; | ||
907 | |||
908 | pr_debug("of_irq_map_one: dev=%s, index=%d\n", | ||
909 | device->full_name, index); | ||
910 | |||
911 | /* Get the interrupts property */ | ||
912 | intspec = of_get_property(device, "interrupts", (int *) &intlen); | ||
913 | if (intspec == NULL) | ||
914 | return -EINVAL; | ||
915 | intlen /= sizeof(u32); | ||
916 | |||
917 | pr_debug(" intspec=%d intlen=%d\n", *intspec, intlen); | ||
918 | |||
919 | /* Get the reg property (if any) */ | ||
920 | addr = of_get_property(device, "reg", NULL); | ||
921 | |||
922 | /* Look for the interrupt parent. */ | ||
923 | p = of_irq_find_parent(device); | ||
924 | if (p == NULL) | ||
925 | return -EINVAL; | ||
926 | |||
927 | /* Get size of interrupt specifier */ | ||
928 | tmp = of_get_property(p, "#interrupt-cells", NULL); | ||
929 | if (tmp == NULL) { | ||
930 | of_node_put(p); | ||
931 | return -EINVAL; | ||
932 | } | ||
933 | intsize = *tmp; | ||
934 | |||
935 | pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); | ||
936 | |||
937 | /* Check index */ | ||
938 | if ((index + 1) * intsize > intlen) | ||
939 | return -EINVAL; | ||
940 | |||
941 | /* Get new specifier and map it */ | ||
942 | res = of_irq_map_raw(p, intspec + index * intsize, intsize, | ||
943 | addr, out_irq); | ||
944 | of_node_put(p); | ||
945 | return res; | ||
946 | } | ||
947 | EXPORT_SYMBOL_GPL(of_irq_map_one); | ||
948 | |||
949 | /** | 114 | /** |
950 | * Search the device tree for the best MAC address to use. 'mac-address' is | 115 | * Search the device tree for the best MAC address to use. 'mac-address' is |
951 | * checked first, because that is supposed to contain to "most recent" MAC | 116 | * checked first, because that is supposed to contain to "most recent" MAC |
@@ -983,43 +148,3 @@ const void *of_get_mac_address(struct device_node *np) | |||
983 | return NULL; | 148 | return NULL; |
984 | } | 149 | } |
985 | EXPORT_SYMBOL(of_get_mac_address); | 150 | EXPORT_SYMBOL(of_get_mac_address); |
986 | |||
987 | int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) | ||
988 | { | ||
989 | struct of_irq out_irq; | ||
990 | int irq; | ||
991 | int res; | ||
992 | |||
993 | res = of_irq_map_one(dev, index, &out_irq); | ||
994 | |||
995 | /* Get irq for the device */ | ||
996 | if (res) { | ||
997 | pr_debug("IRQ not found... code = %d", res); | ||
998 | return NO_IRQ; | ||
999 | } | ||
1000 | /* Assuming single interrupt controller... */ | ||
1001 | irq = out_irq.specifier[0]; | ||
1002 | |||
1003 | pr_debug("IRQ found = %d", irq); | ||
1004 | |||
1005 | /* Only dereference the resource if both the | ||
1006 | * resource and the irq are valid. */ | ||
1007 | if (r && irq != NO_IRQ) { | ||
1008 | r->start = r->end = irq; | ||
1009 | r->flags = IORESOURCE_IRQ; | ||
1010 | } | ||
1011 | |||
1012 | return irq; | ||
1013 | } | ||
1014 | EXPORT_SYMBOL_GPL(of_irq_to_resource); | ||
1015 | |||
1016 | void __iomem *of_iomap(struct device_node *np, int index) | ||
1017 | { | ||
1018 | struct resource res; | ||
1019 | |||
1020 | if (of_address_to_resource(np, index, &res)) | ||
1021 | return NULL; | ||
1022 | |||
1023 | return ioremap(res.start, 1 + res.end - res.start); | ||
1024 | } | ||
1025 | EXPORT_SYMBOL(of_iomap); | ||
diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index a4a7770c6140..dc03ffc8174a 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c | |||
@@ -38,6 +38,8 @@ | |||
38 | #include <asm/processor.h> | 38 | #include <asm/processor.h> |
39 | #include <linux/uaccess.h> | 39 | #include <linux/uaccess.h> |
40 | #include <asm/asm-offsets.h> | 40 | #include <asm/asm-offsets.h> |
41 | #include <asm/cacheflush.h> | ||
42 | #include <asm/io.h> | ||
41 | 43 | ||
42 | /* Returns the address where the register at REG_OFFS in P is stashed away. */ | 44 | /* Returns the address where the register at REG_OFFS in P is stashed away. */ |
43 | static microblaze_reg_t *reg_save_addr(unsigned reg_offs, | 45 | static microblaze_reg_t *reg_save_addr(unsigned reg_offs, |
@@ -101,8 +103,21 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
101 | microblaze_reg_t *reg_addr = reg_save_addr(addr, child); | 103 | microblaze_reg_t *reg_addr = reg_save_addr(addr, child); |
102 | if (request == PTRACE_PEEKUSR) | 104 | if (request == PTRACE_PEEKUSR) |
103 | val = *reg_addr; | 105 | val = *reg_addr; |
104 | else | 106 | else { |
107 | #if 1 | ||
105 | *reg_addr = data; | 108 | *reg_addr = data; |
109 | #else | ||
110 | /* MS potential problem on WB system | ||
111 | * Be aware that reg_addr is virtual address | ||
112 | * virt_to_phys conversion is necessary. | ||
113 | * This could be sensible solution. | ||
114 | */ | ||
115 | u32 paddr = virt_to_phys((u32)reg_addr); | ||
116 | invalidate_icache_range(paddr, paddr + 4); | ||
117 | *reg_addr = data; | ||
118 | flush_dcache_range(paddr, paddr + 4); | ||
119 | #endif | ||
120 | } | ||
106 | } else | 121 | } else |
107 | rval = -EIO; | 122 | rval = -EIO; |
108 | 123 | ||
diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c index a1721a33042e..bd8ccab5ceff 100644 --- a/arch/microblaze/kernel/reset.c +++ b/arch/microblaze/kernel/reset.c | |||
@@ -24,8 +24,8 @@ static int of_reset_gpio_handle(void) | |||
24 | int ret; /* variable which stored handle reset gpio pin */ | 24 | int ret; /* variable which stored handle reset gpio pin */ |
25 | struct device_node *root; /* root node */ | 25 | struct device_node *root; /* root node */ |
26 | struct device_node *gpio; /* gpio node */ | 26 | struct device_node *gpio; /* gpio node */ |
27 | struct of_gpio_chip *of_gc = NULL; | 27 | struct gpio_chip *gc; |
28 | enum of_gpio_flags flags ; | 28 | u32 flags; |
29 | const void *gpio_spec; | 29 | const void *gpio_spec; |
30 | 30 | ||
31 | /* find out root node */ | 31 | /* find out root node */ |
@@ -39,19 +39,19 @@ static int of_reset_gpio_handle(void) | |||
39 | goto err0; | 39 | goto err0; |
40 | } | 40 | } |
41 | 41 | ||
42 | of_gc = gpio->data; | 42 | gc = of_node_to_gpiochip(gpio); |
43 | if (!of_gc) { | 43 | if (!gc) { |
44 | pr_debug("%s: gpio controller %s isn't registered\n", | 44 | pr_debug("%s: gpio controller %s isn't registered\n", |
45 | root->full_name, gpio->full_name); | 45 | root->full_name, gpio->full_name); |
46 | ret = -ENODEV; | 46 | ret = -ENODEV; |
47 | goto err1; | 47 | goto err1; |
48 | } | 48 | } |
49 | 49 | ||
50 | ret = of_gc->xlate(of_gc, root, gpio_spec, &flags); | 50 | ret = gc->of_xlate(gc, root, gpio_spec, &flags); |
51 | if (ret < 0) | 51 | if (ret < 0) |
52 | goto err1; | 52 | goto err1; |
53 | 53 | ||
54 | ret += of_gc->gc.base; | 54 | ret += gc->base; |
55 | err1: | 55 | err1: |
56 | of_node_put(gpio); | 56 | of_node_put(gpio); |
57 | err0: | 57 | err0: |
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 17c98dbcec88..f5f768842354 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c | |||
@@ -213,15 +213,9 @@ static struct notifier_block dflt_plat_bus_notifier = { | |||
213 | .priority = INT_MAX, | 213 | .priority = INT_MAX, |
214 | }; | 214 | }; |
215 | 215 | ||
216 | static struct notifier_block dflt_of_bus_notifier = { | ||
217 | .notifier_call = dflt_bus_notify, | ||
218 | .priority = INT_MAX, | ||
219 | }; | ||
220 | |||
221 | static int __init setup_bus_notifier(void) | 216 | static int __init setup_bus_notifier(void) |
222 | { | 217 | { |
223 | bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier); | 218 | bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier); |
224 | bus_register_notifier(&of_platform_bus_type, &dflt_of_bus_notifier); | ||
225 | 219 | ||
226 | return 0; | 220 | return 0; |
227 | } | 221 | } |
diff --git a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c index 123692f22647..84bc6686102c 100644 --- a/arch/microblaze/kernel/stacktrace.c +++ b/arch/microblaze/kernel/stacktrace.c | |||
@@ -14,52 +14,18 @@ | |||
14 | #include <linux/thread_info.h> | 14 | #include <linux/thread_info.h> |
15 | #include <linux/ptrace.h> | 15 | #include <linux/ptrace.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <asm/unwind.h> | ||
17 | 18 | ||
18 | /* FIXME initial support */ | ||
19 | void save_stack_trace(struct stack_trace *trace) | 19 | void save_stack_trace(struct stack_trace *trace) |
20 | { | 20 | { |
21 | unsigned long *sp; | 21 | /* Exclude our helper functions from the trace*/ |
22 | unsigned long addr; | 22 | trace->skip += 2; |
23 | asm("addik %0, r1, 0" : "=r" (sp)); | 23 | microblaze_unwind(NULL, trace); |
24 | |||
25 | while (!kstack_end(sp)) { | ||
26 | addr = *sp++; | ||
27 | if (__kernel_text_address(addr)) { | ||
28 | if (trace->skip > 0) | ||
29 | trace->skip--; | ||
30 | else | ||
31 | trace->entries[trace->nr_entries++] = addr; | ||
32 | |||
33 | if (trace->nr_entries >= trace->max_entries) | ||
34 | break; | ||
35 | } | ||
36 | } | ||
37 | } | 24 | } |
38 | EXPORT_SYMBOL_GPL(save_stack_trace); | 25 | EXPORT_SYMBOL_GPL(save_stack_trace); |
39 | 26 | ||
40 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | 27 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) |
41 | { | 28 | { |
42 | unsigned int *sp; | 29 | microblaze_unwind(tsk, trace); |
43 | unsigned long addr; | ||
44 | |||
45 | struct thread_info *ti = task_thread_info(tsk); | ||
46 | |||
47 | if (tsk == current) | ||
48 | asm("addik %0, r1, 0" : "=r" (sp)); | ||
49 | else | ||
50 | sp = (unsigned int *)ti->cpu_context.r1; | ||
51 | |||
52 | while (!kstack_end(sp)) { | ||
53 | addr = *sp++; | ||
54 | if (__kernel_text_address(addr)) { | ||
55 | if (trace->skip > 0) | ||
56 | trace->skip--; | ||
57 | else | ||
58 | trace->entries[trace->nr_entries++] = addr; | ||
59 | |||
60 | if (trace->nr_entries >= trace->max_entries) | ||
61 | break; | ||
62 | } | ||
63 | } | ||
64 | } | 30 | } |
65 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | 31 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c index ed61b2f17719..b1380ae93ae1 100644 --- a/arch/microblaze/kernel/timer.c +++ b/arch/microblaze/kernel/timer.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <asm/prom.h> | 28 | #include <asm/prom.h> |
29 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
30 | #include <asm/system.h> | 30 | #include <asm/system.h> |
31 | #include <linux/cnt32_to_63.h> | ||
31 | 32 | ||
32 | #ifdef CONFIG_SELFMOD_TIMER | 33 | #ifdef CONFIG_SELFMOD_TIMER |
33 | #include <asm/selfmod.h> | 34 | #include <asm/selfmod.h> |
@@ -135,7 +136,7 @@ static void microblaze_timer_set_mode(enum clock_event_mode mode, | |||
135 | static struct clock_event_device clockevent_microblaze_timer = { | 136 | static struct clock_event_device clockevent_microblaze_timer = { |
136 | .name = "microblaze_clockevent", | 137 | .name = "microblaze_clockevent", |
137 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, | 138 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, |
138 | .shift = 24, | 139 | .shift = 8, |
139 | .rating = 300, | 140 | .rating = 300, |
140 | .set_next_event = microblaze_timer_set_next_event, | 141 | .set_next_event = microblaze_timer_set_next_event, |
141 | .set_mode = microblaze_timer_set_mode, | 142 | .set_mode = microblaze_timer_set_mode, |
@@ -195,7 +196,7 @@ static cycle_t microblaze_cc_read(const struct cyclecounter *cc) | |||
195 | static struct cyclecounter microblaze_cc = { | 196 | static struct cyclecounter microblaze_cc = { |
196 | .read = microblaze_cc_read, | 197 | .read = microblaze_cc_read, |
197 | .mask = CLOCKSOURCE_MASK(32), | 198 | .mask = CLOCKSOURCE_MASK(32), |
198 | .shift = 24, | 199 | .shift = 8, |
199 | }; | 200 | }; |
200 | 201 | ||
201 | int __init init_microblaze_timecounter(void) | 202 | int __init init_microblaze_timecounter(void) |
@@ -213,7 +214,7 @@ static struct clocksource clocksource_microblaze = { | |||
213 | .rating = 300, | 214 | .rating = 300, |
214 | .read = microblaze_read, | 215 | .read = microblaze_read, |
215 | .mask = CLOCKSOURCE_MASK(32), | 216 | .mask = CLOCKSOURCE_MASK(32), |
216 | .shift = 24, /* I can shift it */ | 217 | .shift = 8, /* I can shift it */ |
217 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 218 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
218 | }; | 219 | }; |
219 | 220 | ||
@@ -235,6 +236,12 @@ static int __init microblaze_clocksource_init(void) | |||
235 | return 0; | 236 | return 0; |
236 | } | 237 | } |
237 | 238 | ||
239 | /* | ||
240 | * We have to protect accesses before timer initialization | ||
241 | * and return 0 for sched_clock function below. | ||
242 | */ | ||
243 | static int timer_initialized; | ||
244 | |||
238 | void __init time_init(void) | 245 | void __init time_init(void) |
239 | { | 246 | { |
240 | u32 irq, i = 0; | 247 | u32 irq, i = 0; |
@@ -289,4 +296,15 @@ void __init time_init(void) | |||
289 | #endif | 296 | #endif |
290 | microblaze_clocksource_init(); | 297 | microblaze_clocksource_init(); |
291 | microblaze_clockevent_init(); | 298 | microblaze_clockevent_init(); |
299 | timer_initialized = 1; | ||
300 | } | ||
301 | |||
302 | unsigned long long notrace sched_clock(void) | ||
303 | { | ||
304 | if (timer_initialized) { | ||
305 | struct clocksource *cs = &clocksource_microblaze; | ||
306 | cycle_t cyc = cnt32_to_63(cs->read(NULL)); | ||
307 | return clocksource_cyc2ns(cyc, cs->mult, cs->shift); | ||
308 | } | ||
309 | return 0; | ||
292 | } | 310 | } |
diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index 75e49202a5ed..ba034d421ec2 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c | |||
@@ -16,13 +16,14 @@ | |||
16 | 16 | ||
17 | #include <asm/exceptions.h> | 17 | #include <asm/exceptions.h> |
18 | #include <asm/system.h> | 18 | #include <asm/system.h> |
19 | #include <asm/unwind.h> | ||
19 | 20 | ||
20 | void trap_init(void) | 21 | void trap_init(void) |
21 | { | 22 | { |
22 | __enable_hw_exceptions(); | 23 | __enable_hw_exceptions(); |
23 | } | 24 | } |
24 | 25 | ||
25 | static unsigned long kstack_depth_to_print = 24; | 26 | static unsigned long kstack_depth_to_print; /* 0 == entire stack */ |
26 | 27 | ||
27 | static int __init kstack_setup(char *s) | 28 | static int __init kstack_setup(char *s) |
28 | { | 29 | { |
@@ -30,31 +31,47 @@ static int __init kstack_setup(char *s) | |||
30 | } | 31 | } |
31 | __setup("kstack=", kstack_setup); | 32 | __setup("kstack=", kstack_setup); |
32 | 33 | ||
33 | void show_trace(struct task_struct *task, unsigned long *stack) | 34 | void show_stack(struct task_struct *task, unsigned long *sp) |
34 | { | 35 | { |
35 | unsigned long addr; | 36 | unsigned long words_to_show; |
36 | 37 | u32 fp = (u32) sp; | |
37 | if (!stack) | 38 | |
38 | stack = (unsigned long *)&stack; | 39 | if (fp == 0) { |
40 | if (task) { | ||
41 | fp = ((struct thread_info *) | ||
42 | (task->stack))->cpu_context.r1; | ||
43 | } else { | ||
44 | /* Pick up caller of dump_stack() */ | ||
45 | fp = (u32)&sp - 8; | ||
46 | } | ||
47 | } | ||
39 | 48 | ||
40 | printk(KERN_NOTICE "Call Trace: "); | 49 | words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE - 1))) >> 2; |
41 | #ifdef CONFIG_KALLSYMS | 50 | if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print)) |
42 | printk(KERN_NOTICE "\n"); | 51 | words_to_show = kstack_depth_to_print; |
43 | #endif | 52 | |
44 | while (!kstack_end(stack)) { | 53 | pr_info("Kernel Stack:\n"); |
45 | addr = *stack++; | 54 | |
46 | /* | 55 | /* |
47 | * If the address is either in the text segment of the | 56 | * Make the first line an 'odd' size if necessary to get |
48 | * kernel, or in the region which contains vmalloc'ed | 57 | * remaining lines to start at an address multiple of 0x10 |
49 | * memory, it *may* be the address of a calling | 58 | */ |
50 | * routine; if so, print it so that someone tracing | 59 | if (fp & 0xF) { |
51 | * down the cause of the crash will be able to figure | 60 | unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2; |
52 | * out the call path that was taken. | 61 | if (line1_words < words_to_show) { |
53 | */ | 62 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, |
54 | if (kernel_text_address(addr)) | 63 | 4, (void *)fp, line1_words << 2, 0); |
55 | print_ip_sym(addr); | 64 | fp += line1_words << 2; |
65 | words_to_show -= line1_words; | ||
66 | } | ||
56 | } | 67 | } |
57 | printk(KERN_NOTICE "\n"); | 68 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp, |
69 | words_to_show << 2, 0); | ||
70 | printk(KERN_INFO "\n\n"); | ||
71 | |||
72 | pr_info("Call Trace:\n"); | ||
73 | microblaze_unwind(task, NULL); | ||
74 | pr_info("\n"); | ||
58 | 75 | ||
59 | if (!task) | 76 | if (!task) |
60 | task = current; | 77 | task = current; |
@@ -62,34 +79,6 @@ void show_trace(struct task_struct *task, unsigned long *stack) | |||
62 | debug_show_held_locks(task); | 79 | debug_show_held_locks(task); |
63 | } | 80 | } |
64 | 81 | ||
65 | void show_stack(struct task_struct *task, unsigned long *sp) | ||
66 | { | ||
67 | unsigned long *stack; | ||
68 | int i; | ||
69 | |||
70 | if (sp == NULL) { | ||
71 | if (task) | ||
72 | sp = (unsigned long *) ((struct thread_info *) | ||
73 | (task->stack))->cpu_context.r1; | ||
74 | else | ||
75 | sp = (unsigned long *)&sp; | ||
76 | } | ||
77 | |||
78 | stack = sp; | ||
79 | |||
80 | printk(KERN_INFO "\nStack:\n "); | ||
81 | |||
82 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
83 | if (kstack_end(sp)) | ||
84 | break; | ||
85 | if (i && ((i % 8) == 0)) | ||
86 | printk("\n "); | ||
87 | printk("%08lx ", *sp++); | ||
88 | } | ||
89 | printk("\n"); | ||
90 | show_trace(task, stack); | ||
91 | } | ||
92 | |||
93 | void dump_stack(void) | 82 | void dump_stack(void) |
94 | { | 83 | { |
95 | show_stack(NULL, NULL); | 84 | show_stack(NULL, NULL); |
diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c new file mode 100644 index 000000000000..fefac5c33586 --- /dev/null +++ b/arch/microblaze/kernel/unwind.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * Backtrace support for Microblaze | ||
3 | * | ||
4 | * Copyright (C) 2010 Digital Design Corporation | ||
5 | * | ||
6 | * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is: | ||
7 | * Copyright (C) 2004 Paul Mundt | ||
8 | * Copyright (C) 2004 Richard Curnow | ||
9 | * | ||
10 | * This file is subject to the terms and conditions of the GNU General Public | ||
11 | * License. See the file "COPYING" in the main directory of this archive | ||
12 | * for more details. | ||
13 | */ | ||
14 | |||
15 | /* #define DEBUG 1 */ | ||
16 | #include <linux/kallsyms.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/stacktrace.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <asm/sections.h> | ||
25 | #include <asm/exceptions.h> | ||
26 | #include <asm/unwind.h> | ||
27 | |||
28 | struct stack_trace; | ||
29 | |||
30 | /* | ||
31 | * On Microblaze, finding the previous stack frame is a little tricky. | ||
32 | * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS, | ||
33 | * and even if it did, gcc (4.1.2) does not store the frame pointer at | ||
34 | * a consistent offset within each frame. To determine frame size, it is | ||
35 | * necessary to search for the assembly instruction that creates or reclaims | ||
36 | * the frame and extract the size from it. | ||
37 | * | ||
38 | * Microblaze stores the stack pointer in r1, and creates a frame via | ||
39 | * | ||
40 | * addik r1, r1, -FRAME_SIZE | ||
41 | * | ||
42 | * The frame is reclaimed via | ||
43 | * | ||
44 | * addik r1, r1, FRAME_SIZE | ||
45 | * | ||
46 | * Frame creation occurs at or near the top of a function. | ||
47 | * Depending on the compiler, reclaim may occur at the end, or before | ||
48 | * a mid-function return. | ||
49 | * | ||
50 | * A stack frame is usually not created in a leaf function. | ||
51 | * | ||
52 | */ | ||
53 | |||
54 | /** | ||
55 | * get_frame_size - Extract the stack adjustment from an | ||
56 | * "addik r1, r1, adjust" instruction | ||
57 | * @instr : Microblaze instruction | ||
58 | * | ||
59 | * Return - Number of stack bytes the instruction reserves or reclaims | ||
60 | */ | ||
61 | inline long get_frame_size(unsigned long instr) | ||
62 | { | ||
63 | return abs((s16)(instr & 0xFFFF)); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * find_frame_creation - Search backward to find the instruction that creates | ||
68 | * the stack frame (hopefully, for the same function the | ||
69 | * initial PC is in). | ||
70 | * @pc : Program counter at which to begin the search | ||
71 | * | ||
72 | * Return - PC at which stack frame creation occurs | ||
73 | * NULL if this cannot be found, i.e. a leaf function | ||
74 | */ | ||
75 | static unsigned long *find_frame_creation(unsigned long *pc) | ||
76 | { | ||
77 | int i; | ||
78 | |||
79 | /* NOTE: Distance to search is arbitrary | ||
80 | * 250 works well for most things, | ||
81 | * 750 picks up things like tcp_recvmsg(), | ||
82 | * 1000 needed for fat_fill_super() | ||
83 | */ | ||
84 | for (i = 0; i < 1000; i++, pc--) { | ||
85 | unsigned long instr; | ||
86 | s16 frame_size; | ||
87 | |||
88 | if (!kernel_text_address((unsigned long) pc)) | ||
89 | return NULL; | ||
90 | |||
91 | instr = *pc; | ||
92 | |||
93 | /* addik r1, r1, foo ? */ | ||
94 | if ((instr & 0xFFFF0000) != 0x30210000) | ||
95 | continue; /* No */ | ||
96 | |||
97 | frame_size = get_frame_size(instr); | ||
98 | if ((frame_size < 8) || (frame_size & 3)) { | ||
99 | pr_debug(" Invalid frame size %d at 0x%p\n", | ||
100 | frame_size, pc); | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | pr_debug(" Found frame creation at 0x%p, size %d\n", pc, | ||
105 | frame_size); | ||
106 | return pc; | ||
107 | } | ||
108 | |||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * lookup_prev_stack_frame - Find the stack frame of the previous function. | ||
114 | * @fp : Frame (stack) pointer for current function | ||
115 | * @pc : Program counter within current function | ||
116 | * @leaf_return : r15 value within current function. If the current function | ||
117 | * is a leaf, this is the caller's return address. | ||
118 | * @pprev_fp : On exit, set to frame (stack) pointer for previous function | ||
119 | * @pprev_pc : On exit, set to current function caller's return address | ||
120 | * | ||
121 | * Return - 0 on success, -EINVAL if the previous frame cannot be found | ||
122 | */ | ||
123 | static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, | ||
124 | unsigned long leaf_return, | ||
125 | unsigned long *pprev_fp, | ||
126 | unsigned long *pprev_pc) | ||
127 | { | ||
128 | unsigned long *prologue = NULL; | ||
129 | |||
130 | /* _switch_to is a special leaf function */ | ||
131 | if (pc != (unsigned long) &_switch_to) | ||
132 | prologue = find_frame_creation((unsigned long *)pc); | ||
133 | |||
134 | if (prologue) { | ||
135 | long frame_size = get_frame_size(*prologue); | ||
136 | |||
137 | *pprev_fp = fp + frame_size; | ||
138 | *pprev_pc = *(unsigned long *)fp; | ||
139 | } else { | ||
140 | if (!leaf_return) | ||
141 | return -EINVAL; | ||
142 | *pprev_pc = leaf_return; | ||
143 | *pprev_fp = fp; | ||
144 | } | ||
145 | |||
146 | /* NOTE: don't check kernel_text_address here, to allow display | ||
147 | * of userland return address | ||
148 | */ | ||
149 | return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0; | ||
150 | } | ||
151 | |||
152 | static void microblaze_unwind_inner(struct task_struct *task, | ||
153 | unsigned long pc, unsigned long fp, | ||
154 | unsigned long leaf_return, | ||
155 | struct stack_trace *trace); | ||
156 | |||
157 | /** | ||
158 | * unwind_trap - Unwind through a system trap, that stored previous state | ||
159 | * on the stack. | ||
160 | */ | ||
161 | #ifdef CONFIG_MMU | ||
162 | static inline void unwind_trap(struct task_struct *task, unsigned long pc, | ||
163 | unsigned long fp, struct stack_trace *trace) | ||
164 | { | ||
165 | /* To be implemented */ | ||
166 | } | ||
167 | #else | ||
168 | static inline void unwind_trap(struct task_struct *task, unsigned long pc, | ||
169 | unsigned long fp, struct stack_trace *trace) | ||
170 | { | ||
171 | const struct pt_regs *regs = (const struct pt_regs *) fp; | ||
172 | microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace); | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | /** | ||
177 | * microblaze_unwind_inner - Unwind the stack from the specified point | ||
178 | * @task : Task whose stack we are to unwind (may be NULL) | ||
179 | * @pc : Program counter from which we start unwinding | ||
180 | * @fp : Frame (stack) pointer from which we start unwinding | ||
181 | * @leaf_return : Value of r15 at pc. If the function is a leaf, this is | ||
182 | * the caller's return address. | ||
183 | * @trace : Where to store stack backtrace (PC values). | ||
184 | * NULL == print backtrace to kernel log | ||
185 | */ | ||
186 | void microblaze_unwind_inner(struct task_struct *task, | ||
187 | unsigned long pc, unsigned long fp, | ||
188 | unsigned long leaf_return, | ||
189 | struct stack_trace *trace) | ||
190 | { | ||
191 | int ofs = 0; | ||
192 | |||
193 | pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp); | ||
194 | if (!pc || !fp || (pc & 3) || (fp & 3)) { | ||
195 | pr_debug(" Invalid state for unwind, aborting\n"); | ||
196 | return; | ||
197 | } | ||
198 | for (; pc != 0;) { | ||
199 | unsigned long next_fp, next_pc = 0; | ||
200 | unsigned long return_to = pc + 2 * sizeof(unsigned long); | ||
201 | const struct trap_handler_info *handler = | ||
202 | µblaze_trap_handlers; | ||
203 | |||
204 | /* Is previous function the HW exception handler? */ | ||
205 | if ((return_to >= (unsigned long)&_hw_exception_handler) | ||
206 | &&(return_to < (unsigned long)&ex_handler_unhandled)) { | ||
207 | /* | ||
208 | * HW exception handler doesn't save all registers, | ||
209 | * so we open-code a special case of unwind_trap() | ||
210 | */ | ||
211 | #ifndef CONFIG_MMU | ||
212 | const struct pt_regs *regs = | ||
213 | (const struct pt_regs *) fp; | ||
214 | #endif | ||
215 | pr_info("HW EXCEPTION\n"); | ||
216 | #ifndef CONFIG_MMU | ||
217 | microblaze_unwind_inner(task, regs->r17 - 4, | ||
218 | fp + EX_HANDLER_STACK_SIZ, | ||
219 | regs->r15, trace); | ||
220 | #endif | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | /* Is previous function a trap handler? */ | ||
225 | for (; handler->start_addr; ++handler) { | ||
226 | if ((return_to >= handler->start_addr) | ||
227 | && (return_to <= handler->end_addr)) { | ||
228 | if (!trace) | ||
229 | pr_info("%s\n", handler->trap_name); | ||
230 | unwind_trap(task, pc, fp, trace); | ||
231 | return; | ||
232 | } | ||
233 | } | ||
234 | pc -= ofs; | ||
235 | |||
236 | if (trace) { | ||
237 | #ifdef CONFIG_STACKTRACE | ||
238 | if (trace->skip > 0) | ||
239 | trace->skip--; | ||
240 | else | ||
241 | trace->entries[trace->nr_entries++] = pc; | ||
242 | |||
243 | if (trace->nr_entries >= trace->max_entries) | ||
244 | break; | ||
245 | #endif | ||
246 | } else { | ||
247 | /* Have we reached userland? */ | ||
248 | if (unlikely(pc == task_pt_regs(task)->pc)) { | ||
249 | pr_info("[<%p>] PID %lu [%s]\n", | ||
250 | (void *) pc, | ||
251 | (unsigned long) task->pid, | ||
252 | task->comm); | ||
253 | break; | ||
254 | } else | ||
255 | print_ip_sym(pc); | ||
256 | } | ||
257 | |||
258 | /* Stop when we reach anything not part of the kernel */ | ||
259 | if (!kernel_text_address(pc)) | ||
260 | break; | ||
261 | |||
262 | if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp, | ||
263 | &next_pc) == 0) { | ||
264 | ofs = sizeof(unsigned long); | ||
265 | pc = next_pc & ~3; | ||
266 | fp = next_fp; | ||
267 | leaf_return = 0; | ||
268 | } else { | ||
269 | pr_debug(" Failed to find previous stack frame\n"); | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | pr_debug(" Next PC=%p, next FP=%p\n", | ||
274 | (void *)next_pc, (void *)next_fp); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * microblaze_unwind - Stack unwinder for Microblaze (external entry point) | ||
280 | * @task : Task whose stack we are to unwind (NULL == current) | ||
281 | * @trace : Where to store stack backtrace (PC values). | ||
282 | * NULL == print backtrace to kernel log | ||
283 | */ | ||
284 | void microblaze_unwind(struct task_struct *task, struct stack_trace *trace) | ||
285 | { | ||
286 | if (task) { | ||
287 | if (task == current) { | ||
288 | const struct pt_regs *regs = task_pt_regs(task); | ||
289 | microblaze_unwind_inner(task, regs->pc, regs->r1, | ||
290 | regs->r15, trace); | ||
291 | } else { | ||
292 | struct thread_info *thread_info = | ||
293 | (struct thread_info *)(task->stack); | ||
294 | const struct cpu_context *cpu_context = | ||
295 | &thread_info->cpu_context; | ||
296 | |||
297 | microblaze_unwind_inner(task, | ||
298 | (unsigned long) &_switch_to, | ||
299 | cpu_context->r1, | ||
300 | cpu_context->r15, trace); | ||
301 | } | ||
302 | } else { | ||
303 | unsigned long pc, fp; | ||
304 | |||
305 | __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp)); | ||
306 | |||
307 | __asm__ __volatile__ ( | ||
308 | "brlid %0, 0f;" | ||
309 | "nop;" | ||
310 | "0:" | ||
311 | : "=r" (pc) | ||
312 | ); | ||
313 | |||
314 | /* Since we are not a leaf function, use leaf_return = 0 */ | ||
315 | microblaze_unwind_inner(current, pc, fp, 0, trace); | ||
316 | } | ||
317 | } | ||
318 | |||
diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S index db72d7124602..a09f2962fbec 100644 --- a/arch/microblaze/kernel/vmlinux.lds.S +++ b/arch/microblaze/kernel/vmlinux.lds.S | |||
@@ -10,7 +10,7 @@ | |||
10 | 10 | ||
11 | OUTPUT_FORMAT("elf32-microblaze", "elf32-microblaze", "elf32-microblaze") | 11 | OUTPUT_FORMAT("elf32-microblaze", "elf32-microblaze", "elf32-microblaze") |
12 | OUTPUT_ARCH(microblaze) | 12 | OUTPUT_ARCH(microblaze) |
13 | ENTRY(_start) | 13 | ENTRY(microblaze_start) |
14 | 14 | ||
15 | #include <asm/page.h> | 15 | #include <asm/page.h> |
16 | #include <asm-generic/vmlinux.lds.h> | 16 | #include <asm-generic/vmlinux.lds.h> |
@@ -20,7 +20,7 @@ jiffies = jiffies_64 + 4; | |||
20 | 20 | ||
21 | SECTIONS { | 21 | SECTIONS { |
22 | . = CONFIG_KERNEL_START; | 22 | . = CONFIG_KERNEL_START; |
23 | _start = CONFIG_KERNEL_BASE_ADDR; | 23 | microblaze_start = CONFIG_KERNEL_BASE_ADDR; |
24 | .text : AT(ADDR(.text) - LOAD_OFFSET) { | 24 | .text : AT(ADDR(.text) - LOAD_OFFSET) { |
25 | _text = . ; | 25 | _text = . ; |
26 | _stext = . ; | 26 | _stext = . ; |
@@ -55,7 +55,7 @@ SECTIONS { | |||
55 | */ | 55 | */ |
56 | .sdata2 : AT(ADDR(.sdata2) - LOAD_OFFSET) { | 56 | .sdata2 : AT(ADDR(.sdata2) - LOAD_OFFSET) { |
57 | _ssrw = .; | 57 | _ssrw = .; |
58 | . = ALIGN(4096); /* page aligned when MMU used - origin 0x8 */ | 58 | . = ALIGN(PAGE_SIZE); /* page aligned when MMU used */ |
59 | *(.sdata2) | 59 | *(.sdata2) |
60 | . = ALIGN(8); | 60 | . = ALIGN(8); |
61 | _essrw = .; | 61 | _essrw = .; |
@@ -70,7 +70,7 @@ SECTIONS { | |||
70 | /* Reserve some low RAM for r0 based memory references */ | 70 | /* Reserve some low RAM for r0 based memory references */ |
71 | . = ALIGN(0x4) ; | 71 | . = ALIGN(0x4) ; |
72 | r0_ram = . ; | 72 | r0_ram = . ; |
73 | . = . + 4096; /* a page should be enough */ | 73 | . = . + PAGE_SIZE; /* a page should be enough */ |
74 | 74 | ||
75 | /* Under the microblaze ABI, .sdata and .sbss must be contiguous */ | 75 | /* Under the microblaze ABI, .sdata and .sbss must be contiguous */ |
76 | . = ALIGN(8); | 76 | . = ALIGN(8); |
@@ -120,7 +120,7 @@ SECTIONS { | |||
120 | 120 | ||
121 | __init_end_before_initramfs = .; | 121 | __init_end_before_initramfs = .; |
122 | 122 | ||
123 | .init.ramfs ALIGN(4096) : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { | 123 | .init.ramfs ALIGN(PAGE_SIZE) : AT(ADDR(.init.ramfs) - LOAD_OFFSET) { |
124 | __initramfs_start = .; | 124 | __initramfs_start = .; |
125 | *(.init.ramfs) | 125 | *(.init.ramfs) |
126 | __initramfs_end = .; | 126 | __initramfs_end = .; |
@@ -132,11 +132,11 @@ SECTIONS { | |||
132 | * so that __init_end == __bss_start. This will make image.elf | 132 | * so that __init_end == __bss_start. This will make image.elf |
133 | * consistent with the image.bin | 133 | * consistent with the image.bin |
134 | */ | 134 | */ |
135 | /* . = ALIGN(4096); */ | 135 | /* . = ALIGN(PAGE_SIZE); */ |
136 | } | 136 | } |
137 | __init_end = .; | 137 | __init_end = .; |
138 | 138 | ||
139 | .bss ALIGN (4096) : AT(ADDR(.bss) - LOAD_OFFSET) { | 139 | .bss ALIGN (PAGE_SIZE) : AT(ADDR(.bss) - LOAD_OFFSET) { |
140 | /* page aligned when MMU used */ | 140 | /* page aligned when MMU used */ |
141 | __bss_start = . ; | 141 | __bss_start = . ; |
142 | *(.bss*) | 142 | *(.bss*) |
@@ -145,7 +145,7 @@ SECTIONS { | |||
145 | __bss_stop = . ; | 145 | __bss_stop = . ; |
146 | _ebss = . ; | 146 | _ebss = . ; |
147 | } | 147 | } |
148 | . = ALIGN(4096); | 148 | . = ALIGN(PAGE_SIZE); |
149 | _end = .; | 149 | _end = .; |
150 | 150 | ||
151 | DISCARDS | 151 | DISCARDS |