process_kern.c 9.41 KB
Newer Older
1
/*
Jeff Dike's avatar
Jeff Dike committed
2 3
 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
 * Copyright 2003 PathScale, Inc.
Linus Torvalds's avatar
Linus Torvalds committed
4 5 6
 * Licensed under the GPL
 */

Jeff Dike's avatar
Jeff Dike committed
7 8
#include "linux/config.h"
#include "linux/kernel.h"
Linus Torvalds's avatar
Linus Torvalds committed
9
#include "linux/sched.h"
Jeff Dike's avatar
Jeff Dike committed
10 11 12
#include "linux/interrupt.h"
#include "linux/string.h"
#include "linux/mm.h"
Linus Torvalds's avatar
Linus Torvalds committed
13
#include "linux/slab.h"
Jeff Dike's avatar
Jeff Dike committed
14 15 16 17 18
#include "linux/utsname.h"
#include "linux/fs.h"
#include "linux/utime.h"
#include "linux/smp_lock.h"
#include "linux/module.h"
Linus Torvalds's avatar
Linus Torvalds committed
19
#include "linux/init.h"
Jeff Dike's avatar
Jeff Dike committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include "linux/capability.h"
#include "linux/vmalloc.h"
#include "linux/spinlock.h"
#include "linux/proc_fs.h"
#include "linux/ptrace.h"
#include "linux/random.h"
#include "linux/personality.h"
#include "asm/unistd.h"
#include "asm/mman.h"
#include "asm/segment.h"
#include "asm/stat.h"
#include "asm/pgtable.h"
#include "asm/processor.h"
#include "asm/tlbflush.h"
Linus Torvalds's avatar
Linus Torvalds committed
34
#include "asm/uaccess.h"
Jeff Dike's avatar
Jeff Dike committed
35
#include "asm/user.h"
Linus Torvalds's avatar
Linus Torvalds committed
36
#include "user_util.h"
Jeff Dike's avatar
Jeff Dike committed
37
#include "kern_util.h"
Linus Torvalds's avatar
Linus Torvalds committed
38
#include "kern.h"
Jeff Dike's avatar
Jeff Dike committed
39 40 41 42 43 44 45 46
#include "signal_kern.h"
#include "init.h"
#include "irq_user.h"
#include "mem_user.h"
#include "tlb.h"
#include "frame_kern.h"
#include "sigcontext.h"
#include "os.h"
Linus Torvalds's avatar
Linus Torvalds committed
47
#include "mode.h"
Jeff Dike's avatar
Jeff Dike committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#include "mode_kern.h"
#include "choose-mode.h"

/* This is a per-cpu array.  A processor only modifies its entry and it only
 * cares about its entry, so it's OK if another processor is modifying its
 * entry.
 */
struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } };

int external_pid(void *t)
{
	struct task_struct *task = t ? t : current;

	return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task));
}

int pid_to_processor_id(int pid)
{
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
67

Jeff Dike's avatar
Jeff Dike committed
68 69 70 71 72 73 74
	for(i = 0; i < ncpus; i++){
		if(cpu_tasks[i].pid == pid) return(i);
	}
	return(-1);
}

void free_stack(unsigned long stack, int order)
Linus Torvalds's avatar
Linus Torvalds committed
75
{
Jeff Dike's avatar
Jeff Dike committed
76 77
	free_pages(stack, order);
}
Linus Torvalds's avatar
Linus Torvalds committed
78

Jeff Dike's avatar
Jeff Dike committed
79 80 81 82
unsigned long alloc_stack(int order, int atomic)
{
	unsigned long page;
	gfp_t flags = GFP_KERNEL;
Linus Torvalds's avatar
Linus Torvalds committed
83

Jeff Dike's avatar
Jeff Dike committed
84 85 86 87 88 89 90 91
	if (atomic)
		flags = GFP_ATOMIC;
	page = __get_free_pages(flags, order);
	if(page == 0)
		return(0);
	stack_protections(page);
	return(page);
}
Linus Torvalds's avatar
Linus Torvalds committed
92

Jeff Dike's avatar
Jeff Dike committed
93 94 95 96 97 98 99 100 101 102 103 104
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
	int pid;

	current->thread.request.u.thread.proc = fn;
	current->thread.request.u.thread.arg = arg;
	pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0,
		      &current->thread.regs, 0, NULL, NULL);
	if(pid < 0)
		panic("do_fork failed in kernel_thread, errno = %d", pid);
	return(pid);
}
Linus Torvalds's avatar
Linus Torvalds committed
105

Jeff Dike's avatar
Jeff Dike committed
106 107 108
void set_current(void *t)
{
	struct task_struct *task = t;
109

Jeff Dike's avatar
Jeff Dike committed
110 111
	cpu_tasks[task_thread_info(task)->cpu] = ((struct cpu_task)
		{ external_pid(task), task });
Linus Torvalds's avatar
Linus Torvalds committed
112 113
}

