process_32.c 11.7 KB
Newer Older
P
Paul Mundt 已提交
1 2
/*
 * arch/sh/kernel/process.c
L
Linus Torvalds 已提交
3
 *
P
Paul Mundt 已提交
4
 * This file handles the architecture-dependent parts of process handling..
L
Linus Torvalds 已提交
5 6 7 8
 *
 *  Copyright (C) 1995  Linus Torvalds
 *
 *  SuperH version:  Copyright (C) 1999, 2000  Niibe Yutaka & Kaz Kojima
R
Ryusuke Sakato 已提交
9
 *		     Copyright (C) 2006 Lineo Solutions Inc. support SH4A UBC
P
Paul Mundt 已提交
10
 *		     Copyright (C) 2002 - 2007  Paul Mundt
L
Linus Torvalds 已提交
11 12 13 14
 */
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/elfcore.h>
15
#include <linux/pm.h>
L
Linus Torvalds 已提交
16
#include <linux/kallsyms.h>
17
#include <linux/kexec.h>
P
Paul Mundt 已提交
18
#include <linux/kdebug.h>
19
#include <linux/tick.h>
20
#include <linux/reboot.h>
21
#include <linux/fs.h>
22
#include <linux/preempt.h>
L
Linus Torvalds 已提交
23 24
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
P
Paul Mundt 已提交
25
#include <asm/pgalloc.h>
26
#include <asm/system.h>
P
Paul Mundt 已提交
27
#include <asm/ubc.h>
L
Linus Torvalds 已提交
28

P
Paul Mundt 已提交
29
static int hlt_counter;
L
Linus Torvalds 已提交
30 31
int ubc_usercnt = 0;

32 33 34 35
void (*pm_idle)(void);
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);

L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44 45 46 47
void disable_hlt(void)
{
	hlt_counter++;
}
EXPORT_SYMBOL(disable_hlt);

void enable_hlt(void)
{
	hlt_counter--;
}
EXPORT_SYMBOL(enable_hlt);

48 49 50 51 52 53 54 55 56 57 58 59 60 61
static int __init nohlt_setup(char *__unused)
{
	hlt_counter = 1;
	return 1;
}
__setup("nohlt", nohlt_setup);

static int __init hlt_setup(char *__unused)
{
	hlt_counter = 0;
	return 1;
}
__setup("hlt", hlt_setup);

62 63
void default_idle(void)
{
64 65 66 67 68 69 70 71 72 73 74
	if (!hlt_counter) {
		clear_thread_flag(TIF_POLLING_NRFLAG);
		smp_mb__after_clear_bit();
		set_bl_bit();
		while (!need_resched())
			cpu_sleep();
		clear_bl_bit();
		set_thread_flag(TIF_POLLING_NRFLAG);
	} else
		while (!need_resched())
			cpu_relax();
75 76
}

77
void cpu_idle(void)
L
Linus Torvalds 已提交
78
{
79 80
	set_thread_flag(TIF_POLLING_NRFLAG);

L
Linus Torvalds 已提交
81 82
	/* endless idle loop with no priority at all */
	while (1) {
83 84 85 86 87
		void (*idle)(void) = pm_idle;

		if (!idle)
			idle = default_idle;

88
		tick_nohz_stop_sched_tick();
89 90
		while (!need_resched())
			idle();
91
		tick_nohz_restart_sched_tick();
L
Linus Torvalds 已提交
92

93
		preempt_enable_no_resched();
L
Linus Torvalds 已提交
94
		schedule();
95
		preempt_disable();
P
Paul Mundt 已提交
96
		check_pgt_cache();
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108
	}
}

