diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/kernel/module.c | 6 | ||||
-rw-r--r-- | arch/powerpc/kernel/process.c | 83 | ||||
-rw-r--r-- | arch/powerpc/kernel/setup_32.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/setup_64.c | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal.h | 10 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 109 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 43 | ||||
-rw-r--r-- | arch/powerpc/kernel/vdso.c | 10 | ||||
-rw-r--r-- | arch/powerpc/kernel/vdso32/vdso32.lds.S | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/vdso64/vdso64.lds.S | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/vmlinux.lds.S | 6 | ||||
-rw-r--r-- | arch/powerpc/lib/feature-fixups-test.S | 21 | ||||
-rw-r--r-- | arch/powerpc/lib/feature-fixups.c | 36 | ||||
-rw-r--r-- | arch/powerpc/mm/mem.c | 1 | ||||
-rw-r--r-- | arch/powerpc/mm/numa.c | 310 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 117 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 38 |
17 files changed, 549 insertions, 253 deletions
diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c index 40dd52d81c18..af07003573c4 100644 --- a/arch/powerpc/kernel/module.c +++ b/arch/powerpc/kernel/module.c | |||
@@ -86,6 +86,12 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
86 | (void *)sect->sh_addr + sect->sh_size); | 86 | (void *)sect->sh_addr + sect->sh_size); |
87 | #endif | 87 | #endif |
88 | 88 | ||
89 | sect = find_section(hdr, sechdrs, "__lwsync_fixup"); | ||
90 | if (sect != NULL) | ||
91 | do_lwsync_fixups(cur_cpu_spec->cpu_features, | ||
92 | (void *)sect->sh_addr, | ||
93 | (void *)sect->sh_addr + sect->sh_size); | ||
94 | |||
89 | return 0; | 95 | return 0; |
90 | } | 96 | } |
91 | 97 | ||
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 1924b57bd241..85e557300d86 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -105,29 +105,6 @@ void enable_kernel_fp(void) | |||
105 | } | 105 | } |
106 | EXPORT_SYMBOL(enable_kernel_fp); | 106 | EXPORT_SYMBOL(enable_kernel_fp); |
107 | 107 | ||
108 | int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) | ||
109 | { | ||
110 | #ifdef CONFIG_VSX | ||
111 | int i; | ||
112 | elf_fpreg_t *reg; | ||
113 | #endif | ||
114 | |||
115 | if (!tsk->thread.regs) | ||
116 | return 0; | ||
117 | flush_fp_to_thread(current); | ||
118 | |||
119 | #ifdef CONFIG_VSX | ||
120 | reg = (elf_fpreg_t *)fpregs; | ||
121 | for (i = 0; i < ELF_NFPREG - 1; i++, reg++) | ||
122 | *reg = tsk->thread.TS_FPR(i); | ||
123 | memcpy(reg, &tsk->thread.fpscr, sizeof(elf_fpreg_t)); | ||
124 | #else | ||
125 | memcpy(fpregs, &tsk->thread.TS_FPR(0), sizeof(*fpregs)); | ||
126 | #endif | ||
127 | |||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | #ifdef CONFIG_ALTIVEC | 108 | #ifdef CONFIG_ALTIVEC |
132 | void enable_kernel_altivec(void) | 109 | void enable_kernel_altivec(void) |
133 | { | 110 | { |
@@ -161,35 +138,6 @@ void flush_altivec_to_thread(struct task_struct *tsk) | |||
161 | preempt_enable(); | 138 | preempt_enable(); |
162 | } | 139 | } |
163 | } | 140 | } |
164 | |||
165 | int dump_task_altivec(struct task_struct *tsk, elf_vrregset_t *vrregs) | ||
166 | { | ||
167 | /* ELF_NVRREG includes the VSCR and VRSAVE which we need to save | ||
168 | * separately, see below */ | ||
169 | const int nregs = ELF_NVRREG - 2; | ||
170 | elf_vrreg_t *reg; | ||
171 | u32 *dest; | ||
172 | |||
173 | if (tsk == current) | ||
174 | flush_altivec_to_thread(tsk); | ||
175 | |||
176 | reg = (elf_vrreg_t *)vrregs; | ||
177 | |||
178 | /* copy the 32 vr registers */ | ||
179 | memcpy(reg, &tsk->thread.vr[0], nregs * sizeof(*reg)); | ||
180 | reg += nregs; | ||
181 | |||
182 | /* copy the vscr */ | ||
183 | memcpy(reg, &tsk->thread.vscr, sizeof(*reg)); | ||
184 | reg++; | ||
185 | |||
186 | /* vrsave is stored in the high 32bit slot of the final 128bits */ | ||
187 | memset(reg, 0, sizeof(*reg)); | ||
188 | dest = (u32 *)reg; | ||
189 | *dest = tsk->thread.vrsave; | ||
190 | |||
191 | return 1; | ||
192 | } | ||
193 | #endif /* CONFIG_ALTIVEC */ | 141 | #endif /* CONFIG_ALTIVEC */ |
194 | 142 | ||
195 | #ifdef CONFIG_VSX | 143 | #ifdef CONFIG_VSX |
@@ -224,29 +172,6 @@ void flush_vsx_to_thread(struct task_struct *tsk) | |||
224 | preempt_enable(); | 172 | preempt_enable(); |
225 | } | 173 | } |
226 | } | 174 | } |
227 | |||
228 | /* | ||
229 | * This dumps the lower half 64bits of the first 32 VSX registers. | ||
230 | * This needs to be called with dump_task_fp and dump_task_altivec to | ||
231 | * get all the VSX state. | ||
232 | */ | ||
233 | int dump_task_vsx(struct task_struct *tsk, elf_vrreg_t *vrregs) | ||
234 | { | ||
235 | elf_vrreg_t *reg; | ||
236 | double buf[32]; | ||
237 | int i; | ||
238 | |||
239 | if (tsk == current) | ||
240 | flush_vsx_to_thread(tsk); | ||
241 | |||
242 | reg = (elf_vrreg_t *)vrregs; | ||
243 | |||
244 | for (i = 0; i < 32 ; i++) | ||
245 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
246 | memcpy(reg, buf, sizeof(buf)); | ||
247 | |||
248 | return 1; | ||
249 | } | ||
250 | #endif /* CONFIG_VSX */ | 175 | #endif /* CONFIG_VSX */ |
251 | 176 | ||
252 | #ifdef CONFIG_SPE | 177 | #ifdef CONFIG_SPE |
@@ -279,14 +204,6 @@ void flush_spe_to_thread(struct task_struct *tsk) | |||
279 | preempt_enable(); | 204 | preempt_enable(); |
280 | } | 205 | } |
281 | } | 206 | } |
282 | |||
283 | int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs) | ||
284 | { | ||
285 | flush_spe_to_thread(current); | ||
286 | /* We copy u32 evr[32] + u64 acc + u32 spefscr -> 35 */ | ||
287 | memcpy(evrregs, ¤t->thread.evr[0], sizeof(u32) * 35); | ||
288 | return 1; | ||
289 | } | ||
290 | #endif /* CONFIG_SPE */ | 207 | #endif /* CONFIG_SPE */ |
291 | 208 | ||
292 | #ifndef CONFIG_SMP | 209 | #ifndef CONFIG_SMP |
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 9e83add54290..0109e7f0ccf9 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c | |||
@@ -101,6 +101,10 @@ unsigned long __init early_init(unsigned long dt_ptr) | |||
101 | PTRRELOC(&__start___ftr_fixup), | 101 | PTRRELOC(&__start___ftr_fixup), |
102 | PTRRELOC(&__stop___ftr_fixup)); | 102 | PTRRELOC(&__stop___ftr_fixup)); |
103 | 103 | ||
104 | do_lwsync_fixups(spec->cpu_features, | ||
105 | PTRRELOC(&__start___lwsync_fixup), | ||
106 | PTRRELOC(&__stop___lwsync_fixup)); | ||
107 | |||
104 | return KERNELBASE + offset; | 108 | return KERNELBASE + offset; |
105 | } | 109 | } |
106 | 110 | ||
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 098fd96a394a..04d8de9f0fc6 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c | |||
@@ -363,6 +363,8 @@ void __init setup_system(void) | |||
363 | &__start___ftr_fixup, &__stop___ftr_fixup); | 363 | &__start___ftr_fixup, &__stop___ftr_fixup); |
364 | do_feature_fixups(powerpc_firmware_features, | 364 | do_feature_fixups(powerpc_firmware_features, |
365 | &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); | 365 | &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); |
366 | do_lwsync_fixups(cur_cpu_spec->cpu_features, | ||
367 | &__start___lwsync_fixup, &__stop___lwsync_fixup); | ||
366 | 368 | ||
367 | /* | 369 | /* |
368 | * Unflatten the device-tree passed by prom_init or kexec | 370 | * Unflatten the device-tree passed by prom_init or kexec |
diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index 77efb3d5465a..28f4b9f5fe5e 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h | |||
@@ -24,6 +24,16 @@ extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, | |||
24 | siginfo_t *info, sigset_t *oldset, | 24 | siginfo_t *info, sigset_t *oldset, |
25 | struct pt_regs *regs); | 25 | struct pt_regs *regs); |
26 | 26 | ||
27 | extern unsigned long copy_fpr_to_user(void __user *to, | ||
28 | struct task_struct *task); | ||
29 | extern unsigned long copy_fpr_from_user(struct task_struct *task, | ||
30 | void __user *from); | ||
31 | #ifdef CONFIG_VSX | ||
32 | extern unsigned long copy_vsx_to_user(void __user *to, | ||
33 | struct task_struct *task); | ||
34 | extern unsigned long copy_vsx_from_user(struct task_struct *task, | ||
35 | void __user *from); | ||
36 | #endif | ||
27 | 37 | ||
28 | #ifdef CONFIG_PPC64 | 38 | #ifdef CONFIG_PPC64 |
29 | 39 | ||
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 349d3487d920..9991e2a58bf4 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -328,6 +328,75 @@ struct rt_sigframe { | |||
328 | int abigap[56]; | 328 | int abigap[56]; |
329 | }; | 329 | }; |
330 | 330 | ||
331 | #ifdef CONFIG_VSX | ||
332 | unsigned long copy_fpr_to_user(void __user *to, | ||
333 | struct task_struct *task) | ||
334 | { | ||
335 | double buf[ELF_NFPREG]; | ||
336 | int i; | ||
337 | |||
338 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
339 | for (i = 0; i < (ELF_NFPREG - 1) ; i++) | ||
340 | buf[i] = task->thread.TS_FPR(i); | ||
341 | memcpy(&buf[i], &task->thread.fpscr, sizeof(double)); | ||
342 | return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); | ||
343 | } | ||
344 | |||
345 | unsigned long copy_fpr_from_user(struct task_struct *task, | ||
346 | void __user *from) | ||
347 | { | ||
348 | double buf[ELF_NFPREG]; | ||
349 | int i; | ||
350 | |||
351 | if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) | ||
352 | return 1; | ||
353 | for (i = 0; i < (ELF_NFPREG - 1) ; i++) | ||
354 | task->thread.TS_FPR(i) = buf[i]; | ||
355 | memcpy(&task->thread.fpscr, &buf[i], sizeof(double)); | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | unsigned long copy_vsx_to_user(void __user *to, | ||
361 | struct task_struct *task) | ||
362 | { | ||
363 | double buf[ELF_NVSRHALFREG]; | ||
364 | int i; | ||
365 | |||
366 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
367 | for (i = 0; i < ELF_NVSRHALFREG; i++) | ||
368 | buf[i] = task->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
369 | return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); | ||
370 | } | ||
371 | |||
372 | unsigned long copy_vsx_from_user(struct task_struct *task, | ||
373 | void __user *from) | ||
374 | { | ||
375 | double buf[ELF_NVSRHALFREG]; | ||
376 | int i; | ||
377 | |||
378 | if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) | ||
379 | return 1; | ||
380 | for (i = 0; i < ELF_NVSRHALFREG ; i++) | ||
381 | task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
382 | return 0; | ||
383 | } | ||
384 | #else | ||
385 | inline unsigned long copy_fpr_to_user(void __user *to, | ||
386 | struct task_struct *task) | ||
387 | { | ||
388 | return __copy_to_user(to, task->thread.fpr, | ||
389 | ELF_NFPREG * sizeof(double)); | ||
390 | } | ||
391 | |||
392 | inline unsigned long copy_fpr_from_user(struct task_struct *task, | ||
393 | void __user *from) | ||
394 | { | ||
395 | return __copy_from_user(task->thread.fpr, from, | ||
396 | ELF_NFPREG * sizeof(double)); | ||
397 | } | ||
398 | #endif | ||
399 | |||
331 | /* | 400 | /* |
332 | * Save the current user registers on the user stack. | 401 | * Save the current user registers on the user stack. |
333 | * We only save the altivec/spe registers if the process has used | 402 | * We only save the altivec/spe registers if the process has used |
@@ -337,10 +406,6 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
337 | int sigret) | 406 | int sigret) |
338 | { | 407 | { |
339 | unsigned long msr = regs->msr; | 408 | unsigned long msr = regs->msr; |
340 | #ifdef CONFIG_VSX | ||
341 | double buf[32]; | ||
342 | int i; | ||
343 | #endif | ||
344 | 409 | ||
345 | /* Make sure floating point registers are stored in regs */ | 410 | /* Make sure floating point registers are stored in regs */ |
346 | flush_fp_to_thread(current); | 411 | flush_fp_to_thread(current); |
@@ -370,14 +435,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
370 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) | 435 | if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) |
371 | return 1; | 436 | return 1; |
372 | #endif /* CONFIG_ALTIVEC */ | 437 | #endif /* CONFIG_ALTIVEC */ |
373 | #ifdef CONFIG_VSX | 438 | if (copy_fpr_to_user(&frame->mc_fregs, current)) |
374 | /* save FPR copy to local buffer then write to the thread_struct */ | ||
375 | flush_fp_to_thread(current); | ||
376 | for (i = 0; i < 32 ; i++) | ||
377 | buf[i] = current->thread.TS_FPR(i); | ||
378 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | ||
379 | if (__copy_to_user(&frame->mc_fregs, buf, ELF_NFPREG * sizeof(double))) | ||
380 | return 1; | 439 | return 1; |
440 | #ifdef CONFIG_VSX | ||
381 | /* | 441 | /* |
382 | * Copy VSR 0-31 upper half from thread_struct to local | 442 | * Copy VSR 0-31 upper half from thread_struct to local |
383 | * buffer, then write that to userspace. Also set MSR_VSX in | 443 | * buffer, then write that to userspace. Also set MSR_VSX in |
@@ -386,18 +446,10 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
386 | */ | 446 | */ |
387 | if (current->thread.used_vsr) { | 447 | if (current->thread.used_vsr) { |
388 | flush_vsx_to_thread(current); | 448 | flush_vsx_to_thread(current); |
389 | for (i = 0; i < 32 ; i++) | 449 | if (copy_vsx_to_user(&frame->mc_vsregs, current)) |
390 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
391 | if (__copy_to_user(&frame->mc_vsregs, buf, | ||
392 | ELF_NVSRHALFREG * sizeof(double))) | ||
393 | return 1; | 450 | return 1; |
394 | msr |= MSR_VSX; | 451 | msr |= MSR_VSX; |
395 | } | 452 | } |
396 | #else | ||
397 | /* save floating-point registers */ | ||
398 | if (__copy_to_user(&frame->mc_fregs, current->thread.fpr, | ||
399 | ELF_NFPREG * sizeof(double))) | ||
400 | return 1; | ||
401 | #endif /* CONFIG_VSX */ | 453 | #endif /* CONFIG_VSX */ |
402 | #ifdef CONFIG_SPE | 454 | #ifdef CONFIG_SPE |
403 | /* save spe registers */ | 455 | /* save spe registers */ |
@@ -442,7 +494,6 @@ static long restore_user_regs(struct pt_regs *regs, | |||
442 | unsigned int save_r2 = 0; | 494 | unsigned int save_r2 = 0; |
443 | unsigned long msr; | 495 | unsigned long msr; |
444 | #ifdef CONFIG_VSX | 496 | #ifdef CONFIG_VSX |
445 | double buf[32]; | ||
446 | int i; | 497 | int i; |
447 | #endif | 498 | #endif |
448 | 499 | ||
@@ -490,13 +541,10 @@ static long restore_user_regs(struct pt_regs *regs, | |||
490 | if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) | 541 | if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) |
491 | return 1; | 542 | return 1; |
492 | #endif /* CONFIG_ALTIVEC */ | 543 | #endif /* CONFIG_ALTIVEC */ |
544 | if (copy_fpr_from_user(current, &sr->mc_fregs)) | ||
545 | return 1; | ||
493 | 546 | ||
494 | #ifdef CONFIG_VSX | 547 | #ifdef CONFIG_VSX |
495 | if (__copy_from_user(buf, &sr->mc_fregs,sizeof(sr->mc_fregs))) | ||
496 | return 1; | ||
497 | for (i = 0; i < 32 ; i++) | ||
498 | current->thread.TS_FPR(i) = buf[i]; | ||
499 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
500 | /* | 548 | /* |
501 | * Force the process to reload the VSX registers from | 549 | * Force the process to reload the VSX registers from |
502 | * current->thread when it next does VSX instruction. | 550 | * current->thread when it next does VSX instruction. |
@@ -507,18 +555,11 @@ static long restore_user_regs(struct pt_regs *regs, | |||
507 | * Restore altivec registers from the stack to a local | 555 | * Restore altivec registers from the stack to a local |
508 | * buffer, then write this out to the thread_struct | 556 | * buffer, then write this out to the thread_struct |
509 | */ | 557 | */ |
510 | if (__copy_from_user(buf, &sr->mc_vsregs, | 558 | if (copy_vsx_from_user(current, &sr->mc_vsregs)) |
511 | sizeof(sr->mc_vsregs))) | ||
512 | return 1; | 559 | return 1; |
513 | for (i = 0; i < 32 ; i++) | ||
514 | current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
515 | } else if (current->thread.used_vsr) | 560 | } else if (current->thread.used_vsr) |
516 | for (i = 0; i < 32 ; i++) | 561 | for (i = 0; i < 32 ; i++) |
517 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; | 562 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; |
518 | #else | ||
519 | if (__copy_from_user(current->thread.fpr, &sr->mc_fregs, | ||
520 | sizeof(sr->mc_fregs))) | ||
521 | return 1; | ||
522 | #endif /* CONFIG_VSX */ | 563 | #endif /* CONFIG_VSX */ |
523 | /* | 564 | /* |
524 | * force the process to reload the FP registers from | 565 | * force the process to reload the FP registers from |
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 8214e57aab67..93ebfb6944b6 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c | |||
@@ -89,10 +89,6 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
89 | #endif | 89 | #endif |
90 | unsigned long msr = regs->msr; | 90 | unsigned long msr = regs->msr; |
91 | long err = 0; | 91 | long err = 0; |
92 | #ifdef CONFIG_VSX | ||
93 | double buf[FP_REGS_SIZE]; | ||
94 | int i; | ||
95 | #endif | ||
96 | 92 | ||
97 | flush_fp_to_thread(current); | 93 | flush_fp_to_thread(current); |
98 | 94 | ||
@@ -117,12 +113,9 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
117 | err |= __put_user(0, &sc->v_regs); | 113 | err |= __put_user(0, &sc->v_regs); |
118 | #endif /* CONFIG_ALTIVEC */ | 114 | #endif /* CONFIG_ALTIVEC */ |
119 | flush_fp_to_thread(current); | 115 | flush_fp_to_thread(current); |
116 | /* copy fpr regs and fpscr */ | ||
117 | err |= copy_fpr_to_user(&sc->fp_regs, current); | ||
120 | #ifdef CONFIG_VSX | 118 | #ifdef CONFIG_VSX |
121 | /* Copy FP to local buffer then write that out */ | ||
122 | for (i = 0; i < 32 ; i++) | ||
123 | buf[i] = current->thread.TS_FPR(i); | ||
124 | memcpy(&buf[i], ¤t->thread.fpscr, sizeof(double)); | ||
125 | err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE); | ||
126 | /* | 119 | /* |
127 | * Copy VSX low doubleword to local buffer for formatting, | 120 | * Copy VSX low doubleword to local buffer for formatting, |
128 | * then out to userspace. Update v_regs to point after the | 121 | * then out to userspace. Update v_regs to point after the |
@@ -131,17 +124,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
131 | if (current->thread.used_vsr) { | 124 | if (current->thread.used_vsr) { |
132 | flush_vsx_to_thread(current); | 125 | flush_vsx_to_thread(current); |
133 | v_regs += ELF_NVRREG; | 126 | v_regs += ELF_NVRREG; |
134 | for (i = 0; i < 32 ; i++) | 127 | err |= copy_vsx_to_user(v_regs, current); |
135 | buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET]; | ||
136 | err |= __copy_to_user(v_regs, buf, 32 * sizeof(double)); | ||
137 | /* set MSR_VSX in the MSR value in the frame to | 128 | /* set MSR_VSX in the MSR value in the frame to |
138 | * indicate that sc->vs_reg) contains valid data. | 129 | * indicate that sc->vs_reg) contains valid data. |
139 | */ | 130 | */ |
140 | msr |= MSR_VSX; | 131 | msr |= MSR_VSX; |
141 | } | 132 | } |
142 | #else /* CONFIG_VSX */ | ||
143 | /* copy fpr regs and fpscr */ | ||
144 | err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE); | ||
145 | #endif /* CONFIG_VSX */ | 133 | #endif /* CONFIG_VSX */ |
146 | err |= __put_user(&sc->gp_regs, &sc->regs); | 134 | err |= __put_user(&sc->gp_regs, &sc->regs); |
147 | WARN_ON(!FULL_REGS(regs)); | 135 | WARN_ON(!FULL_REGS(regs)); |
@@ -165,13 +153,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
165 | #ifdef CONFIG_ALTIVEC | 153 | #ifdef CONFIG_ALTIVEC |
166 | elf_vrreg_t __user *v_regs; | 154 | elf_vrreg_t __user *v_regs; |
167 | #endif | 155 | #endif |
168 | #ifdef CONFIG_VSX | ||
169 | double buf[FP_REGS_SIZE]; | ||
170 | int i; | ||
171 | #endif | ||
172 | unsigned long err = 0; | 156 | unsigned long err = 0; |
173 | unsigned long save_r13 = 0; | 157 | unsigned long save_r13 = 0; |
174 | unsigned long msr; | 158 | unsigned long msr; |
159 | #ifdef CONFIG_VSX | ||
160 | int i; | ||
161 | #endif | ||
175 | 162 | ||
176 | /* If this is not a signal return, we preserve the TLS in r13 */ | 163 | /* If this is not a signal return, we preserve the TLS in r13 */ |
177 | if (!sig) | 164 | if (!sig) |
@@ -234,15 +221,9 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
234 | else | 221 | else |
235 | current->thread.vrsave = 0; | 222 | current->thread.vrsave = 0; |
236 | #endif /* CONFIG_ALTIVEC */ | 223 | #endif /* CONFIG_ALTIVEC */ |
237 | #ifdef CONFIG_VSX | ||
238 | /* restore floating point */ | 224 | /* restore floating point */ |
239 | err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE); | 225 | err |= copy_fpr_from_user(current, &sc->fp_regs); |
240 | if (err) | 226 | #ifdef CONFIG_VSX |
241 | return err; | ||
242 | for (i = 0; i < 32 ; i++) | ||
243 | current->thread.TS_FPR(i) = buf[i]; | ||
244 | memcpy(¤t->thread.fpscr, &buf[i], sizeof(double)); | ||
245 | |||
246 | /* | 227 | /* |
247 | * Get additional VSX data. Update v_regs to point after the | 228 | * Get additional VSX data. Update v_regs to point after the |
248 | * VMX data. Copy VSX low doubleword from userspace to local | 229 | * VMX data. Copy VSX low doubleword from userspace to local |
@@ -250,14 +231,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, | |||
250 | */ | 231 | */ |
251 | v_regs += ELF_NVRREG; | 232 | v_regs += ELF_NVRREG; |
252 | if ((msr & MSR_VSX) != 0) | 233 | if ((msr & MSR_VSX) != 0) |
253 | err |= __copy_from_user(buf, v_regs, 32 * sizeof(double)); | 234 | err |= copy_vsx_from_user(current, v_regs); |
254 | else | 235 | else |
255 | memset(buf, 0, 32 * sizeof(double)); | 236 | for (i = 0; i < 32 ; i++) |
237 | current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; | ||
256 | 238 | ||
257 | for (i = 0; i < 32 ; i++) | ||
258 | current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; | ||
259 | #else | 239 | #else |
260 | err |= __copy_from_user(¤t->thread.fpr, &sc->fp_regs, FP_REGS_SIZE); | ||
261 | #endif | 240 | #endif |
262 | return err; | 241 | return err; |
263 | } | 242 | } |
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index ce245a850db2..f177c60ea766 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c | |||
@@ -571,6 +571,11 @@ static __init int vdso_fixup_features(struct lib32_elfinfo *v32, | |||
571 | if (start64) | 571 | if (start64) |
572 | do_feature_fixups(powerpc_firmware_features, | 572 | do_feature_fixups(powerpc_firmware_features, |
573 | start64, start64 + size64); | 573 | start64, start64 + size64); |
574 | |||
575 | start64 = find_section64(v64->hdr, "__lwsync_fixup", &size64); | ||
576 | if (start64) | ||
577 | do_lwsync_fixups(cur_cpu_spec->cpu_features, | ||
578 | start64, start64 + size64); | ||
574 | #endif /* CONFIG_PPC64 */ | 579 | #endif /* CONFIG_PPC64 */ |
575 | 580 | ||
576 | start32 = find_section32(v32->hdr, "__ftr_fixup", &size32); | 581 | start32 = find_section32(v32->hdr, "__ftr_fixup", &size32); |
@@ -585,6 +590,11 @@ static __init int vdso_fixup_features(struct lib32_elfinfo *v32, | |||
585 | start32, start32 + size32); | 590 | start32, start32 + size32); |
586 | #endif /* CONFIG_PPC64 */ | 591 | #endif /* CONFIG_PPC64 */ |
587 | 592 | ||
593 | start32 = find_section32(v32->hdr, "__lwsync_fixup", &size32); | ||
594 | if (start32) | ||
595 | do_lwsync_fixups(cur_cpu_spec->cpu_features, | ||
596 | start32, start32 + size32); | ||
597 | |||
588 | return 0; | 598 | return 0; |
589 | } | 599 | } |
590 | 600 | ||
diff --git a/arch/powerpc/kernel/vdso32/vdso32.lds.S b/arch/powerpc/kernel/vdso32/vdso32.lds.S index 271793577cdc..be3b6a41dc09 100644 --- a/arch/powerpc/kernel/vdso32/vdso32.lds.S +++ b/arch/powerpc/kernel/vdso32/vdso32.lds.S | |||
@@ -33,6 +33,9 @@ SECTIONS | |||
33 | . = ALIGN(8); | 33 | . = ALIGN(8); |
34 | __ftr_fixup : { *(__ftr_fixup) } | 34 | __ftr_fixup : { *(__ftr_fixup) } |
35 | 35 | ||
36 | . = ALIGN(8); | ||
37 | __lwsync_fixup : { *(__lwsync_fixup) } | ||
38 | |||
36 | #ifdef CONFIG_PPC64 | 39 | #ifdef CONFIG_PPC64 |
37 | . = ALIGN(8); | 40 | . = ALIGN(8); |
38 | __fw_ftr_fixup : { *(__fw_ftr_fixup) } | 41 | __fw_ftr_fixup : { *(__fw_ftr_fixup) } |
diff --git a/arch/powerpc/kernel/vdso64/vdso64.lds.S b/arch/powerpc/kernel/vdso64/vdso64.lds.S index e608d1bd3bff..d0b2526dd38d 100644 --- a/arch/powerpc/kernel/vdso64/vdso64.lds.S +++ b/arch/powerpc/kernel/vdso64/vdso64.lds.S | |||
@@ -35,6 +35,9 @@ SECTIONS | |||
35 | __ftr_fixup : { *(__ftr_fixup) } | 35 | __ftr_fixup : { *(__ftr_fixup) } |
36 | 36 | ||
37 | . = ALIGN(8); | 37 | . = ALIGN(8); |
38 | __lwsync_fixup : { *(__lwsync_fixup) } | ||
39 | |||
40 | . = ALIGN(8); | ||
38 | __fw_ftr_fixup : { *(__fw_ftr_fixup) } | 41 | __fw_ftr_fixup : { *(__fw_ftr_fixup) } |
39 | 42 | ||
40 | /* | 43 | /* |
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 3c07811989fc..6856f6c15727 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S | |||
@@ -127,6 +127,12 @@ SECTIONS | |||
127 | *(__ftr_fixup) | 127 | *(__ftr_fixup) |
128 | __stop___ftr_fixup = .; | 128 | __stop___ftr_fixup = .; |
129 | } | 129 | } |
130 | . = ALIGN(8); | ||
131 | __lwsync_fixup : AT(ADDR(__lwsync_fixup) - LOAD_OFFSET) { | ||
132 | __start___lwsync_fixup = .; | ||
133 | *(__lwsync_fixup) | ||
134 | __stop___lwsync_fixup = .; | ||
135 | } | ||
130 | #ifdef CONFIG_PPC64 | 136 | #ifdef CONFIG_PPC64 |
131 | . = ALIGN(8); | 137 | . = ALIGN(8); |
132 | __fw_ftr_fixup : AT(ADDR(__fw_ftr_fixup) - LOAD_OFFSET) { | 138 | __fw_ftr_fixup : AT(ADDR(__fw_ftr_fixup) - LOAD_OFFSET) { |
diff --git a/arch/powerpc/lib/feature-fixups-test.S b/arch/powerpc/lib/feature-fixups-test.S index 10d038b501a6..cb737484c5aa 100644 --- a/arch/powerpc/lib/feature-fixups-test.S +++ b/arch/powerpc/lib/feature-fixups-test.S | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | #include <asm/feature-fixups.h> | 11 | #include <asm/feature-fixups.h> |
12 | #include <asm/ppc_asm.h> | 12 | #include <asm/ppc_asm.h> |
13 | #include <asm/synch.h> | ||
13 | 14 | ||
14 | .text | 15 | .text |
15 | 16 | ||
@@ -139,14 +140,14 @@ globl(ftr_fixup_test6) | |||
139 | 1: or 1,1,1 | 140 | 1: or 1,1,1 |
140 | BEGIN_FTR_SECTION | 141 | BEGIN_FTR_SECTION |
141 | or 5,5,5 | 142 | or 5,5,5 |
142 | 2: cmpdi r3,0 | 143 | 2: PPC_LCMPI r3,0 |
143 | beq 4f | 144 | beq 4f |
144 | blt 2b | 145 | blt 2b |
145 | b 1b | 146 | b 1b |
146 | b 4f | 147 | b 4f |
147 | FTR_SECTION_ELSE | 148 | FTR_SECTION_ELSE |
148 | 2: or 2,2,2 | 149 | 2: or 2,2,2 |
149 | cmpdi r3,1 | 150 | PPC_LCMPI r3,1 |
150 | beq 3f | 151 | beq 3f |
151 | blt 2b | 152 | blt 2b |
152 | b 3f | 153 | b 3f |
@@ -161,7 +162,7 @@ globl(end_ftr_fixup_test6) | |||
161 | globl(ftr_fixup_test6_expected) | 162 | globl(ftr_fixup_test6_expected) |
162 | 1: or 1,1,1 | 163 | 1: or 1,1,1 |
163 | 2: or 2,2,2 | 164 | 2: or 2,2,2 |
164 | cmpdi r3,1 | 165 | PPC_LCMPI r3,1 |
165 | beq 3f | 166 | beq 3f |
166 | blt 2b | 167 | blt 2b |
167 | b 3f | 168 | b 3f |
@@ -725,3 +726,17 @@ MAKE_MACRO_TEST_EXPECTED(FTR); | |||
725 | MAKE_MACRO_TEST(FW_FTR); | 726 | MAKE_MACRO_TEST(FW_FTR); |
726 | MAKE_MACRO_TEST_EXPECTED(FW_FTR); | 727 | MAKE_MACRO_TEST_EXPECTED(FW_FTR); |
727 | #endif | 728 | #endif |
729 | |||
730 | globl(lwsync_fixup_test) | ||
731 | 1: or 1,1,1 | ||
732 | LWSYNC | ||
733 | globl(end_lwsync_fixup_test) | ||
734 | |||
735 | globl(lwsync_fixup_test_expected_LWSYNC) | ||
736 | 1: or 1,1,1 | ||
737 | lwsync | ||
738 | |||
739 | globl(lwsync_fixup_test_expected_SYNC) | ||
740 | 1: or 1,1,1 | ||
741 | sync | ||
742 | |||
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c index 48e1ed89052d..4e43702b9813 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c | |||
@@ -110,6 +110,22 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | void do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end) | ||
114 | { | ||
115 | unsigned int *start, *end, *dest; | ||
116 | |||
117 | if (!(value & CPU_FTR_LWSYNC)) | ||
118 | return ; | ||
119 | |||
120 | start = fixup_start; | ||
121 | end = fixup_end; | ||
122 | |||
123 | for (; start < end; start++) { | ||
124 | dest = (void *)start + *start; | ||
125 | patch_instruction(dest, PPC_LWSYNC_INSTR); | ||
126 | } | ||
127 | } | ||
128 | |||
113 | #ifdef CONFIG_FTR_FIXUP_SELFTEST | 129 | #ifdef CONFIG_FTR_FIXUP_SELFTEST |
114 | 130 | ||
115 | #define check(x) \ | 131 | #define check(x) \ |
@@ -295,6 +311,25 @@ static void test_fw_macros(void) | |||
295 | #endif | 311 | #endif |
296 | } | 312 | } |
297 | 313 | ||
314 | static void test_lwsync_macros(void) | ||
315 | { | ||
316 | extern void lwsync_fixup_test; | ||
317 | extern void end_lwsync_fixup_test; | ||
318 | extern void lwsync_fixup_test_expected_LWSYNC; | ||
319 | extern void lwsync_fixup_test_expected_SYNC; | ||
320 | unsigned long size = &end_lwsync_fixup_test - | ||
321 | &lwsync_fixup_test; | ||
322 | |||
323 | /* The fixups have already been done for us during boot */ | ||
324 | if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) { | ||
325 | check(memcmp(&lwsync_fixup_test, | ||
326 | &lwsync_fixup_test_expected_LWSYNC, size) == 0); | ||
327 | } else { | ||
328 | check(memcmp(&lwsync_fixup_test, | ||
329 | &lwsync_fixup_test_expected_SYNC, size) == 0); | ||
330 | } | ||
331 | } | ||
332 | |||
298 | static int __init test_feature_fixups(void) | 333 | static int __init test_feature_fixups(void) |
299 | { | 334 | { |
300 | printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); | 335 | printk(KERN_DEBUG "Running feature fixup self-tests ...\n"); |
@@ -307,6 +342,7 @@ static int __init test_feature_fixups(void) | |||
307 | test_alternative_case_with_external_branch(); | 342 | test_alternative_case_with_external_branch(); |
308 | test_cpu_macros(); | 343 | test_cpu_macros(); |
309 | test_fw_macros(); | 344 | test_fw_macros(); |
345 | test_lwsync_macros(); | ||
310 | 346 | ||
311 | return 0; | 347 | return 0; |
312 | } | 348 | } |
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 51f82d83bf14..776ba6ad5e1e 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include <asm/btext.h> | 44 | #include <asm/btext.h> |
45 | #include <asm/tlb.h> | 45 | #include <asm/tlb.h> |
46 | #include <asm/sections.h> | 46 | #include <asm/sections.h> |
47 | #include <asm/sparsemem.h> | ||
47 | #include <asm/vdso.h> | 48 | #include <asm/vdso.h> |
48 | #include <asm/fixmap.h> | 49 | #include <asm/fixmap.h> |
49 | 50 | ||
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index dc704da363eb..cf4bffba6f7c 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c | |||
@@ -268,6 +268,144 @@ static unsigned long __devinit read_n_cells(int n, const unsigned int **buf) | |||
268 | return result; | 268 | return result; |
269 | } | 269 | } |
270 | 270 | ||
271 | struct of_drconf_cell { | ||
272 | u64 base_addr; | ||
273 | u32 drc_index; | ||
274 | u32 reserved; | ||
275 | u32 aa_index; | ||
276 | u32 flags; | ||
277 | }; | ||
278 | |||
279 | #define DRCONF_MEM_ASSIGNED 0x00000008 | ||
280 | #define DRCONF_MEM_AI_INVALID 0x00000040 | ||
281 | #define DRCONF_MEM_RESERVED 0x00000080 | ||
282 | |||
283 | /* | ||
284 | * Read the next lmb list entry from the ibm,dynamic-memory property | ||
285 | * and return the information in the provided of_drconf_cell structure. | ||
286 | */ | ||
287 | static void read_drconf_cell(struct of_drconf_cell *drmem, const u32 **cellp) | ||
288 | { | ||
289 | const u32 *cp; | ||
290 | |||
291 | drmem->base_addr = read_n_cells(n_mem_addr_cells, cellp); | ||
292 | |||
293 | cp = *cellp; | ||
294 | drmem->drc_index = cp[0]; | ||
295 | drmem->reserved = cp[1]; | ||
296 | drmem->aa_index = cp[2]; | ||
297 | drmem->flags = cp[3]; | ||
298 | |||
299 | *cellp = cp + 4; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Retreive and validate the ibm,dynamic-memory property of the device tree. | ||
304 | * | ||
305 | * The layout of the ibm,dynamic-memory property is a number N of lmb | ||
306 | * list entries followed by N lmb list entries. Each lmb list entry | ||
307 | * contains information as layed out in the of_drconf_cell struct above. | ||
308 | */ | ||
309 | static int of_get_drconf_memory(struct device_node *memory, const u32 **dm) | ||
310 | { | ||
311 | const u32 *prop; | ||
312 | u32 len, entries; | ||
313 | |||
314 | prop = of_get_property(memory, "ibm,dynamic-memory", &len); | ||
315 | if (!prop || len < sizeof(unsigned int)) | ||
316 | return 0; | ||
317 | |||
318 | entries = *prop++; | ||
319 | |||
320 | /* Now that we know the number of entries, revalidate the size | ||
321 | * of the property read in to ensure we have everything | ||
322 | */ | ||
323 | if (len < (entries * (n_mem_addr_cells + 4) + 1) * sizeof(unsigned int)) | ||
324 | return 0; | ||
325 | |||
326 | *dm = prop; | ||
327 | return entries; | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * Retreive and validate the ibm,lmb-size property for drconf memory | ||
332 | * from the device tree. | ||
333 | */ | ||
334 | static u64 of_get_lmb_size(struct device_node *memory) | ||
335 | { | ||
336 | const u32 *prop; | ||
337 | u32 len; | ||
338 | |||
339 | prop = of_get_property(memory, "ibm,lmb-size", &len); | ||
340 | if (!prop || len < sizeof(unsigned int)) | ||
341 | return 0; | ||
342 | |||
343 | return read_n_cells(n_mem_size_cells, &prop); | ||
344 | } | ||
345 | |||
346 | struct assoc_arrays { | ||
347 | u32 n_arrays; | ||
348 | u32 array_sz; | ||
349 | const u32 *arrays; | ||
350 | }; | ||
351 | |||
352 | /* | ||
353 | * Retreive and validate the list of associativity arrays for drconf | ||
354 | * memory from the ibm,associativity-lookup-arrays property of the | ||
355 | * device tree.. | ||
356 | * | ||
357 | * The layout of the ibm,associativity-lookup-arrays property is a number N | ||
358 | * indicating the number of associativity arrays, followed by a number M | ||
359 | * indicating the size of each associativity array, followed by a list | ||
360 | * of N associativity arrays. | ||
361 | */ | ||
362 | static int of_get_assoc_arrays(struct device_node *memory, | ||
363 | struct assoc_arrays *aa) | ||
364 | { | ||
365 | const u32 *prop; | ||
366 | u32 len; | ||
367 | |||
368 | prop = of_get_property(memory, "ibm,associativity-lookup-arrays", &len); | ||
369 | if (!prop || len < 2 * sizeof(unsigned int)) | ||
370 | return -1; | ||
371 | |||
372 | aa->n_arrays = *prop++; | ||
373 | aa->array_sz = *prop++; | ||
374 | |||
375 | /* Now that we know the number of arrrays and size of each array, | ||
376 | * revalidate the size of the property read in. | ||
377 | */ | ||
378 | if (len < (aa->n_arrays * aa->array_sz + 2) * sizeof(unsigned int)) | ||
379 | return -1; | ||
380 | |||
381 | aa->arrays = prop; | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * This is like of_node_to_nid_single() for memory represented in the | ||
387 | * ibm,dynamic-reconfiguration-memory node. | ||
388 | */ | ||
389 | static int of_drconf_to_nid_single(struct of_drconf_cell *drmem, | ||
390 | struct assoc_arrays *aa) | ||
391 | { | ||
392 | int default_nid = 0; | ||
393 | int nid = default_nid; | ||
394 | int index; | ||
395 | |||
396 | if (min_common_depth > 0 && min_common_depth <= aa->array_sz && | ||
397 | !(drmem->flags & DRCONF_MEM_AI_INVALID) && | ||
398 | drmem->aa_index < aa->n_arrays) { | ||
399 | index = drmem->aa_index * aa->array_sz + min_common_depth - 1; | ||
400 | nid = aa->arrays[index]; | ||
401 | |||
402 | if (nid == 0xffff || nid >= MAX_NUMNODES) | ||
403 | nid = default_nid; | ||
404 | } | ||
405 | |||
406 | return nid; | ||
407 | } | ||
408 | |||
271 | /* | 409 | /* |
272 | * Figure out to which domain a cpu belongs and stick it there. | 410 | * Figure out to which domain a cpu belongs and stick it there. |
273 | * Return the id of the domain used. | 411 | * Return the id of the domain used. |
@@ -355,57 +493,50 @@ static unsigned long __init numa_enforce_memory_limit(unsigned long start, | |||
355 | */ | 493 | */ |
356 | static void __init parse_drconf_memory(struct device_node *memory) | 494 | static void __init parse_drconf_memory(struct device_node *memory) |
357 | { | 495 | { |
358 | const unsigned int *lm, *dm, *aa; | 496 | const u32 *dm; |
359 | unsigned int ls, ld, la; | 497 | unsigned int n, rc; |
360 | unsigned int n, aam, aalen; | 498 | unsigned long lmb_size, size; |
361 | unsigned long lmb_size, size, start; | 499 | int nid; |
362 | int nid, default_nid = 0; | 500 | struct assoc_arrays aa; |
363 | unsigned int ai, flags; | 501 | |
364 | 502 | n = of_get_drconf_memory(memory, &dm); | |
365 | lm = of_get_property(memory, "ibm,lmb-size", &ls); | 503 | if (!n) |
366 | dm = of_get_property(memory, "ibm,dynamic-memory", &ld); | 504 | return; |
367 | aa = of_get_property(memory, "ibm,associativity-lookup-arrays", &la); | 505 | |
368 | if (!lm || !dm || !aa || | 506 | lmb_size = of_get_lmb_size(memory); |
369 | ls < sizeof(unsigned int) || ld < sizeof(unsigned int) || | 507 | if (!lmb_size) |
370 | la < 2 * sizeof(unsigned int)) | ||
371 | return; | 508 | return; |
372 | 509 | ||
373 | lmb_size = read_n_cells(n_mem_size_cells, &lm); | 510 | rc = of_get_assoc_arrays(memory, &aa); |
374 | n = *dm++; /* number of LMBs */ | 511 | if (rc) |
375 | aam = *aa++; /* number of associativity lists */ | ||
376 | aalen = *aa++; /* length of each associativity list */ | ||
377 | if (ld < (n * (n_mem_addr_cells + 4) + 1) * sizeof(unsigned int) || | ||
378 | la < (aam * aalen + 2) * sizeof(unsigned int)) | ||
379 | return; | 512 | return; |
380 | 513 | ||
381 | for (; n != 0; --n) { | 514 | for (; n != 0; --n) { |
382 | start = read_n_cells(n_mem_addr_cells, &dm); | 515 | struct of_drconf_cell drmem; |
383 | ai = dm[2]; | 516 | |
384 | flags = dm[3]; | 517 | read_drconf_cell(&drmem, &dm); |
385 | dm += 4; | 518 | |
386 | /* 0x80 == reserved, 0x8 = assigned to us */ | 519 | /* skip this block if the reserved bit is set in flags (0x80) |
387 | if ((flags & 0x80) || !(flags & 0x8)) | 520 | or if the block is not assigned to this partition (0x8) */ |
521 | if ((drmem.flags & DRCONF_MEM_RESERVED) | ||
522 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) | ||
388 | continue; | 523 | continue; |
389 | nid = default_nid; | ||
390 | /* flags & 0x40 means associativity index is invalid */ | ||
391 | if (min_common_depth > 0 && min_common_depth <= aalen && | ||
392 | (flags & 0x40) == 0 && ai < aam) { | ||
393 | /* this is like of_node_to_nid_single */ | ||
394 | nid = aa[ai * aalen + min_common_depth - 1]; | ||
395 | if (nid == 0xffff || nid >= MAX_NUMNODES) | ||
396 | nid = default_nid; | ||
397 | } | ||
398 | 524 | ||
399 | fake_numa_create_new_node(((start + lmb_size) >> PAGE_SHIFT), | 525 | nid = of_drconf_to_nid_single(&drmem, &aa); |
400 | &nid); | 526 | |
527 | fake_numa_create_new_node( | ||
528 | ((drmem.base_addr + lmb_size) >> PAGE_SHIFT), | ||
529 | &nid); | ||
530 | |||
401 | node_set_online(nid); | 531 | node_set_online(nid); |
402 | 532 | ||
403 | size = numa_enforce_memory_limit(start, lmb_size); | 533 | size = numa_enforce_memory_limit(drmem.base_addr, lmb_size); |
404 | if (!size) | 534 | if (!size) |
405 | continue; | 535 | continue; |
406 | 536 | ||
407 | add_active_range(nid, start >> PAGE_SHIFT, | 537 | add_active_range(nid, drmem.base_addr >> PAGE_SHIFT, |
408 | (start >> PAGE_SHIFT) + (size >> PAGE_SHIFT)); | 538 | (drmem.base_addr >> PAGE_SHIFT) |
539 | + (size >> PAGE_SHIFT)); | ||
409 | } | 540 | } |
410 | } | 541 | } |
411 | 542 | ||
@@ -770,6 +901,79 @@ early_param("numa", early_numa); | |||
770 | 901 | ||
771 | #ifdef CONFIG_MEMORY_HOTPLUG | 902 | #ifdef CONFIG_MEMORY_HOTPLUG |
772 | /* | 903 | /* |
904 | * Validate the node associated with the memory section we are | ||
905 | * trying to add. | ||
906 | */ | ||
907 | int valid_hot_add_scn(int *nid, unsigned long start, u32 lmb_size, | ||
908 | unsigned long scn_addr) | ||
909 | { | ||
910 | nodemask_t nodes; | ||
911 | |||
912 | if (*nid < 0 || !node_online(*nid)) | ||
913 | *nid = any_online_node(NODE_MASK_ALL); | ||
914 | |||
915 | if ((scn_addr >= start) && (scn_addr < (start + lmb_size))) { | ||
916 | nodes_setall(nodes); | ||
917 | while (NODE_DATA(*nid)->node_spanned_pages == 0) { | ||
918 | node_clear(*nid, nodes); | ||
919 | *nid = any_online_node(nodes); | ||
920 | } | ||
921 | |||
922 | return 1; | ||
923 | } | ||
924 | |||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * Find the node associated with a hot added memory section represented | ||
930 | * by the ibm,dynamic-reconfiguration-memory node. | ||
931 | */ | ||
932 | static int hot_add_drconf_scn_to_nid(struct device_node *memory, | ||
933 | unsigned long scn_addr) | ||
934 | { | ||
935 | const u32 *dm; | ||
936 | unsigned int n, rc; | ||
937 | unsigned long lmb_size; | ||
938 | int default_nid = any_online_node(NODE_MASK_ALL); | ||
939 | int nid; | ||
940 | struct assoc_arrays aa; | ||
941 | |||
942 | n = of_get_drconf_memory(memory, &dm); | ||
943 | if (!n) | ||
944 | return default_nid;; | ||
945 | |||
946 | lmb_size = of_get_lmb_size(memory); | ||
947 | if (!lmb_size) | ||
948 | return default_nid; | ||
949 | |||
950 | rc = of_get_assoc_arrays(memory, &aa); | ||
951 | if (rc) | ||
952 | return default_nid; | ||
953 | |||
954 | for (; n != 0; --n) { | ||
955 | struct of_drconf_cell drmem; | ||
956 | |||
957 | read_drconf_cell(&drmem, &dm); | ||
958 | |||
959 | /* skip this block if it is reserved or not assigned to | ||
960 | * this partition */ | ||
961 | if ((drmem.flags & DRCONF_MEM_RESERVED) | ||
962 | || !(drmem.flags & DRCONF_MEM_ASSIGNED)) | ||
963 | continue; | ||
964 | |||
965 | nid = of_drconf_to_nid_single(&drmem, &aa); | ||
966 | |||
967 | if (valid_hot_add_scn(&nid, drmem.base_addr, lmb_size, | ||
968 | scn_addr)) | ||
969 | return nid; | ||
970 | } | ||
971 | |||
972 | BUG(); /* section address should be found above */ | ||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | /* | ||
773 | * Find the node associated with a hot added memory section. Section | 977 | * Find the node associated with a hot added memory section. Section |
774 | * corresponds to a SPARSEMEM section, not an LMB. It is assumed that | 978 | * corresponds to a SPARSEMEM section, not an LMB. It is assumed that |
775 | * sections are fully contained within a single LMB. | 979 | * sections are fully contained within a single LMB. |
@@ -777,12 +981,17 @@ early_param("numa", early_numa); | |||
777 | int hot_add_scn_to_nid(unsigned long scn_addr) | 981 | int hot_add_scn_to_nid(unsigned long scn_addr) |
778 | { | 982 | { |
779 | struct device_node *memory = NULL; | 983 | struct device_node *memory = NULL; |
780 | nodemask_t nodes; | ||
781 | int default_nid = any_online_node(NODE_MASK_ALL); | ||
782 | int nid; | 984 | int nid; |
783 | 985 | ||
784 | if (!numa_enabled || (min_common_depth < 0)) | 986 | if (!numa_enabled || (min_common_depth < 0)) |
785 | return default_nid; | 987 | return any_online_node(NODE_MASK_ALL); |
988 | |||
989 | memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | ||
990 | if (memory) { | ||
991 | nid = hot_add_drconf_scn_to_nid(memory, scn_addr); | ||
992 | of_node_put(memory); | ||
993 | return nid; | ||
994 | } | ||
786 | 995 | ||
787 | while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { | 996 | while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { |
788 | unsigned long start, size; | 997 | unsigned long start, size; |
@@ -801,13 +1010,9 @@ ha_new_range: | |||
801 | size = read_n_cells(n_mem_size_cells, &memcell_buf); | 1010 | size = read_n_cells(n_mem_size_cells, &memcell_buf); |
802 | nid = of_node_to_nid_single(memory); | 1011 | nid = of_node_to_nid_single(memory); |
803 | 1012 | ||
804 | /* Domains not present at boot default to 0 */ | 1013 | if (valid_hot_add_scn(&nid, start, size, scn_addr)) { |
805 | if (nid < 0 || !node_online(nid)) | ||
806 | nid = default_nid; | ||
807 | |||
808 | if ((scn_addr >= start) && (scn_addr < (start + size))) { | ||
809 | of_node_put(memory); | 1014 | of_node_put(memory); |
810 | goto got_nid; | 1015 | return nid; |
811 | } | 1016 | } |
812 | 1017 | ||
813 | if (--ranges) /* process all ranges in cell */ | 1018 | if (--ranges) /* process all ranges in cell */ |
@@ -815,14 +1020,5 @@ ha_new_range: | |||
815 | } | 1020 | } |
816 | BUG(); /* section address should be found above */ | 1021 | BUG(); /* section address should be found above */ |
817 | return 0; | 1022 | return 0; |
818 | |||
819 | /* Temporary code to ensure that returned node is not empty */ | ||
820 | got_nid: | ||
821 | nodes_setall(nodes); | ||
822 | while (NODE_DATA(nid)->node_spanned_pages == 0) { | ||
823 | node_clear(nid, nodes); | ||
824 | nid = any_online_node(nodes); | ||
825 | } | ||
826 | return nid; | ||
827 | } | 1023 | } |
828 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 1024 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 3c5727dd5aa5..a1a368dd2d99 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c | |||
@@ -15,34 +15,13 @@ | |||
15 | #include <asm/machdep.h> | 15 | #include <asm/machdep.h> |
16 | #include <asm/pSeries_reconfig.h> | 16 | #include <asm/pSeries_reconfig.h> |
17 | 17 | ||
18 | static int pseries_remove_memory(struct device_node *np) | 18 | static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size) |
19 | { | 19 | { |
20 | const char *type; | 20 | unsigned long start, start_pfn; |
21 | const unsigned int *my_index; | ||
22 | const unsigned int *regs; | ||
23 | u64 start_pfn, start; | ||
24 | struct zone *zone; | 21 | struct zone *zone; |
25 | int ret = -EINVAL; | 22 | int ret; |
26 | |||
27 | /* | ||
28 | * Check to see if we are actually removing memory | ||
29 | */ | ||
30 | type = of_get_property(np, "device_type", NULL); | ||
31 | if (type == NULL || strcmp(type, "memory") != 0) | ||
32 | return 0; | ||
33 | 23 | ||
34 | /* | 24 | start_pfn = base >> PFN_SECTION_SHIFT; |
35 | * Find the memory index and size of the removing section | ||
36 | */ | ||
37 | my_index = of_get_property(np, "ibm,my-drc-index", NULL); | ||
38 | if (!my_index) | ||
39 | return ret; | ||
40 | |||
41 | regs = of_get_property(np, "reg", NULL); | ||
42 | if (!regs) | ||
43 | return ret; | ||
44 | |||
45 | start_pfn = section_nr_to_pfn(*my_index & 0xffff); | ||
46 | zone = page_zone(pfn_to_page(start_pfn)); | 25 | zone = page_zone(pfn_to_page(start_pfn)); |
47 | 26 | ||
48 | /* | 27 | /* |
@@ -54,56 +33,111 @@ static int pseries_remove_memory(struct device_node *np) | |||
54 | * to sysfs "state" file and we can't remove sysfs entries | 33 | * to sysfs "state" file and we can't remove sysfs entries |
55 | * while writing to it. So we have to defer it to here. | 34 | * while writing to it. So we have to defer it to here. |
56 | */ | 35 | */ |
57 | ret = __remove_pages(zone, start_pfn, regs[3] >> PAGE_SHIFT); | 36 | ret = __remove_pages(zone, start_pfn, lmb_size >> PAGE_SHIFT); |
58 | if (ret) | 37 | if (ret) |
59 | return ret; | 38 | return ret; |
60 | 39 | ||
61 | /* | 40 | /* |
62 | * Update memory regions for memory remove | 41 | * Update memory regions for memory remove |
63 | */ | 42 | */ |
64 | lmb_remove(start_pfn << PAGE_SHIFT, regs[3]); | 43 | lmb_remove(base, lmb_size); |
65 | 44 | ||
66 | /* | 45 | /* |
67 | * Remove htab bolted mappings for this section of memory | 46 | * Remove htab bolted mappings for this section of memory |
68 | */ | 47 | */ |
69 | start = (unsigned long)__va(start_pfn << PAGE_SHIFT); | 48 | start = (unsigned long)__va(base); |
70 | ret = remove_section_mapping(start, start + regs[3]); | 49 | ret = remove_section_mapping(start, start + lmb_size); |
71 | return ret; | 50 | return ret; |
72 | } | 51 | } |
73 | 52 | ||
74 | static int pseries_add_memory(struct device_node *np) | 53 | static int pseries_remove_memory(struct device_node *np) |
75 | { | 54 | { |
76 | const char *type; | 55 | const char *type; |
77 | const unsigned int *my_index; | ||
78 | const unsigned int *regs; | 56 | const unsigned int *regs; |
79 | u64 start_pfn; | 57 | unsigned long base; |
58 | unsigned int lmb_size; | ||
80 | int ret = -EINVAL; | 59 | int ret = -EINVAL; |
81 | 60 | ||
82 | /* | 61 | /* |
83 | * Check to see if we are actually adding memory | 62 | * Check to see if we are actually removing memory |
84 | */ | 63 | */ |
85 | type = of_get_property(np, "device_type", NULL); | 64 | type = of_get_property(np, "device_type", NULL); |
86 | if (type == NULL || strcmp(type, "memory") != 0) | 65 | if (type == NULL || strcmp(type, "memory") != 0) |
87 | return 0; | 66 | return 0; |
88 | 67 | ||
89 | /* | 68 | /* |
90 | * Find the memory index and size of the added section | 69 | * Find the bae address and size of the lmb |
91 | */ | 70 | */ |
92 | my_index = of_get_property(np, "ibm,my-drc-index", NULL); | 71 | regs = of_get_property(np, "reg", NULL); |
93 | if (!my_index) | 72 | if (!regs) |
94 | return ret; | 73 | return ret; |
95 | 74 | ||
75 | base = *(unsigned long *)regs; | ||
76 | lmb_size = regs[3]; | ||
77 | |||
78 | ret = pseries_remove_lmb(base, lmb_size); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | static int pseries_add_memory(struct device_node *np) | ||
83 | { | ||
84 | const char *type; | ||
85 | const unsigned int *regs; | ||
86 | unsigned long base; | ||
87 | unsigned int lmb_size; | ||
88 | int ret = -EINVAL; | ||
89 | |||
90 | /* | ||
91 | * Check to see if we are actually adding memory | ||
92 | */ | ||
93 | type = of_get_property(np, "device_type", NULL); | ||
94 | if (type == NULL || strcmp(type, "memory") != 0) | ||
95 | return 0; | ||
96 | |||
97 | /* | ||
98 | * Find the base and size of the lmb | ||
99 | */ | ||
96 | regs = of_get_property(np, "reg", NULL); | 100 | regs = of_get_property(np, "reg", NULL); |
97 | if (!regs) | 101 | if (!regs) |
98 | return ret; | 102 | return ret; |
99 | 103 | ||
100 | start_pfn = section_nr_to_pfn(*my_index & 0xffff); | 104 | base = *(unsigned long *)regs; |
105 | lmb_size = regs[3]; | ||
101 | 106 | ||
102 | /* | 107 | /* |
103 | * Update memory region to represent the memory add | 108 | * Update memory region to represent the memory add |
104 | */ | 109 | */ |
105 | lmb_add(start_pfn << PAGE_SHIFT, regs[3]); | 110 | ret = lmb_add(base, lmb_size); |
106 | return 0; | 111 | return (ret < 0) ? -EINVAL : 0; |
112 | } | ||
113 | |||
114 | static int pseries_drconf_memory(unsigned long *base, unsigned int action) | ||
115 | { | ||
116 | struct device_node *np; | ||
117 | const unsigned long *lmb_size; | ||
118 | int rc; | ||
119 | |||
120 | np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | ||
121 | if (!np) | ||
122 | return -EINVAL; | ||
123 | |||
124 | lmb_size = of_get_property(np, "ibm,lmb-size", NULL); | ||
125 | if (!lmb_size) { | ||
126 | of_node_put(np); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | |||
130 | if (action == PSERIES_DRCONF_MEM_ADD) { | ||
131 | rc = lmb_add(*base, *lmb_size); | ||
132 | rc = (rc < 0) ? -EINVAL : 0; | ||
133 | } else if (action == PSERIES_DRCONF_MEM_REMOVE) { | ||
134 | rc = pseries_remove_lmb(*base, *lmb_size); | ||
135 | } else { | ||
136 | rc = -EINVAL; | ||
137 | } | ||
138 | |||
139 | of_node_put(np); | ||
140 | return rc; | ||
107 | } | 141 | } |
108 | 142 | ||
109 | static int pseries_memory_notifier(struct notifier_block *nb, | 143 | static int pseries_memory_notifier(struct notifier_block *nb, |
@@ -120,6 +154,11 @@ static int pseries_memory_notifier(struct notifier_block *nb, | |||
120 | if (pseries_remove_memory(node)) | 154 | if (pseries_remove_memory(node)) |
121 | err = NOTIFY_BAD; | 155 | err = NOTIFY_BAD; |
122 | break; | 156 | break; |
157 | case PSERIES_DRCONF_MEM_ADD: | ||
158 | case PSERIES_DRCONF_MEM_REMOVE: | ||
159 | if (pseries_drconf_memory(node, action)) | ||
160 | err = NOTIFY_BAD; | ||
161 | break; | ||
123 | default: | 162 | default: |
124 | err = NOTIFY_DONE; | 163 | err = NOTIFY_DONE; |
125 | break; | 164 | break; |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 75769aae41d5..7637bd38c795 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
@@ -365,7 +365,7 @@ static char *parse_node(char *buf, size_t bufsize, struct device_node **npp) | |||
365 | *buf = '\0'; | 365 | *buf = '\0'; |
366 | buf++; | 366 | buf++; |
367 | 367 | ||
368 | handle = simple_strtoul(handle_str, NULL, 10); | 368 | handle = simple_strtoul(handle_str, NULL, 0); |
369 | 369 | ||
370 | *npp = of_find_node_by_phandle(handle); | 370 | *npp = of_find_node_by_phandle(handle); |
371 | return buf; | 371 | return buf; |
@@ -422,8 +422,8 @@ static int do_update_property(char *buf, size_t bufsize) | |||
422 | { | 422 | { |
423 | struct device_node *np; | 423 | struct device_node *np; |
424 | unsigned char *value; | 424 | unsigned char *value; |
425 | char *name, *end; | 425 | char *name, *end, *next_prop; |
426 | int length; | 426 | int rc, length; |
427 | struct property *newprop, *oldprop; | 427 | struct property *newprop, *oldprop; |
428 | buf = parse_node(buf, bufsize, &np); | 428 | buf = parse_node(buf, bufsize, &np); |
429 | end = buf + bufsize; | 429 | end = buf + bufsize; |
@@ -431,7 +431,8 @@ static int do_update_property(char *buf, size_t bufsize) | |||
431 | if (!np) | 431 | if (!np) |
432 | return -ENODEV; | 432 | return -ENODEV; |
433 | 433 | ||
434 | if (parse_next_property(buf, end, &name, &length, &value) == NULL) | 434 | next_prop = parse_next_property(buf, end, &name, &length, &value); |
435 | if (!next_prop) | ||
435 | return -EINVAL; | 436 | return -EINVAL; |
436 | 437 | ||
437 | newprop = new_property(name, length, value, NULL); | 438 | newprop = new_property(name, length, value, NULL); |
@@ -442,7 +443,34 @@ static int do_update_property(char *buf, size_t bufsize) | |||
442 | if (!oldprop) | 443 | if (!oldprop) |
443 | return -ENODEV; | 444 | return -ENODEV; |
444 | 445 | ||
445 | return prom_update_property(np, newprop, oldprop); | 446 | rc = prom_update_property(np, newprop, oldprop); |
447 | if (rc) | ||
448 | return rc; | ||
449 | |||
450 | /* For memory under the ibm,dynamic-reconfiguration-memory node | ||
451 | * of the device tree, adding and removing memory is just an update | ||
452 | * to the ibm,dynamic-memory property instead of adding/removing a | ||
453 | * memory node in the device tree. For these cases we still need to | ||
454 | * involve the notifier chain. | ||
455 | */ | ||
456 | if (!strcmp(name, "ibm,dynamic-memory")) { | ||
457 | int action; | ||
458 | |||
459 | next_prop = parse_next_property(next_prop, end, &name, | ||
460 | &length, &value); | ||
461 | if (!next_prop) | ||
462 | return -EINVAL; | ||
463 | |||
464 | if (!strcmp(name, "add")) | ||
465 | action = PSERIES_DRCONF_MEM_ADD; | ||
466 | else | ||
467 | action = PSERIES_DRCONF_MEM_REMOVE; | ||
468 | |||
469 | blocking_notifier_call_chain(&pSeries_reconfig_chain, | ||
470 | action, value); | ||
471 | } | ||
472 | |||
473 | return 0; | ||
446 | } | 474 | } |
447 | 475 | ||
448 | /** | 476 | /** |