diff options
Diffstat (limited to 'arch/sparc64/kernel/process.c')
-rw-r--r-- | arch/sparc64/kernel/process.c | 133 |
1 files changed, 57 insertions, 76 deletions
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 059b0d025224..1c7ca2f712d9 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c | |||
@@ -44,83 +44,61 @@ | |||
44 | #include <asm/fpumacro.h> | 44 | #include <asm/fpumacro.h> |
45 | #include <asm/head.h> | 45 | #include <asm/head.h> |
46 | #include <asm/cpudata.h> | 46 | #include <asm/cpudata.h> |
47 | #include <asm/mmu_context.h> | ||
47 | #include <asm/unistd.h> | 48 | #include <asm/unistd.h> |
49 | #include <asm/hypervisor.h> | ||
48 | 50 | ||
49 | /* #define VERBOSE_SHOWREGS */ | 51 | /* #define VERBOSE_SHOWREGS */ |
50 | 52 | ||
51 | /* | 53 | static void sparc64_yield(void) |
52 | * Nothing special yet... | ||
53 | */ | ||
54 | void default_idle(void) | ||
55 | { | ||
56 | } | ||
57 | |||
58 | #ifndef CONFIG_SMP | ||
59 | |||
60 | /* | ||
61 | * the idle loop on a Sparc... ;) | ||
62 | */ | ||
63 | void cpu_idle(void) | ||
64 | { | 54 | { |
65 | /* endless idle loop with no priority at all */ | 55 | if (tlb_type != hypervisor) |
66 | for (;;) { | 56 | return; |
67 | /* If current->work.need_resched is zero we should really | ||
68 | * setup for a system wakup event and execute a shutdown | ||
69 | * instruction. | ||
70 | * | ||
71 | * But this requires writing back the contents of the | ||
72 | * L2 cache etc. so implement this later. -DaveM | ||
73 | */ | ||
74 | while (!need_resched()) | ||
75 | barrier(); | ||
76 | 57 | ||
77 | preempt_enable_no_resched(); | 58 | clear_thread_flag(TIF_POLLING_NRFLAG); |
78 | schedule(); | 59 | smp_mb__after_clear_bit(); |
79 | preempt_disable(); | 60 | |
80 | check_pgt_cache(); | 61 | while (!need_resched()) { |
62 | unsigned long pstate; | ||
63 | |||
64 | /* Disable interrupts. */ | ||
65 | __asm__ __volatile__( | ||
66 | "rdpr %%pstate, %0\n\t" | ||
67 | "andn %0, %1, %0\n\t" | ||
68 | "wrpr %0, %%g0, %%pstate" | ||
69 | : "=&r" (pstate) | ||
70 | : "i" (PSTATE_IE)); | ||
71 | |||
72 | if (!need_resched()) | ||
73 | sun4v_cpu_yield(); | ||
74 | |||
75 | /* Re-enable interrupts. */ | ||
76 | __asm__ __volatile__( | ||
77 | "rdpr %%pstate, %0\n\t" | ||
78 | "or %0, %1, %0\n\t" | ||
79 | "wrpr %0, %%g0, %%pstate" | ||
80 | : "=&r" (pstate) | ||
81 | : "i" (PSTATE_IE)); | ||
81 | } | 82 | } |
82 | } | ||
83 | 83 | ||
84 | #else | 84 | set_thread_flag(TIF_POLLING_NRFLAG); |
85 | } | ||
85 | 86 | ||
86 | /* | 87 | /* The idle loop on sparc64. */ |
87 | * the idle loop on a UltraMultiPenguin... | ||
88 | * | ||
89 | * TIF_POLLING_NRFLAG is set because we do not sleep the cpu | ||
90 | * inside of the idler task, so an interrupt is not needed | ||
91 | * to get a clean fast response. | ||
92 | * | ||
93 | * XXX Reverify this assumption... -DaveM | ||
94 | * | ||
95 | * Addendum: We do want it to do something for the signal | ||
96 | * delivery case, we detect that by just seeing | ||
97 | * if we are trying to send this to an idler or not. | ||
98 | */ | ||
99 | void cpu_idle(void) | 88 | void cpu_idle(void) |
100 | { | 89 | { |
101 | cpuinfo_sparc *cpuinfo = &local_cpu_data(); | ||
102 | set_thread_flag(TIF_POLLING_NRFLAG); | 90 | set_thread_flag(TIF_POLLING_NRFLAG); |
103 | 91 | ||
104 | while(1) { | 92 | while(1) { |
105 | if (need_resched()) { | 93 | if (need_resched()) { |
106 | cpuinfo->idle_volume = 0; | ||
107 | preempt_enable_no_resched(); | 94 | preempt_enable_no_resched(); |
108 | schedule(); | 95 | schedule(); |
109 | preempt_disable(); | 96 | preempt_disable(); |
110 | check_pgt_cache(); | ||
111 | } | 97 | } |
112 | cpuinfo->idle_volume++; | 98 | sparc64_yield(); |
113 | |||
114 | /* The store ordering is so that IRQ handlers on | ||
115 | * other cpus see our increasing idleness for the buddy | ||
116 | * redistribution algorithm. -DaveM | ||
117 | */ | ||
118 | membar_storeload_storestore(); | ||
119 | } | 99 | } |
120 | } | 100 | } |
121 | 101 | ||
122 | #endif | ||
123 | |||
124 | extern char reboot_command []; | 102 | extern char reboot_command []; |
125 | 103 | ||
126 | extern void (*prom_palette)(int); | 104 | extern void (*prom_palette)(int); |
@@ -354,6 +332,7 @@ void show_regs(struct pt_regs *regs) | |||
354 | extern long etrap, etraptl1; | 332 | extern long etrap, etraptl1; |
355 | #endif | 333 | #endif |
356 | __show_regs(regs); | 334 | __show_regs(regs); |
335 | #if 0 | ||
357 | #ifdef CONFIG_SMP | 336 | #ifdef CONFIG_SMP |
358 | { | 337 | { |
359 | extern void smp_report_regs(void); | 338 | extern void smp_report_regs(void); |
@@ -361,6 +340,7 @@ void show_regs(struct pt_regs *regs) | |||
361 | smp_report_regs(); | 340 | smp_report_regs(); |
362 | } | 341 | } |
363 | #endif | 342 | #endif |
343 | #endif | ||
364 | 344 | ||
365 | #ifdef VERBOSE_SHOWREGS | 345 | #ifdef VERBOSE_SHOWREGS |
366 | if (regs->tpc >= &etrap && regs->tpc < &etraptl1 && | 346 | if (regs->tpc >= &etrap && regs->tpc < &etraptl1 && |
@@ -433,30 +413,15 @@ void exit_thread(void) | |||
433 | void flush_thread(void) | 413 | void flush_thread(void) |
434 | { | 414 | { |
435 | struct thread_info *t = current_thread_info(); | 415 | struct thread_info *t = current_thread_info(); |
416 | struct mm_struct *mm; | ||
436 | 417 | ||
437 | if (t->flags & _TIF_ABI_PENDING) | 418 | if (t->flags & _TIF_ABI_PENDING) |
438 | t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); | 419 | t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT); |
439 | 420 | ||
440 | if (t->task->mm) { | 421 | mm = t->task->mm; |
441 | unsigned long pgd_cache = 0UL; | 422 | if (mm) |
442 | if (test_thread_flag(TIF_32BIT)) { | 423 | tsb_context_switch(mm); |
443 | struct mm_struct *mm = t->task->mm; | ||
444 | pgd_t *pgd0 = &mm->pgd[0]; | ||
445 | pud_t *pud0 = pud_offset(pgd0, 0); | ||
446 | 424 | ||
447 | if (pud_none(*pud0)) { | ||
448 | pmd_t *page = pmd_alloc_one(mm, 0); | ||
449 | pud_set(pud0, page); | ||
450 | } | ||
451 | pgd_cache = get_pgd_cache(pgd0); | ||
452 | } | ||
453 | __asm__ __volatile__("stxa %0, [%1] %2\n\t" | ||
454 | "membar #Sync" | ||
455 | : /* no outputs */ | ||
456 | : "r" (pgd_cache), | ||
457 | "r" (TSB_REG), | ||
458 | "i" (ASI_DMMU)); | ||
459 | } | ||
460 | set_thread_wsaved(0); | 425 | set_thread_wsaved(0); |
461 | 426 | ||
462 | /* Turn off performance counters if on. */ | 427 | /* Turn off performance counters if on. */ |
@@ -555,6 +520,18 @@ void synchronize_user_stack(void) | |||
555 | } | 520 | } |
556 | } | 521 | } |
557 | 522 | ||
523 | static void stack_unaligned(unsigned long sp) | ||
524 | { | ||
525 | siginfo_t info; | ||
526 | |||
527 | info.si_signo = SIGBUS; | ||
528 | info.si_errno = 0; | ||
529 | info.si_code = BUS_ADRALN; | ||
530 | info.si_addr = (void __user *) sp; | ||
531 | info.si_trapno = 0; | ||
532 | force_sig_info(SIGBUS, &info, current); | ||
533 | } | ||
534 | |||
558 | void fault_in_user_windows(void) | 535 | void fault_in_user_windows(void) |
559 | { | 536 | { |
560 | struct thread_info *t = current_thread_info(); | 537 | struct thread_info *t = current_thread_info(); |
@@ -570,13 +547,17 @@ void fault_in_user_windows(void) | |||
570 | flush_user_windows(); | 547 | flush_user_windows(); |
571 | window = get_thread_wsaved(); | 548 | window = get_thread_wsaved(); |
572 | 549 | ||
573 | if (window != 0) { | 550 | if (likely(window != 0)) { |
574 | window -= 1; | 551 | window -= 1; |
575 | do { | 552 | do { |
576 | unsigned long sp = (t->rwbuf_stkptrs[window] + bias); | 553 | unsigned long sp = (t->rwbuf_stkptrs[window] + bias); |
577 | struct reg_window *rwin = &t->reg_window[window]; | 554 | struct reg_window *rwin = &t->reg_window[window]; |
578 | 555 | ||
579 | if (copy_to_user((char __user *)sp, rwin, winsize)) | 556 | if (unlikely(sp & 0x7UL)) |
557 | stack_unaligned(sp); | ||
558 | |||
559 | if (unlikely(copy_to_user((char __user *)sp, | ||
560 | rwin, winsize))) | ||
580 | goto barf; | 561 | goto barf; |
581 | } while (window--); | 562 | } while (window--); |
582 | } | 563 | } |