Jeff Dike's avatar
Jeff Dike committed
114 115 116 117
void *_switch_to(void *prev, void *next, void *last)
{
        struct task_struct *from = prev;
        struct task_struct *to= next;
Linus Torvalds's avatar
Linus Torvalds committed
118

Jeff Dike's avatar
Jeff Dike committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
        to->thread.prev_sched = from;
        set_current(to);

	do {
		current->thread.saved_task = NULL ;
		CHOOSE_MODE_PROC(switch_to_tt, switch_to_skas, prev, next);
		if(current->thread.saved_task)
			show_regs(&(current->thread.regs));
		next= current->thread.saved_task;
		prev= current;
	} while(current->thread.saved_task);

        return(current->thread.prev_sched);

}

void interrupt_end(void)
Linus Torvalds's avatar
Linus Torvalds committed
136
{
Jeff Dike's avatar
Jeff Dike committed
137 138 139
	if(need_resched()) schedule();
	if(test_tsk_thread_flag(current, TIF_SIGPENDING)) do_signal();
}
Linus Torvalds's avatar
Linus Torvalds committed
140

Jeff Dike's avatar
Jeff Dike committed
141 142 143 144
void release_thread(struct task_struct *task)
{
	CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task));
}
Linus Torvalds's avatar
Linus Torvalds committed
145

Jeff Dike's avatar
Jeff Dike committed
146 147 148 149
void exit_thread(void)
{
	unprotect_stack((unsigned long) current_thread);
}
150

Jeff Dike's avatar
Jeff Dike committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
void *get_current(void)
{
	return(current);
}

int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
		unsigned long stack_top, struct task_struct * p,
		struct pt_regs *regs)
{
	int ret;

	p->thread = (struct thread_struct) INIT_THREAD;
	ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
				clone_flags, sp, stack_top, p, regs);

	if (ret || !current->thread.forking)
		goto out;

	clear_flushed_tls(p);

	/*
	 * Set a new TLS for the child thread?
Linus Torvalds's avatar
Linus Torvalds committed
173
	 */
Jeff Dike's avatar
Jeff Dike committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	if (clone_flags & CLONE_SETTLS)
		ret = arch_copy_tls(p);

out:
	return ret;
}

void initial_thread_cb(void (*proc)(void *), void *arg)
{
	int save_kmalloc_ok = kmalloc_ok;

	kmalloc_ok = 0;
	CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc,
			 arg);
	kmalloc_ok = save_kmalloc_ok;
}

unsigned long stack_sp(unsigned long page)
{
	return(page + PAGE_SIZE - sizeof(void *));
}

int current_pid(void)
{
	return(current->pid);
}

void default_idle(void)
{
	CHOOSE_MODE(uml_idle_timer(), (void) 0);

	while(1){
		/* endless idle loop with no priority at all */

		/*
		 * although we are an idle CPU, we do not want to
		 * get into the scheduler unnecessarily.
		 */
		if(need_resched())
			schedule();

		idle_sleep(10);
Jeff Dike's avatar
Jeff Dike committed
216
	}
Linus Torvalds's avatar
Linus Torvalds committed
217 218
}

Jeff Dike's avatar
Jeff Dike committed
219
void cpu_idle(void)
Linus Torvalds's avatar
Linus Torvalds committed
220
{
Jeff Dike's avatar
Jeff Dike committed
221
	CHOOSE_MODE(init_idle_tt(), init_idle_skas());
Linus Torvalds's avatar
Linus Torvalds committed
222 223
}

Jeff Dike's avatar
Jeff Dike committed
224
int page_size(void)
Linus Torvalds's avatar
Linus Torvalds committed
225
{
Jeff Dike's avatar
Jeff Dike committed
226 227
	return(PAGE_SIZE);
}
Linus Torvalds's avatar
Linus Torvalds committed
228

Jeff Dike's avatar
Jeff Dike committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
		      pte_t *pte_out)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	pte_t ptent;

	if(task->mm == NULL)
		return(ERR_PTR(-EINVAL));
	pgd = pgd_offset(task->mm, addr);
	if(!pgd_present(*pgd))
		return(ERR_PTR(-EINVAL));

	pud = pud_offset(pgd, addr);
	if(!pud_present(*pud))
		return(ERR_PTR(-EINVAL));

	pmd = pmd_offset(pud, addr);
	if(!pmd_present(*pmd))
		return(ERR_PTR(-EINVAL));

	pte = pte_offset_kernel(pmd, addr);
	ptent = *pte;
	if(!pte_present(ptent))
		return(ERR_PTR(-EINVAL));

	if(pte_out != NULL)
		*pte_out = ptent;
	return((void *) (pte_val(ptent) & PAGE_MASK) + (addr & ~PAGE_MASK));
}
261