void machine_restart(char * __unused)
{
	/* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
	asm volatile("ldc %0, sr\n\t"
		     "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
}

void machine_halt(void)
{
109
	local_irq_disable();
L
Linus Torvalds 已提交
110 111 112 113 114 115 116

	while (1)
		cpu_sleep();
}

void machine_power_off(void)
{
117 118
	if (pm_power_off)
		pm_power_off();
L
Linus Torvalds 已提交
119 120 121 122 123
}

void show_regs(struct pt_regs * regs)
{
	printk("\n");
124
	printk("Pid : %d, Comm: %20s\n", task_pid_nr(current), current->comm);
125
	print_symbol("PC is at %s\n", instruction_pointer(regs));
L
Linus Torvalds 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	printk("PC  : %08lx SP  : %08lx SR  : %08lx ",
	       regs->pc, regs->regs[15], regs->sr);
#ifdef CONFIG_MMU
	printk("TEA : %08x    ", ctrl_inl(MMU_TEA));
#else
	printk("                  ");
#endif
	printk("%s\n", print_tainted());

	printk("R0  : %08lx R1  : %08lx R2  : %08lx R3  : %08lx\n",
	       regs->regs[0],regs->regs[1],
	       regs->regs[2],regs->regs[3]);
	printk("R4  : %08lx R5  : %08lx R6  : %08lx R7  : %08lx\n",
	       regs->regs[4],regs->regs[5],
	       regs->regs[6],regs->regs[7]);
	printk("R8  : %08lx R9  : %08lx R10 : %08lx R11 : %08lx\n",
	       regs->regs[8],regs->regs[9],
	       regs->regs[10],regs->regs[11]);
	printk("R12 : %08lx R13 : %08lx R14 : %08lx\n",
	       regs->regs[12],regs->regs[13],
	       regs->regs[14]);
	printk("MACH: %08lx MACL: %08lx GBR : %08lx PR  : %08lx\n",
	       regs->mach, regs->macl, regs->gbr, regs->pr);

150
	show_trace(NULL, (unsigned long *)regs->regs[15], regs);
L
Linus Torvalds 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
}

/*
 * Create a kernel thread
 */

/*
 * This is the mechanism for creating a new kernel thread.
 *
 */
extern void kernel_thread_helper(void);
__asm__(".align 5\n"
	"kernel_thread_helper:\n\t"
	"jsr	@r5\n\t"
	" nop\n\t"
	"mov.l	1f, r1\n\t"
	"jsr	@r1\n\t"
	" mov	r0, r4\n\t"
	".align 2\n\t"
	"1:.long do_exit");

P
Paul Mundt 已提交
172
/* Don't use this in BL=1(cli).  Or else, CPU resets! */
L
Linus Torvalds 已提交
173
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
P
Paul Mundt 已提交
174
{
L
Linus Torvalds 已提交
175 176 177
	struct pt_regs regs;

	memset(&regs, 0, sizeof(regs));
P
Paul Mundt 已提交
178 179
	regs.regs[4] = (unsigned long)arg;
	regs.regs[5] = (unsigned long)fn;
L
Linus Torvalds 已提交
180

P
Paul Mundt 已提交
181
	regs.pc = (unsigned long)kernel_thread_helper;
L
Linus Torvalds 已提交
182 183 184
	regs.sr = (1 << 30);

	/* Ok, create the new process.. */
P
Paul Mundt 已提交
185 186
	return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
		       &regs, 0, NULL, NULL);
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
}

/*
 * Free current thread data structures etc..
 */
void exit_thread(void)
{
	if (current->thread.ubc_pc) {
		current->thread.ubc_pc = 0;
		ubc_usercnt -= 1;
	}
}

void flush_thread(void)
{
#if defined(CONFIG_SH_FPU)
	struct task_struct *tsk = current;
	/* Forget lazy FPU state */
A
Al Viro 已提交
205
	clear_fpu(tsk, task_pt_regs(tsk));
L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
	clear_used_math();
#endif
}

void release_thread(struct task_struct *dead_task)
{
	/* do nothing */
}

/* Fill in the fpu structure for a core dump.. */
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
{
	int fpvalid = 0;

#if defined(CONFIG_SH_FPU)
	struct task_struct *tsk = current;

	fpvalid = !!tsk_used_math(tsk);
	if (fpvalid) {
		unlazy_fpu(tsk, regs);
		memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu));
	}
#endif

	return fpvalid;
}

asmlinkage void ret_from_fork(void);

int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
		unsigned long unused,
		struct task_struct *p, struct pt_regs *regs)
{
239
	struct thread_info *ti = task_thread_info(p);
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247 248
	struct pt_regs *childregs;
#if defined(CONFIG_SH_FPU)
	struct task_struct *tsk = current;

