#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.h>
-#include <linux/config.h>
#include <linux/reboot.h>
#include <linux/delay.h>
+#include <linux/compat.h>
#include <linux/init.h>
#include <asm/oplib.h>
#include <asm/fpumacro.h>
#include <asm/head.h>
#include <asm/cpudata.h>
+#include <asm/mmu_context.h>
#include <asm/unistd.h>
+#include <asm/hypervisor.h>
/* #define VERBOSE_SHOWREGS */
-/*
- * Nothing special yet...
- */
-void default_idle(void)
+static void sparc64_yield(void)
{
-}
-
-#ifndef CONFIG_SMP
-
-/*
- * the idle loop on a Sparc... ;)
- */
-int cpu_idle(void)
-{
- if (current->pid != 0)
- return -EPERM;
-
- /* endless idle loop with no priority at all */
- for (;;) {
- /* If current->work.need_resched is zero we should really
- * setup for a system wakup event and execute a shutdown
- * instruction.
- *
- * But this requires writing back the contents of the
- * L2 cache etc. so implement this later. -DaveM
- */
- while (!need_resched())
- barrier();
+ if (tlb_type != hypervisor)
+ return;
- schedule();
- check_pgt_cache();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb__after_clear_bit();
+
+ while (!need_resched()) {
+ unsigned long pstate;
+
+ /* Disable interrupts. */
+ __asm__ __volatile__(
+ "rdpr %%pstate, %0\n\t"
+ "andn %0, %1, %0\n\t"
+ "wrpr %0, %%g0, %%pstate"
+ : "=&r" (pstate)
+ : "i" (PSTATE_IE));
+
+ if (!need_resched())
+ sun4v_cpu_yield();
+
+ /* Re-enable interrupts. */
+ __asm__ __volatile__(
+ "rdpr %%pstate, %0\n\t"
+ "or %0, %1, %0\n\t"
+ "wrpr %0, %%g0, %%pstate"
+ : "=&r" (pstate)
+ : "i" (PSTATE_IE));
}
- return 0;
-}
-#else
+ set_thread_flag(TIF_POLLING_NRFLAG);
+}
-/*
- * the idle loop on a UltraMultiPenguin...
- */
-#define idle_me_harder() (cpu_data(smp_processor_id()).idle_volume += 1)
-#define unidle_me() (cpu_data(smp_processor_id()).idle_volume = 0)
-int cpu_idle(void)
+/* The idle loop on sparc64. */
+void cpu_idle(void)
{
set_thread_flag(TIF_POLLING_NRFLAG);
+
while(1) {
if (need_resched()) {
- unidle_me();
- clear_thread_flag(TIF_POLLING_NRFLAG);
+ preempt_enable_no_resched();
schedule();
- set_thread_flag(TIF_POLLING_NRFLAG);
- check_pgt_cache();
+ preempt_disable();
}
- idle_me_harder();
-
- /* The store ordering is so that IRQ handlers on
- * other cpus see our increasing idleness for the buddy
- * redistribution algorithm. -DaveM
- */
- membar("#StoreStore | #StoreLoad");
+ sparc64_yield();
}
}
-#endif
-
extern char reboot_command [];
extern void (*prom_palette)(int);
panic("Halt failed!");
}
-EXPORT_SYMBOL(machine_halt);
-
void machine_alt_power_off(void)
{
if (!serial_console && prom_palette)
panic("Reboot failed!");
}
-EXPORT_SYMBOL(machine_restart);
-
+#ifdef CONFIG_COMPAT
static void show_regwindow32(struct pt_regs *regs)
{
- struct reg_window32 *rw;
+ struct reg_window32 __user *rw;
struct reg_window32 r_w;
mm_segment_t old_fs;
__asm__ __volatile__ ("flushw");
- rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]);
+ rw = compat_ptr((unsigned)regs->u_regs[14]);
old_fs = get_fs();
set_fs (USER_DS);
if (copy_from_user (&r_w, rw, sizeof(r_w))) {
set_fs (old_fs);
return;
}
- rw = &r_w;
+
set_fs (old_fs);
printk("l0: %08x l1: %08x l2: %08x l3: %08x "
"l4: %08x l5: %08x l6: %08x l7: %08x\n",
- rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
- rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
+ r_w.locals[0], r_w.locals[1], r_w.locals[2], r_w.locals[3],
+ r_w.locals[4], r_w.locals[5], r_w.locals[6], r_w.locals[7]);
printk("i0: %08x i1: %08x i2: %08x i3: %08x "
"i4: %08x i5: %08x i6: %08x i7: %08x\n",
- rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
- rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
+ r_w.ins[0], r_w.ins[1], r_w.ins[2], r_w.ins[3],
+ r_w.ins[4], r_w.ins[5], r_w.ins[6], r_w.ins[7]);
}
+#else
+#define show_regwindow32(regs) do { } while (0)
+#endif
static void show_regwindow(struct pt_regs *regs)
{
- struct reg_window *rw;
+ struct reg_window __user *rw;
+ struct reg_window *rwk;
struct reg_window r_w;
mm_segment_t old_fs;
if ((regs->tstate & TSTATE_PRIV) || !(test_thread_flag(TIF_32BIT))) {
__asm__ __volatile__ ("flushw");
- rw = (struct reg_window *)(regs->u_regs[14] + STACK_BIAS);
+ rw = (struct reg_window __user *)
+ (regs->u_regs[14] + STACK_BIAS);
+ rwk = (struct reg_window *)
+ (regs->u_regs[14] + STACK_BIAS);
if (!(regs->tstate & TSTATE_PRIV)) {
old_fs = get_fs();
set_fs (USER_DS);
set_fs (old_fs);
return;
}
- rw = &r_w;
+ rwk = &r_w;
set_fs (old_fs);
}
} else {
return;
}
printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n",
- rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3]);
+ rwk->locals[0], rwk->locals[1], rwk->locals[2], rwk->locals[3]);
printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n",
- rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
+ rwk->locals[4], rwk->locals[5], rwk->locals[6], rwk->locals[7]);
printk("i0: %016lx i1: %016lx i2: %016lx i3: %016lx\n",
- rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3]);
+ rwk->ins[0], rwk->ins[1], rwk->ins[2], rwk->ins[3]);
printk("i4: %016lx i5: %016lx i6: %016lx i7: %016lx\n",
- rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
+ rwk->ins[4], rwk->ins[5], rwk->ins[6], rwk->ins[7]);
if (regs->tstate & TSTATE_PRIV)
- print_symbol("I7: <%s>\n", rw->ins[7]);
+ print_symbol("I7: <%s>\n", rwk->ins[7]);
}
void show_stackframe(struct sparc_stackf *sf)
}
#ifdef CONFIG_SMP
-static spinlock_t regdump_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(regdump_lock);
#endif
void __show_regs(struct pt_regs * regs)
extern long etrap, etraptl1;
#endif
__show_regs(regs);
+#if 0
#ifdef CONFIG_SMP
{
extern void smp_report_regs(void);
smp_report_regs();
}
#endif
+#endif
#ifdef VERBOSE_SHOWREGS
if (regs->tpc >= &etrap && regs->tpc < &etraptl1 &&
unsigned long thread_saved_pc(struct task_struct *tsk)
{
- struct thread_info *ti = tsk->thread_info;
+ struct thread_info *ti = task_thread_info(tsk);
unsigned long ret = 0xdeadbeefUL;
if (ti && ti->ksp) {
void flush_thread(void)
{
struct thread_info *t = current_thread_info();
+ struct mm_struct *mm;
if (t->flags & _TIF_ABI_PENDING)
t->flags ^= (_TIF_ABI_PENDING | _TIF_32BIT);
- if (t->task->mm) {
- unsigned long pgd_cache = 0UL;
- if (test_thread_flag(TIF_32BIT)) {
- struct mm_struct *mm = t->task->mm;
- pgd_t *pgd0 = &mm->pgd[0];
-
- if (pgd_none(*pgd0)) {
- pmd_t *page = pmd_alloc_one_fast(NULL, 0);
- if (!page)
- page = pmd_alloc_one(NULL, 0);
- pgd_set(pgd0, page);
- }
- pgd_cache = ((unsigned long) pgd_val(*pgd0)) << 11UL;
- }
- __asm__ __volatile__("stxa %0, [%1] %2\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (pgd_cache),
- "r" (TSB_REG),
- "i" (ASI_DMMU));
- }
+ mm = t->task->mm;
+ if (mm)
+ tsb_context_switch(mm);
+
set_thread_wsaved(0);
/* Turn off performance counters if on. */
if (!(test_thread_flag(TIF_32BIT))) {
csp += STACK_BIAS;
psp += STACK_BIAS;
- __get_user(fp, &(((struct reg_window *)psp)->ins[6]));
+ __get_user(fp, &(((struct reg_window __user *)psp)->ins[6]));
fp += STACK_BIAS;
} else
- __get_user(fp, &(((struct reg_window32 *)psp)->ins[6]));
+ __get_user(fp, &(((struct reg_window32 __user *)psp)->ins[6]));
/* Now 8-byte align the stack as this is mandatory in the
* Sparc ABI due to how register windows work. This hides
if (copy_in_user((void __user *) rval, (void __user *) psp, distance))
rval = 0;
else if (test_thread_flag(TIF_32BIT)) {
- if (put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6])))
+ if (put_user(((u32)csp),
+ &(((struct reg_window32 __user *)rval)->ins[6])))
rval = 0;
} else {
if (put_user(((u64)csp - STACK_BIAS),
- &(((struct reg_window *)rval)->ins[6])))
+ &(((struct reg_window __user *)rval)->ins[6])))
rval = 0;
else
rval = rval - STACK_BIAS;
unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
- if (!copy_to_user((char *)sp, rwin, winsize)) {
+ if (!copy_to_user((char __user *)sp, rwin, winsize)) {
shift_window_buffer(window, get_thread_wsaved() - 1, t);
set_thread_wsaved(get_thread_wsaved() - 1);
}
}
}
+static void stack_unaligned(unsigned long sp)
+{
+ siginfo_t info;
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void __user *) sp;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
+}
+
void fault_in_user_windows(void)
{
struct thread_info *t = current_thread_info();
flush_user_windows();
window = get_thread_wsaved();
- if (window != 0) {
+ if (likely(window != 0)) {
window -= 1;
do {
unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
- if (copy_to_user((char *)sp, rwin, winsize))
+ if (unlikely(sp & 0x7UL))
+ stack_unaligned(sp);
+
+ if (unlikely(copy_to_user((char __user *)sp,
+ rwin, winsize)))
goto barf;
} while (window--);
}
do_exit(SIGILL);
}
-asmlinkage int sparc_do_fork(unsigned long clone_flags,
- unsigned long stack_start,
- struct pt_regs *regs,
- unsigned long stack_size)
+asmlinkage long sparc_do_fork(unsigned long clone_flags,
+ unsigned long stack_start,
+ struct pt_regs *regs,
+ unsigned long stack_size)
{
- unsigned long parent_tid_ptr, child_tid_ptr;
-
- clone_flags &= ~CLONE_IDLETASK;
+ int __user *parent_tid_ptr, *child_tid_ptr;
- parent_tid_ptr = regs->u_regs[UREG_I2];
- child_tid_ptr = regs->u_regs[UREG_I4];
+#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) {
- parent_tid_ptr &= 0xffffffff;
- child_tid_ptr &= 0xffffffff;
+ parent_tid_ptr = compat_ptr(regs->u_regs[UREG_I2]);
+ child_tid_ptr = compat_ptr(regs->u_regs[UREG_I4]);
+ } else
+#endif
+ {
+ parent_tid_ptr = (int __user *) regs->u_regs[UREG_I2];
+ child_tid_ptr = (int __user *) regs->u_regs[UREG_I4];
}
return do_fork(clone_flags, stack_start,
regs, stack_size,
- (int *) parent_tid_ptr,
- (int *) child_tid_ptr);
+ parent_tid_ptr, child_tid_ptr);
}
/* Copy a Sparc thread. The fork() return value conventions
unsigned long unused,
struct task_struct *p, struct pt_regs *regs)
{
- struct thread_info *t = p->thread_info;
+ struct thread_info *t = task_thread_info(p);
char *child_trap_frame;
-#ifdef CONFIG_DEBUG_SPINLOCK
- p->thread.smp_lock_count = 0;
- p->thread.smp_lock_pc = 0;
-#endif
-
- p->set_child_tid = p->clear_child_tid = NULL;
-
/* Calculate offset to stack_frame & pt_regs */
- child_trap_frame = ((char *)t) + (THREAD_SIZE - (TRACEREG_SZ+STACKFRAME_SZ));
+ child_trap_frame = task_stack_page(p) + (THREAD_SIZE - (TRACEREG_SZ+STACKFRAME_SZ));
memcpy(child_trap_frame, (((struct sparc_stackf *)regs)-1), (TRACEREG_SZ+STACKFRAME_SZ));
t->flags = (t->flags & ~((0xffUL << TI_FLAG_CWP_SHIFT) | (0xffUL << TI_FLAG_CURRENT_DS_SHIFT))) |
- _TIF_NEWCHILD |
(((regs->tstate + 1) & TSTATE_CWP) << TI_FLAG_CWP_SHIFT);
+ t->new_child = 1;
t->ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
t->kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct sparc_stackf));
t->fpsaved[0] = 0;
* So we stash 'fn' and 'arg' into global registers which
* will not be modified by the parent.
*/
- __asm__ __volatile("mov %4, %%g2\n\t" /* Save FN into global */
- "mov %5, %%g3\n\t" /* Save ARG into global */
- "mov %1, %%g1\n\t" /* Clone syscall nr. */
- "mov %2, %%o0\n\t" /* Clone flags. */
- "mov 0, %%o1\n\t" /* usp arg == 0 */
- "t 0x6d\n\t" /* Linux/Sparc clone(). */
- "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
- " mov %%o0, %0\n\t"
- "jmpl %%g2, %%o7\n\t" /* Call the function. */
- " mov %%g3, %%o0\n\t" /* Set arg in delay. */
- "mov %3, %%g1\n\t"
- "t 0x6d\n\t" /* Linux/Sparc exit(). */
- /* Notreached by child. */
- "1:" :
- "=r" (retval) :
- "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
- "i" (__NR_exit), "r" (fn), "r" (arg) :
- "g1", "g2", "g3", "o0", "o1", "memory", "cc");
+ __asm__ __volatile__("mov %4, %%g2\n\t" /* Save FN into global */
+ "mov %5, %%g3\n\t" /* Save ARG into global */
+ "mov %1, %%g1\n\t" /* Clone syscall nr. */
+ "mov %2, %%o0\n\t" /* Clone flags. */
+ "mov 0, %%o1\n\t" /* usp arg == 0 */
+ "t 0x6d\n\t" /* Linux/Sparc clone(). */
+ "brz,a,pn %%o1, 1f\n\t" /* Parent, just return. */
+ " mov %%o0, %0\n\t"
+ "jmpl %%g2, %%o7\n\t" /* Call the function. */
+ " mov %%g3, %%o0\n\t" /* Set arg in delay. */
+ "mov %3, %%g1\n\t"
+ "t 0x6d\n\t" /* Linux/Sparc exit(). */
+ /* Notreached by child. */
+ "1:" :
+ "=r" (retval) :
+ "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
+ "i" (__NR_exit), "r" (fn), "r" (arg) :
+ "g1", "g2", "g3", "o0", "o1", "memory", "cc");
return retval;
}
if (regs->u_regs[UREG_G1] == 0)
base = 1;
- filename = getname((char *)regs->u_regs[base + UREG_I0]);
+ filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
- error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1],
- (char **) regs->u_regs[base + UREG_I2], regs);
+ error = do_execve(filename,
+ (char __user * __user *)
+ regs->u_regs[base + UREG_I1],
+ (char __user * __user *)
+ regs->u_regs[base + UREG_I2], regs);
putname(filename);
if (!error) {
fprs_write(0);
current_thread_info()->xfsr[0] = 0;
current_thread_info()->fpsaved[0] = 0;
regs->tstate &= ~TSTATE_PEF;
+ task_lock(current);
current->ptrace &= ~PT_DTRACE;
+ task_unlock(current);
}
out:
return error;
task->state == TASK_RUNNING)
goto out;
- thread_info_base = (unsigned long) task->thread_info;
+ thread_info_base = (unsigned long) task_stack_page(task);
bias = STACK_BIAS;
- fp = task->thread_info->ksp + bias;
+ fp = task_thread_info(task)->ksp + bias;
do {
/* Bogus frame pointer? */
break;
rw = (struct reg_window *) fp;
pc = rw->ins[7];
- if (pc < ((unsigned long) scheduling_functions_start_here) ||
- pc >= ((unsigned long) scheduling_functions_end_here)) {
+ if (!in_sched_functions(pc)) {
ret = pc;
goto out;
}