Jeff Dike's avatar
Jeff Dike committed
262 263 264 265 266 267 268 269 270
char *current_cmd(void)
{
#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
	return("(Unknown)");
#else
	void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
	return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
#endif
}
271

Jeff Dike's avatar
Jeff Dike committed
272 273 274 275 276 277 278 279 280 281
void force_sigbus(void)
{
	printk(KERN_ERR "Killing pid %d because of a lack of memory\n",
	       current->pid);
	lock_kernel();
	sigaddset(&current->pending.signal, SIGBUS);
	recalc_sigpending();
	current->flags |= PF_SIGNALED;
	do_exit(SIGBUS | 0x80);
}
Linus Torvalds's avatar
Linus Torvalds committed
282

Jeff Dike's avatar
Jeff Dike committed
283 284 285
void dump_thread(struct pt_regs *regs, struct user *u)
{
}
286

Jeff Dike's avatar
Jeff Dike committed
287 288 289
void enable_hlt(void)
{
	panic("enable_hlt");
Linus Torvalds's avatar
Linus Torvalds committed
290 291
}

Jeff Dike's avatar
Jeff Dike committed
292 293 294
EXPORT_SYMBOL(enable_hlt);

void disable_hlt(void)
Linus Torvalds's avatar
Linus Torvalds committed
295
{
Jeff Dike's avatar
Jeff Dike committed
296 297
	panic("disable_hlt");
}
Linus Torvalds's avatar
Linus Torvalds committed
298

Jeff Dike's avatar
Jeff Dike committed
299
EXPORT_SYMBOL(disable_hlt);
Linus Torvalds's avatar
Linus Torvalds committed
300

Jeff Dike's avatar
Jeff Dike committed
301 302 303 304
void *um_kmalloc(int size)
{
	return kmalloc(size, GFP_KERNEL);
}
305

Jeff Dike's avatar
Jeff Dike committed
306 307 308 309
void *um_kmalloc_atomic(int size)
{
	return kmalloc(size, GFP_ATOMIC);
}
Linus Torvalds's avatar
Linus Torvalds committed
310

Jeff Dike's avatar
Jeff Dike committed
311 312 313
void *um_vmalloc(int size)
{
	return vmalloc(size);
Linus Torvalds's avatar
Linus Torvalds committed
314 315
}

Jeff Dike's avatar
Jeff Dike committed
316
void *um_vmalloc_atomic(int size)
Linus Torvalds's avatar
Linus Torvalds committed
317
{
Jeff Dike's avatar
Jeff Dike committed
318 319
	return __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, PAGE_KERNEL);
}
Linus Torvalds's avatar
Linus Torvalds committed
320

Jeff Dike's avatar
Jeff Dike committed
321 322 323 324
int __cant_sleep(void) {
	return in_atomic() || irqs_disabled() || in_interrupt();
	/* Is in_interrupt() really needed? */
}
Linus Torvalds's avatar
Linus Torvalds committed
325

Jeff Dike's avatar
Jeff Dike committed
326 327 328 329
unsigned long get_fault_addr(void)
{
	return((unsigned long) current->thread.fault_addr);
}
330

Jeff Dike's avatar
Jeff Dike committed
331 332 333 334 335
EXPORT_SYMBOL(get_fault_addr);

void not_implemented(void)
{
	printk(KERN_DEBUG "Something isn't implemented in here\n");
Linus Torvalds's avatar
Linus Torvalds committed
336 337
}

Jeff Dike's avatar
Jeff Dike committed
338 339 340
EXPORT_SYMBOL(not_implemented);

int user_context(unsigned long sp)
Linus Torvalds's avatar
Linus Torvalds committed
341
{
Jeff Dike's avatar
Jeff Dike committed
342 343 344 345
	unsigned long stack;

	stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER);
	return(stack != (unsigned long) current_thread);
Linus Torvalds's avatar
Linus Torvalds committed
346 347
}

Jeff Dike's avatar
Jeff Dike committed
348
extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end;
Linus Torvalds's avatar
Linus Torvalds committed
349

Jeff Dike's avatar
Jeff Dike committed
350
void do_uml_exitcalls(void)
Linus Torvalds's avatar
Linus Torvalds committed
351
{
Jeff Dike's avatar
Jeff Dike committed
352 353 354 355 356 357
	exitcall_t *call;

	call = &__uml_exitcall_end;
	while (--call >= &__uml_exitcall_begin)
		(*call)();
}
Linus Torvalds's avatar
Linus Torvalds committed
358