	unlazy_fpu(tsk, regs);
	p->thread.fpu = tsk->thread.fpu;
	copy_to_stopped_child_used_math(p);
#endif

A
Al Viro 已提交
249
	childregs = task_pt_regs(p);
L
Linus Torvalds 已提交
250 251 252 253
	*childregs = *regs;

	if (user_mode(regs)) {
		childregs->regs[15] = usp;
254
		ti->addr_limit = USER_DS;
L
Linus Torvalds 已提交
255
	} else {
256
		childregs->regs[15] = (unsigned long)childregs;
257
		ti->addr_limit = KERNEL_DS;
L
Linus Torvalds 已提交
258
	}
P
Paul Mundt 已提交
259

260
	if (clone_flags & CLONE_SETTLS)
L
Linus Torvalds 已提交
261
		childregs->gbr = childregs->regs[0];
P
Paul Mundt 已提交
262

L
Linus Torvalds 已提交
263 264 265 266 267 268 269 270 271 272 273
	childregs->regs[0] = 0; /* Set return value for child */

	p->thread.sp = (unsigned long) childregs;
	p->thread.pc = (unsigned long) ret_from_fork;

	p->thread.ubc_pc = 0;

	return 0;
}

/* Tracing by user break controller.  */
P
Paul Mundt 已提交
274
static void ubc_set_tracing(int asid, unsigned long pc)
L
Linus Torvalds 已提交
275
{
R
Ryusuke Sakato 已提交
276 277 278 279 280 281 282 283 284 285 286 287 288 289
#if defined(CONFIG_CPU_SH4A)
	unsigned long val;

	val = (UBC_CBR_ID_INST | UBC_CBR_RW_READ | UBC_CBR_CE);
	val |= (UBC_CBR_AIE | UBC_CBR_AIV_SET(asid));

	ctrl_outl(val, UBC_CBR0);
	ctrl_outl(pc,  UBC_CAR0);
	ctrl_outl(0x0, UBC_CAMR0);
	ctrl_outl(0x0, UBC_CBCR);

	val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE);
	ctrl_outl(val, UBC_CRR0);

P
Paul Mundt 已提交
290
	/* Read UBC register that we wrote last, for checking update */
R
Ryusuke Sakato 已提交
291 292 293
	val = ctrl_inl(UBC_CRR0);

#else	/* CONFIG_CPU_SH4A */
L
Linus Torvalds 已提交
294 295
	ctrl_outl(pc, UBC_BARA);

296
#ifdef CONFIG_MMU
297
	ctrl_outb(asid, UBC_BASRA);
298
#endif
L
Linus Torvalds 已提交
299 300 301

	ctrl_outl(0, UBC_BAMRA);

302
	if (current_cpu_data.type == CPU_SH7729 ||
P
Paul Mundt 已提交
303 304
	    current_cpu_data.type == CPU_SH7710 ||
	    current_cpu_data.type == CPU_SH7712) {
L
Linus Torvalds 已提交
305 306 307 308 309 310
		ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA);
		ctrl_outl(BRCR_PCBA | BRCR_PCTE, UBC_BRCR);
	} else {
		ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
		ctrl_outw(BRCR_PCBA, UBC_BRCR);
	}
R
Ryusuke Sakato 已提交
311
#endif	/* CONFIG_CPU_SH4A */
L
Linus Torvalds 已提交
312 313 314 315 316 317
}

/*
 *	switch_to(x,y) should switch tasks from x to y.
 *
 */
P
Paul Mundt 已提交
318 319
struct task_struct *__switch_to(struct task_struct *prev,
				struct task_struct *next)
