process_32.c 12.3 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
	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;
}

P
Paul Mundt 已提交
233
/*
L
Linus Torvalds 已提交
234 235 236 237 238
 * Capture the user space registers if the task is not running (in user space)
 */
int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
{
	struct pt_regs ptregs;
P
Paul Mundt 已提交
239

A
Al Viro 已提交
240
	ptregs = *task_pt_regs(tsk);
L
Linus Torvalds 已提交
241 242 243 244 245
	elf_core_copy_regs(regs, &ptregs);

	return 1;
}

P
Paul Mundt 已提交
246
int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpu)
L
Linus Torvalds 已提交
247 248 249 250 251 252
{
	int fpvalid = 0;

#if defined(CONFIG_SH_FPU)
	fpvalid = !!tsk_used_math(tsk);
	if (fpvalid) {
A
Al Viro 已提交
253
		unlazy_fpu(tsk, task_pt_regs(tsk));
L
Linus Torvalds 已提交
254 255 256 257 258 259 260 261 262 263 264 265 266
		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)
{
267
	struct thread_info *ti = task_thread_info(p);
L
Linus Torvalds 已提交
268 269 270 271 272 273 274 275 276
	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 已提交
277
	childregs = task_pt_regs(p);
L
Linus Torvalds 已提交
278 279 280 281
	*childregs = *regs;

	if (user_mode(regs)) {
		childregs->regs[15] = usp;
282
		ti->addr_limit = USER_DS;
L
Linus Torvalds 已提交
283
	} else {
284
		childregs->regs[15] = (unsigned long)childregs;
285
		ti->addr_limit = KERNEL_DS;
L
Linus Torvalds 已提交
286
	}
P
Paul Mundt 已提交
287

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

L
Linus Torvalds 已提交
291 292 293 294 295 296 297 298 299 300 301
	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 已提交
302
static void ubc_set_tracing(int asid, unsigned long pc)
L
Linus Torvalds 已提交
303
{
R
Ryusuke Sakato 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317
#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 已提交
318
	/* Read UBC register that we wrote last, for checking update */
R
Ryusuke Sakato 已提交
319 320 321
	val = ctrl_inl(UBC_CRR0);

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

324
#ifdef CONFIG_MMU
325
	ctrl_outb(asid, UBC_BASRA);
326
#endif
L
Linus Torvalds 已提交
327 328 329

	ctrl_outl(0, UBC_BAMRA);

330
	if (current_cpu_data.type == CPU_SH7729 ||
P
Paul Mundt 已提交
331 332
	    current_cpu_data.type == CPU_SH7710 ||
	    current_cpu_data.type == CPU_SH7712) {
L
Linus Torvalds 已提交
333 334 335 336 337 338
		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 已提交
339
#endif	/* CONFIG_CPU_SH4A */
L
Linus Torvalds 已提交
340 341 342 343 344 345
}

/*
 *	switch_to(x,y) should switch tasks from x to y.
 *
 */
P
Paul Mundt 已提交
346 347
struct task_struct *__switch_to(struct task_struct *prev,
				struct task_struct *next)
L
Linus Torvalds 已提交
348 349
{
#if defined(CONFIG_SH_FPU)
A
Al Viro 已提交
350
	unlazy_fpu(prev, task_pt_regs(prev));
L
Linus Torvalds 已提交
351 352
#endif

P
Paul Mundt 已提交
353
#if defined(CONFIG_GUSA) && defined(CONFIG_PREEMPT)
L
Linus Torvalds 已提交
354 355 356
	{
		struct pt_regs *regs;

357
		preempt_disable();
A
Al Viro 已提交
358
		regs = task_pt_regs(prev);
L
Linus Torvalds 已提交
359 360 361 362 363 364 365 366 367
		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;
		}
368
		preempt_enable_no_resched();
L
Linus Torvalds 已提交
369 370 371
	}
#endif

372
#ifdef CONFIG_MMU
L
Linus Torvalds 已提交
373 374
	/*
	 * Restore the kernel mode register
P
Paul Mundt 已提交
375
	 *	k7 (r7_bank1)
L
Linus Torvalds 已提交
376 377 378
	 */
	asm volatile("ldc	%0, r7_bank"
		     : /* no output */
A
Al Viro 已提交
379
		     : "r" (task_thread_info(next)));
380
#endif
L
Linus Torvalds 已提交
381 382 383 384 385

	/* 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) {
386 387
		int asid = 0;
#ifdef CONFIG_MMU
P
Paul Mundt 已提交
388
		asid |= cpu_asid(smp_processor_id(), next->mm);
389 390
#endif
		ubc_set_tracing(asid, next->thread.ubc_pc);
L
Linus Torvalds 已提交
391
	} else {
R
Ryusuke Sakato 已提交
392 393 394 395
#if defined(CONFIG_CPU_SH4A)
		ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
		ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
L
Linus Torvalds 已提交
396 397
		ctrl_outw(0, UBC_BBRA);
		ctrl_outw(0, UBC_BBRB);
R
Ryusuke Sakato 已提交
398
#endif
L
Linus Torvalds 已提交
399 400 401 402 403 404 405
	}

	return prev;
}

asmlinkage int sys_fork(unsigned long r4, unsigned long r5,
			unsigned long r6, unsigned long r7,
S
Stuart Menefy 已提交
406
			struct pt_regs __regs)
L
Linus Torvalds 已提交
407 408
{
#ifdef CONFIG_MMU
409
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
S
Stuart Menefy 已提交
410
	return do_fork(SIGCHLD, regs->regs[15], regs, 0, NULL, NULL);
L
Linus Torvalds 已提交
411 412 413 414 415 416 417 418 419
#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 已提交
420
			 struct pt_regs __regs)
L
Linus Torvalds 已提交
421
{
S
Stuart Menefy 已提交
422
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
L
Linus Torvalds 已提交
423
	if (!newsp)
S
Stuart Menefy 已提交
424 425
		newsp = regs->regs[15];
	return do_fork(clone_flags, newsp, regs, 0,
P
Paul Mundt 已提交
426 427
			(int __user *)parent_tidptr,
			(int __user *)child_tidptr);
L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441
}

/*
 * 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 已提交
442
			 struct pt_regs __regs)
L
Linus Torvalds 已提交
443
{
S
Stuart Menefy 已提交
444 445
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->regs[15], regs,
L
Linus Torvalds 已提交
446 447 448 449 450 451
		       0, NULL, NULL);
}

/*
 * sys_execve() executes a new program.
 */
452 453
asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv,
			  char __user * __user *uenvp, unsigned long r7,
S
Stuart Menefy 已提交
454
			  struct pt_regs __regs)
L
Linus Torvalds 已提交
455
{
S
Stuart Menefy 已提交
456
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
L
Linus Torvalds 已提交
457 458 459
	int error;
	char *filename;

460
	filename = getname(ufilename);
L
Linus Torvalds 已提交
461 462 463 464
	error = PTR_ERR(filename);
	if (IS_ERR(filename))
		goto out;

465
	error = do_execve(filename, uargv, uenvp, regs);
L
Linus Torvalds 已提交
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
	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);
487 488

#ifdef CONFIG_FRAME_POINTER
L
Linus Torvalds 已提交
489
	if (in_sched_functions(pc)) {
490
		unsigned long schedule_frame = (unsigned long)p->thread.sp;
P
Paul Mundt 已提交
491
		return ((unsigned long *)schedule_frame)[21];
L
Linus Torvalds 已提交
492
	}
493
#endif
P
Paul Mundt 已提交
494

L
Linus Torvalds 已提交
495 496 497
	return pc;
}

S
Stuart Menefy 已提交
498
asmlinkage void break_point_trap(void)
L
Linus Torvalds 已提交
499 500
{
	/* Clear tracing.  */
R
Ryusuke Sakato 已提交
501 502 503 504
#if defined(CONFIG_CPU_SH4A)
	ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
	ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
L
Linus Torvalds 已提交
505 506
	ctrl_outw(0, UBC_BBRA);
	ctrl_outw(0, UBC_BBRB);
R
Ryusuke Sakato 已提交
507
#endif
L
Linus Torvalds 已提交
508 509 510 511 512 513
	current->thread.ubc_pc = 0;
	ubc_usercnt -= 1;

	force_sig(SIGTRAP, current);
}

514 515 516 517 518 519 520 521 522 523
/*
 * 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 */
524
	regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
525

P
Paul Mundt 已提交
526
	if (notify_die(DIE_TRAP, "debug trap", regs, 0, regs->tra & 0xff,
P
Paul Mundt 已提交
527 528 529
		       SIGTRAP) == NOTIFY_STOP)
		return;

530 531 532 533 534 535 536 537 538
	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 已提交
539
{
S
Stuart Menefy 已提交
540 541
	struct pt_regs *regs = RELOC_HIDE(&__regs, 0);

542
	/* Rewind */
543
	regs->pc -= instruction_size(ctrl_inw(regs->pc - 4));
544

P
Paul Mundt 已提交
545
	if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff,
P
Paul Mundt 已提交
546 547 548
		       SIGTRAP) == NOTIFY_STOP)
		return;

549 550 551 552 553 554 555 556
#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 已提交
557 558
	force_sig(SIGTRAP, current);
}