Jeff Dike's avatar
Jeff Dike committed
359 360 361 362
char *uml_strdup(char *string)
{
	return kstrdup(string, GFP_KERNEL);
}
Linus Torvalds's avatar
Linus Torvalds committed
363

Jeff Dike's avatar
Jeff Dike committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
int copy_to_user_proc(void __user *to, void *from, int size)
{
	return(copy_to_user(to, from, size));
}

int copy_from_user_proc(void *to, void __user *from, int size)
{
	return(copy_from_user(to, from, size));
}

int clear_user_proc(void __user *buf, int size)
{
	return(clear_user(buf, size));
}

int strlen_user_proc(char __user *str)
{
	return(strlen_user(str));
}

int smp_sigio_handler(void)
{
Linus Torvalds's avatar
Linus Torvalds committed
386
#ifdef CONFIG_SMP
Jeff Dike's avatar
Jeff Dike committed
387 388 389 390
	int cpu = current_thread->cpu;
	IPI_handler(cpu);
	if(cpu != 0)
		return(1);
Linus Torvalds's avatar
Linus Torvalds committed
391 392 393 394
#endif
	return(0);
}

Jeff Dike's avatar
Jeff Dike committed
395
int cpu(void)
Linus Torvalds's avatar
Linus Torvalds committed
396
{
Jeff Dike's avatar
Jeff Dike committed
397 398
	return(current_thread->cpu);
}
Linus Torvalds's avatar
Linus Torvalds committed
399

Jeff Dike's avatar
Jeff Dike committed
400 401
static atomic_t using_sysemu = ATOMIC_INIT(0);
int sysemu_supported;
Linus Torvalds's avatar
Linus Torvalds committed
402

Jeff Dike's avatar
Jeff Dike committed
403 404 405 406 407
void set_using_sysemu(int value)
{
	if (value > sysemu_supported)
		return;
	atomic_set(&using_sysemu, value);
Linus Torvalds's avatar
Linus Torvalds committed
408 409
}

Jeff Dike's avatar
Jeff Dike committed
410
int get_using_sysemu(void)
Linus Torvalds's avatar
Linus Torvalds committed
411
{
Jeff Dike's avatar
Jeff Dike committed
412
	return atomic_read(&using_sysemu);
Linus Torvalds's avatar
Linus Torvalds committed
413 414
}

Jeff Dike's avatar
Jeff Dike committed
415
static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
416
{
Jeff Dike's avatar
Jeff Dike committed
417 418 419 420
	if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/
		*eof = 1;

	return strlen(buf);
Linus Torvalds's avatar
Linus Torvalds committed
421
}
422

Jeff Dike's avatar
Jeff Dike committed
423
static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned long count,void *data)
424
{
Jeff Dike's avatar
Jeff Dike committed
425
	char tmp[2];
426

Jeff Dike's avatar
Jeff Dike committed
427 428
	if (copy_from_user(tmp, buf, 1))
		return -EFAULT;
429

Jeff Dike's avatar
Jeff Dike committed
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	if (tmp[0] >= '0' && tmp[0] <= '2')
		set_using_sysemu(tmp[0] - '0');
	return count; /*We use the first char, but pretend to write everything*/
}

int __init make_proc_sysemu(void)
{
	struct proc_dir_entry *ent;
	if (!sysemu_supported)
		return 0;

	ent = create_proc_entry("sysemu", 0600, &proc_root);

	if (ent == NULL)
	{
		printk(KERN_WARNING "Failed to register /proc/sysemu\n");
		return(0);
447
	}
Jeff Dike's avatar
Jeff Dike committed
448 449 450 451 452

	ent->read_proc  = proc_read_sysemu;
	ent->write_proc = proc_write_sysemu;

	return 0;
453 454
}

Jeff Dike's avatar
Jeff Dike committed
455 456 457
late_initcall(make_proc_sysemu);

int singlestepping(void * t)
458
{
Jeff Dike's avatar
Jeff Dike committed
459 460 461
	struct task_struct *task = t ? t : current;

	if ( ! (task->ptrace & PT_DTRACE) )
462 463
		return(0);

Jeff Dike's avatar
Jeff Dike committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	if (task->thread.singlestep_syscall)
		return(1);

	return 2;
}

/*
 * Only x86 and x86_64 have an arch_align_stack().
 * All other arches have "#define arch_align_stack(x) (x)"
 * in their asm/system.h
 * As this is included in UML from asm-um/system-generic.h,
 * we can use it to behave as the subarch does.
 */
#ifndef arch_align_stack
unsigned long arch_align_stack(unsigned long sp)
{
	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
		sp -= get_random_int() % 8192;
	return sp & ~0xf;
483
}
Jeff Dike's avatar
Jeff Dike committed
484
#endif