L
Linus Torvalds 已提交
320 321
{
#if defined(CONFIG_SH_FPU)
A
Al Viro 已提交
322
	unlazy_fpu(prev, task_pt_regs(prev));
L
Linus Torvalds 已提交
323 324
#endif

P
Paul Mundt 已提交
325
#if defined(CONFIG_GUSA) && defined(CONFIG_PREEMPT)
L
Linus Torvalds 已提交
326 327 328
	{
		struct pt_regs *regs;

329
		preempt_disable();
A
Al Viro 已提交
330
		regs = task_pt_regs(prev);
L
Linus Torvalds 已提交
331 332 333 334 335 336 337 338 339
		if (user_mode(regs) && regs->regs[15] >= 0xc0000000) {
			int offset = (int)regs->regs[15];

			/* Reset stack pointer: clear critical region mark */
			regs->regs[15] = regs->regs[1];
			if (regs->pc < regs->regs[0])
				/* Go to rewind point */
				regs->pc = regs->regs[0] + offset;
		}
340
		preempt_enable_no_resched();
L
Linus Torvalds 已提交
341 342 343
	}
#endif

344
#ifdef CONFIG_MMU
L
Linus Torvalds 已提交
345 346
	/*
	 * Restore the kernel mode register
P
Paul Mundt 已提交
347
	 *	k7 (r7_bank1)
L
Linus Torvalds 已提交
348 349 350
	 */
	asm volatile("ldc	%0, r7_bank"
		     : /* no output */
A
Al Viro 已提交
351
		     : "r" (task_thread_info(next)));
352
#endif
L
Linus Torvalds 已提交
353 354 355 356 357

	/* If no tasks are using the UBC, we're done */
	if (ubc_usercnt == 0)
		/* If no tasks are using the UBC, we're done */;
	else if (next->thread.ubc_pc && next->mm) {
358 359
		int asid = 0;
#ifdef CONFIG_MMU
P
Paul Mundt 已提交
360
		asid |= cpu_asid(smp_processor_id(), next->mm);
361 362
#endif
		ubc_set_tracing(asid, next->thread.ubc_pc);
L
Linus Torvalds 已提交
363
	} else {
R
Ryusuke Sakato 已提交
364 365 366 367
#if defined(CONFIG_CPU_SH4A)
		ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
		ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
L
Linus Torvalds 已提交
368 369
		ctrl_outw(0, UBC_BBRA);
		ctrl_outw(0, UBC_BBRB);
R
Ryusuke Sakato 已提交
370
#endif
L
Linus Torvalds 已提交
371 372 373 374 375 376 377
	}

	return prev;
}

asmlinkage int sys_fork(unsigned long r4, unsigned long r5,
			unsigned long r6, unsigned long r7,
S
Stuart Menefy 已提交
378
			struct pt_regs __regs)
L
Linus Torvalds 已提交
379 380
{
#ifdef CONFIG_MMU
381
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
S
Stuart Menefy 已提交
382
	return do_fork(SIGCHLD, regs->regs[15], regs, 0, NULL, NULL);
L
Linus Torvalds 已提交
383 384 385 386 387 388 389 390 391
#else
	/* fork almost works, enough to trick you into looking elsewhere :-( */
	return -EINVAL;
#endif
}

asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
			 unsigned long parent_tidptr,
			 unsigned long child_tidptr,
S
Stuart Menefy 已提交
392
			 struct pt_regs __regs)
L
Linus Torvalds 已提交
393
{
S
Stuart Menefy 已提交
394
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
L
Linus Torvalds 已提交
395
	if (!newsp)
S
Stuart Menefy 已提交
396 397
		newsp = regs->regs[15];
	return do_fork(clone_flags, newsp, regs, 0,
P
Paul Mundt 已提交
398 399
			(int __user *)parent_tidptr,
			(int __user *)child_tidptr);
L
Linus Torvalds 已提交
400 401 402 403 404 405 406 407 408 409 410 411 412 413
}

/*
 * This is trivial, and on the face of it looks like it
 * could equally well be done in user mode.
 *
 * Not so, for quite unobvious reasons - register pressure.
 * In user mode vfork() cannot have a stack frame, and if
 * done by calling the "clone()" system call directly, you
 * do not have enough call-clobbered registers to hold all
 * the information you need.
 */
asmlinkage int sys_vfork(unsigned long r4, unsigned long r5,
			 unsigned long r6, unsigned long r7,
S
Stuart Menefy 已提交
414
			 struct pt_regs __regs)
L
Linus Torvalds 已提交
415
{
S
Stuart Menefy 已提交
416 417
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->regs[15], regs,
L
Linus Torvalds 已提交
418 419 420 421 422 423
		       0, NULL, NULL);
}

/*
 * sys_execve() executes a new program.
 */
424 425
asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv,
			  char __user * __user *uenvp, unsigned long r7,
S
Stuart Menefy 已提交
426
			  struct pt_regs __regs)
L
Linus Torvalds 已提交
427
{
S
Stuart Menefy 已提交
428
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
L
Linus Torvalds 已提交
429 430 431
	int error;
	char *filename;

432
	filename = getname(ufilename);
L
Linus Torvalds 已提交
433 434 435 436
	error = PTR_ERR(filename);
	if (IS_ERR(filename))
		goto out;

437
	error = do_execve(filename, uargv, uenvp, regs);
L
Linus Torvalds 已提交
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
	if (error == 0) {
		task_lock(current);
		current->ptrace &= ~PT_DTRACE;
		task_unlock(current);
	}
	putname(filename);
out:
	return error;
}

unsigned long get_wchan(struct task_struct *p)
{
	unsigned long pc;

	if (!p || p == current || p->state == TASK_RUNNING)
		return 0;

	/*
	 * The same comment as on the Alpha applies here, too ...
	 */
	pc = thread_saved_pc(p);
459 460

#ifdef CONFIG_FRAME_POINTER
L
Linus Torvalds 已提交
461
	if (in_sched_functions(pc)) {
462
		unsigned long schedule_frame = (unsigned long)p->thread.sp;
P
Paul Mundt 已提交
463
		return ((unsigned long *)schedule_frame)[21];
L
Linus Torvalds 已提交
464
	}
465
#endif
P
Paul Mundt 已提交
466

L
Linus Torvalds 已提交
467 468 469
	return pc;
}

S
Stuart Menefy 已提交
470
asmlinkage void break_point_trap(void)
L
Linus Torvalds 已提交
471 472
{
	/* Clear tracing.  */
R
Ryusuke Sakato 已提交
473 474 475 476
#if defined(CONFIG_CPU_SH4A)
	ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
	ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
L
Linus Torvalds 已提交
477 478
	ctrl_outw(0, UBC_BBRA);
	ctrl_outw(0, UBC_BBRB);
R
Ryusuke Sakato 已提交
479
#endif
L
Linus Torvalds 已提交
480 481 482 483 484 485
	current->thread.ubc_pc = 0;
	ubc_usercnt -= 1;

	force_sig(SIGTRAP, current);
}

486 487 488 489 490 491 492 493 494 495
/*
 * Generic trap handler.
 */
asmlinkage void debug_trap_handler(unsigned long r4, unsigned long r5,
				   unsigned long r6, unsigned long r7,
				   struct pt_regs __regs)
{
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);

	/* Rewind */
496
	regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
497

P
Paul Mundt 已提交
498
	if (notify_die(DIE_TRAP, "debug trap", regs, 0, regs->tra & 0xff,
P
Paul Mundt 已提交
499 500 501
		       SIGTRAP) == NOTIFY_STOP)
		return;

502 503 504 505 506 507 508 509 510
	force_sig(SIGTRAP, current);
}

/*
 * Special handler for BUG() traps.
 */
asmlinkage void bug_trap_handler(unsigned long r4, unsigned long r5,
				 unsigned long r6, unsigned long r7,
				 struct pt_regs __regs)
L
Linus Torvalds 已提交
511
{
S
Stuart Menefy 已提交
512 513
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);

514
	/* Rewind */
515
	regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
516

P
Paul Mundt 已提交
517
	if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff,
P
Paul Mundt 已提交
518 519 520
		       SIGTRAP) == NOTIFY_STOP)
		return;

521 522 523 524 525 526 527 528
#ifdef CONFIG_BUG
	if (__kernel_text_address(instruction_pointer(regs))) {
		u16 insn = *(u16 *)instruction_pointer(regs);
		if (insn == TRAPA_BUG_OPCODE)
			handle_BUG(regs);
	}
#endif

L
Linus Torvalds 已提交
529 530
	force_sig(SIGTRAP, current);
}