diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/um/kernel |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/um/kernel')
99 files changed, 13673 insertions, 0 deletions
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile new file mode 100644 index 000000000000..dc796c1bf39e --- /dev/null +++ b/arch/um/kernel/Makefile | |||
@@ -0,0 +1,58 @@ | |||
1 | # | ||
2 | # Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | extra-y := vmlinux.lds | ||
7 | clean-files := vmlinux.lds.S config.tmp | ||
8 | |||
9 | obj-y = checksum.o config.o exec_kern.o exitcode.o \ | ||
10 | helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \ | ||
11 | physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \ | ||
12 | sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \ | ||
13 | syscall_kern.o sysrq.o sys_call_table.o tempfile.o time.o time_kern.o \ | ||
14 | tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o umid.o \ | ||
15 | user_util.o | ||
16 | |||
17 | obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o | ||
18 | obj-$(CONFIG_GPROF) += gprof_syms.o | ||
19 | obj-$(CONFIG_GCOV) += gmon_syms.o | ||
20 | obj-$(CONFIG_TTY_LOG) += tty_log.o | ||
21 | obj-$(CONFIG_SYSCALL_DEBUG) += syscall_user.o | ||
22 | |||
23 | obj-$(CONFIG_MODE_TT) += tt/ | ||
24 | obj-$(CONFIG_MODE_SKAS) += skas/ | ||
25 | |||
26 | # This needs be compiled with frame pointers regardless of how the rest of the | ||
27 | # kernel is built. | ||
28 | CFLAGS_frame.o := -fno-omit-frame-pointer | ||
29 | |||
30 | user-objs-$(CONFIG_TTY_LOG) += tty_log.o | ||
31 | |||
32 | USER_OBJS := $(user-objs-y) config.o helper.o main.o process.o tempfile.o \ | ||
33 | time.o tty_log.o umid.o user_util.o frame.o | ||
34 | |||
35 | include arch/um/scripts/Makefile.rules | ||
36 | |||
37 | targets += config.c | ||
38 | |||
39 | # Be careful with the below Sed code - sed is pitfall-rich! | ||
40 | # We use sed to lower build requirements, for "embedded" builders for instance. | ||
41 | |||
42 | $(obj)/config.tmp: $(objtree)/.config FORCE | ||
43 | $(call if_changed,quote1) | ||
44 | |||
45 | quiet_cmd_quote1 = QUOTE $@ | ||
46 | cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' \ | ||
47 | $< > $@ | ||
48 | |||
49 | $(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE | ||
50 | $(call if_changed,quote2) | ||
51 | |||
52 | quiet_cmd_quote2 = QUOTE $@ | ||
53 | cmd_quote2 = sed -e '/CONFIG/{' \ | ||
54 | -e 's/"CONFIG"\;/""/' \ | ||
55 | -e 'r $(obj)/config.tmp' \ | ||
56 | -e 'a""\;' \ | ||
57 | -e '}' \ | ||
58 | $< > $@ | ||
diff --git a/arch/um/kernel/checksum.c b/arch/um/kernel/checksum.c new file mode 100644 index 000000000000..e69b2be951d1 --- /dev/null +++ b/arch/um/kernel/checksum.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include "asm/uaccess.h" | ||
2 | #include "linux/errno.h" | ||
3 | #include "linux/module.h" | ||
4 | |||
5 | unsigned int arch_csum_partial(const unsigned char *buff, int len, int sum); | ||
6 | |||
7 | unsigned int csum_partial(unsigned char *buff, int len, int sum) | ||
8 | { | ||
9 | return arch_csum_partial(buff, len, sum); | ||
10 | } | ||
11 | |||
12 | EXPORT_SYMBOL(csum_partial); | ||
13 | |||
14 | unsigned int csum_partial_copy_to(const unsigned char *src, | ||
15 | unsigned char __user *dst, int len, int sum, | ||
16 | int *err_ptr) | ||
17 | { | ||
18 | if(copy_to_user(dst, src, len)){ | ||
19 | *err_ptr = -EFAULT; | ||
20 | return(-1); | ||
21 | } | ||
22 | |||
23 | return(arch_csum_partial(src, len, sum)); | ||
24 | } | ||
25 | |||
26 | unsigned int csum_partial_copy_from(const unsigned char __user *src, | ||
27 | unsigned char *dst, int len, int sum, | ||
28 | int *err_ptr) | ||
29 | { | ||
30 | if(copy_from_user(dst, src, len)){ | ||
31 | *err_ptr = -EFAULT; | ||
32 | return(-1); | ||
33 | } | ||
34 | |||
35 | return arch_csum_partial(dst, len, sum); | ||
36 | } | ||
diff --git a/arch/um/kernel/config.c.in b/arch/um/kernel/config.c.in new file mode 100644 index 000000000000..c062cbfe386e --- /dev/null +++ b/arch/um/kernel/config.c.in | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include "init.h" | ||
9 | |||
10 | static __initdata char *config = "CONFIG"; | ||
11 | |||
12 | static int __init print_config(char *line, int *add) | ||
13 | { | ||
14 | printf("%s", config); | ||
15 | exit(0); | ||
16 | } | ||
17 | |||
18 | __uml_setup("--showconfig", print_config, | ||
19 | "--showconfig\n" | ||
20 | " Prints the config file that this UML binary was generated from.\n\n" | ||
21 | ); | ||
22 | |||
23 | /* | ||
24 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
25 | * Emacs will notice this stuff at the end of the file and automatically | ||
26 | * adjust the settings for this buffer only. This must remain at the end | ||
27 | * of the file. | ||
28 | * --------------------------------------------------------------------------- | ||
29 | * Local variables: | ||
30 | * c-file-style: "linux" | ||
31 | * End: | ||
32 | */ | ||
diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S new file mode 100644 index 000000000000..715b0838a68c --- /dev/null +++ b/arch/um/kernel/dyn.lds.S | |||
@@ -0,0 +1,176 @@ | |||
1 | #include <asm-generic/vmlinux.lds.h> | ||
2 | |||
3 | OUTPUT_FORMAT(ELF_FORMAT) | ||
4 | OUTPUT_ARCH(ELF_ARCH) | ||
5 | ENTRY(_start) | ||
6 | jiffies = jiffies_64; | ||
7 | |||
8 | SECTIONS | ||
9 | { | ||
10 | PROVIDE (__executable_start = START); | ||
11 | . = START + SIZEOF_HEADERS; | ||
12 | .interp : { *(.interp) } | ||
13 | /* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start | ||
14 | * is remapped.*/ | ||
15 | __binary_start = .; | ||
16 | . = ALIGN(4096); /* Init code and data */ | ||
17 | _stext = .; | ||
18 | __init_begin = .; | ||
19 | .init.text : { | ||
20 | _sinittext = .; | ||
21 | *(.init.text) | ||
22 | _einittext = .; | ||
23 | } | ||
24 | |||
25 | . = ALIGN(4096); | ||
26 | |||
27 | /* Read-only sections, merged into text segment: */ | ||
28 | .hash : { *(.hash) } | ||
29 | .dynsym : { *(.dynsym) } | ||
30 | .dynstr : { *(.dynstr) } | ||
31 | .gnu.version : { *(.gnu.version) } | ||
32 | .gnu.version_d : { *(.gnu.version_d) } | ||
33 | .gnu.version_r : { *(.gnu.version_r) } | ||
34 | .rel.init : { *(.rel.init) } | ||
35 | .rela.init : { *(.rela.init) } | ||
36 | .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } | ||
37 | .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } | ||
38 | .rel.fini : { *(.rel.fini) } | ||
39 | .rela.fini : { *(.rela.fini) } | ||
40 | .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } | ||
41 | .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } | ||
42 | .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } | ||
43 | .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } | ||
44 | .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } | ||
45 | .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } | ||
46 | .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } | ||
47 | .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } | ||
48 | .rel.ctors : { *(.rel.ctors) } | ||
49 | .rela.ctors : { *(.rela.ctors) } | ||
50 | .rel.dtors : { *(.rel.dtors) } | ||
51 | .rela.dtors : { *(.rela.dtors) } | ||
52 | .rel.got : { *(.rel.got) } | ||
53 | .rela.got : { *(.rela.got) } | ||
54 | .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } | ||
55 | .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } | ||
56 | .rel.plt : { *(.rel.plt) } | ||
57 | .rela.plt : { *(.rela.plt) } | ||
58 | .init : { | ||
59 | KEEP (*(.init)) | ||
60 | } =0x90909090 | ||
61 | .plt : { *(.plt) } | ||
62 | .text : { | ||
63 | *(.text) | ||
64 | SCHED_TEXT | ||
65 | LOCK_TEXT | ||
66 | *(.fixup) | ||
67 | *(.stub .text.* .gnu.linkonce.t.*) | ||
68 | /* .gnu.warning sections are handled specially by elf32.em. */ | ||
69 | *(.gnu.warning) | ||
70 | } =0x90909090 | ||
71 | .fini : { | ||
72 | KEEP (*(.fini)) | ||
73 | } =0x90909090 | ||
74 | |||
75 | .kstrtab : { *(.kstrtab) } | ||
76 | |||
77 | #include "asm/common.lds.S" | ||
78 | |||
79 | init.data : { *(.init.data) } | ||
80 | |||
81 | /* Ensure the __preinit_array_start label is properly aligned. We | ||
82 | could instead move the label definition inside the section, but | ||
83 | the linker would then create the section even if it turns out to | ||
84 | be empty, which isn't pretty. */ | ||
85 | . = ALIGN(32 / 8); | ||
86 | .preinit_array : { *(.preinit_array) } | ||
87 | .init_array : { *(.init_array) } | ||
88 | .fini_array : { *(.fini_array) } | ||
89 | .data : { | ||
90 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ | ||
91 | *(.data.init_task) | ||
92 | *(.data .data.* .gnu.linkonce.d.*) | ||
93 | SORT(CONSTRUCTORS) | ||
94 | } | ||
95 | .data1 : { *(.data1) } | ||
96 | .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } | ||
97 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } | ||
98 | .eh_frame : { KEEP (*(.eh_frame)) } | ||
99 | .gcc_except_table : { *(.gcc_except_table) } | ||
100 | .dynamic : { *(.dynamic) } | ||
101 | .ctors : { | ||
102 | /* gcc uses crtbegin.o to find the start of | ||
103 | the constructors, so we make sure it is | ||
104 | first. Because this is a wildcard, it | ||
105 | doesn't matter if the user does not | ||
106 | actually link against crtbegin.o; the | ||
107 | linker won't look for a file to match a | ||
108 | wildcard. The wildcard also means that it | ||
109 | doesn't matter which directory crtbegin.o | ||
110 | is in. */ | ||
111 | KEEP (*crtbegin.o(.ctors)) | ||
112 | /* We don't want to include the .ctor section from | ||
113 | from the crtend.o file until after the sorted ctors. | ||
114 | The .ctor section from the crtend file contains the | ||
115 | end of ctors marker and it must be last */ | ||
116 | KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) | ||
117 | KEEP (*(SORT(.ctors.*))) | ||
118 | KEEP (*(.ctors)) | ||
119 | } | ||
120 | .dtors : { | ||
121 | KEEP (*crtbegin.o(.dtors)) | ||
122 | KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) | ||
123 | KEEP (*(SORT(.dtors.*))) | ||
124 | KEEP (*(.dtors)) | ||
125 | } | ||
126 | .jcr : { KEEP (*(.jcr)) } | ||
127 | .got : { *(.got.plt) *(.got) } | ||
128 | _edata = .; | ||
129 | PROVIDE (edata = .); | ||
130 | __bss_start = .; | ||
131 | .bss : { | ||
132 | *(.dynbss) | ||
133 | *(.bss .bss.* .gnu.linkonce.b.*) | ||
134 | *(COMMON) | ||
135 | /* Align here to ensure that the .bss section occupies space up to | ||
136 | _end. Align after .bss to ensure correct alignment even if the | ||
137 | .bss section disappears because there are no input sections. */ | ||
138 | . = ALIGN(32 / 8); | ||
139 | . = ALIGN(32 / 8); | ||
140 | } | ||
141 | _end = .; | ||
142 | PROVIDE (end = .); | ||
143 | /* Stabs debugging sections. */ | ||
144 | .stab 0 : { *(.stab) } | ||
145 | .stabstr 0 : { *(.stabstr) } | ||
146 | .stab.excl 0 : { *(.stab.excl) } | ||
147 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
148 | .stab.index 0 : { *(.stab.index) } | ||
149 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
150 | .comment 0 : { *(.comment) } | ||
151 | /* DWARF debug sections. | ||
152 | Symbols in the DWARF debugging sections are relative to the beginning | ||
153 | of the section so we begin them at 0. */ | ||
154 | /* DWARF 1 */ | ||
155 | .debug 0 : { *(.debug) } | ||
156 | .line 0 : { *(.line) } | ||
157 | /* GNU DWARF 1 extensions */ | ||
158 | .debug_srcinfo 0 : { *(.debug_srcinfo) } | ||
159 | .debug_sfnames 0 : { *(.debug_sfnames) } | ||
160 | /* DWARF 1.1 and DWARF 2 */ | ||
161 | .debug_aranges 0 : { *(.debug_aranges) } | ||
162 | .debug_pubnames 0 : { *(.debug_pubnames) } | ||
163 | /* DWARF 2 */ | ||
164 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } | ||
165 | .debug_abbrev 0 : { *(.debug_abbrev) } | ||
166 | .debug_line 0 : { *(.debug_line) } | ||
167 | .debug_frame 0 : { *(.debug_frame) } | ||
168 | .debug_str 0 : { *(.debug_str) } | ||
169 | .debug_loc 0 : { *(.debug_loc) } | ||
170 | .debug_macinfo 0 : { *(.debug_macinfo) } | ||
171 | /* SGI/MIPS DWARF 2 extensions */ | ||
172 | .debug_weaknames 0 : { *(.debug_weaknames) } | ||
173 | .debug_funcnames 0 : { *(.debug_funcnames) } | ||
174 | .debug_typenames 0 : { *(.debug_typenames) } | ||
175 | .debug_varnames 0 : { *(.debug_varnames) } | ||
176 | } | ||
diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c new file mode 100644 index 000000000000..49ddabe69be7 --- /dev/null +++ b/arch/um/kernel/exec_kern.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/slab.h" | ||
7 | #include "linux/smp_lock.h" | ||
8 | #include "linux/ptrace.h" | ||
9 | #include "asm/ptrace.h" | ||
10 | #include "asm/pgtable.h" | ||
11 | #include "asm/tlbflush.h" | ||
12 | #include "asm/uaccess.h" | ||
13 | #include "user_util.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "mem_user.h" | ||
16 | #include "kern.h" | ||
17 | #include "irq_user.h" | ||
18 | #include "tlb.h" | ||
19 | #include "2_5compat.h" | ||
20 | #include "os.h" | ||
21 | #include "time_user.h" | ||
22 | #include "choose-mode.h" | ||
23 | #include "mode_kern.h" | ||
24 | |||
25 | void flush_thread(void) | ||
26 | { | ||
27 | CHOOSE_MODE(flush_thread_tt(), flush_thread_skas()); | ||
28 | } | ||
29 | |||
30 | void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) | ||
31 | { | ||
32 | CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); | ||
33 | } | ||
34 | |||
35 | extern void log_exec(char **argv, void *tty); | ||
36 | |||
37 | static long execve1(char *file, char __user * __user *argv, | ||
38 | char *__user __user *env) | ||
39 | { | ||
40 | long error; | ||
41 | |||
42 | #ifdef CONFIG_TTY_LOG | ||
43 | log_exec(argv, current->tty); | ||
44 | #endif | ||
45 | error = do_execve(file, argv, env, ¤t->thread.regs); | ||
46 | if (error == 0){ | ||
47 | task_lock(current); | ||
48 | current->ptrace &= ~PT_DTRACE; | ||
49 | task_unlock(current); | ||
50 | set_cmdline(current_cmd()); | ||
51 | } | ||
52 | return(error); | ||
53 | } | ||
54 | |||
55 | long um_execve(char *file, char __user *__user *argv, char __user *__user *env) | ||
56 | { | ||
57 | long err; | ||
58 | |||
59 | err = execve1(file, argv, env); | ||
60 | if(!err) | ||
61 | do_longjmp(current->thread.exec_buf, 1); | ||
62 | return(err); | ||
63 | } | ||
64 | |||
65 | long sys_execve(char *file, char __user *__user *argv, | ||
66 | char __user *__user *env) | ||
67 | { | ||
68 | long error; | ||
69 | char *filename; | ||
70 | |||
71 | lock_kernel(); | ||
72 | filename = getname((char __user *) file); | ||
73 | error = PTR_ERR(filename); | ||
74 | if (IS_ERR(filename)) goto out; | ||
75 | error = execve1(filename, argv, env); | ||
76 | putname(filename); | ||
77 | out: | ||
78 | unlock_kernel(); | ||
79 | return(error); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
84 | * Emacs will notice this stuff at the end of the file and automatically | ||
85 | * adjust the settings for this buffer only. This must remain at the end | ||
86 | * of the file. | ||
87 | * --------------------------------------------------------------------------- | ||
88 | * Local variables: | ||
89 | * c-file-style: "linux" | ||
90 | * End: | ||
91 | */ | ||
diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c new file mode 100644 index 000000000000..0ea87f24b36f --- /dev/null +++ b/arch/um/kernel/exitcode.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/init.h" | ||
7 | #include "linux/ctype.h" | ||
8 | #include "linux/proc_fs.h" | ||
9 | #include "asm/uaccess.h" | ||
10 | |||
11 | /* If read and write race, the read will still atomically read a valid | ||
12 | * value. | ||
13 | */ | ||
14 | int uml_exitcode = 0; | ||
15 | |||
16 | static int read_proc_exitcode(char *page, char **start, off_t off, | ||
17 | int count, int *eof, void *data) | ||
18 | { | ||
19 | int len; | ||
20 | |||
21 | len = sprintf(page, "%d\n", uml_exitcode); | ||
22 | len -= off; | ||
23 | if(len <= off+count) *eof = 1; | ||
24 | *start = page + off; | ||
25 | if(len > count) len = count; | ||
26 | if(len < 0) len = 0; | ||
27 | return(len); | ||
28 | } | ||
29 | |||
30 | static int write_proc_exitcode(struct file *file, const char __user *buffer, | ||
31 | unsigned long count, void *data) | ||
32 | { | ||
33 | char *end, buf[sizeof("nnnnn\0")]; | ||
34 | int tmp; | ||
35 | |||
36 | if(copy_from_user(buf, buffer, count)) | ||
37 | return(-EFAULT); | ||
38 | tmp = simple_strtol(buf, &end, 0); | ||
39 | if((*end != '\0') && !isspace(*end)) | ||
40 | return(-EINVAL); | ||
41 | uml_exitcode = tmp; | ||
42 | return(count); | ||
43 | } | ||
44 | |||
45 | static int make_proc_exitcode(void) | ||
46 | { | ||
47 | struct proc_dir_entry *ent; | ||
48 | |||
49 | ent = create_proc_entry("exitcode", 0600, &proc_root); | ||
50 | if(ent == NULL){ | ||
51 | printk("make_proc_exitcode : Failed to register " | ||
52 | "/proc/exitcode\n"); | ||
53 | return(0); | ||
54 | } | ||
55 | |||
56 | ent->read_proc = read_proc_exitcode; | ||
57 | ent->write_proc = write_proc_exitcode; | ||
58 | |||
59 | return(0); | ||
60 | } | ||
61 | |||
62 | __initcall(make_proc_exitcode); | ||
63 | |||
64 | /* | ||
65 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
66 | * Emacs will notice this stuff at the end of the file and automatically | ||
67 | * adjust the settings for this buffer only. This must remain at the end | ||
68 | * of the file. | ||
69 | * --------------------------------------------------------------------------- | ||
70 | * Local variables: | ||
71 | * c-file-style: "linux" | ||
72 | * End: | ||
73 | */ | ||
diff --git a/arch/um/kernel/gmon_syms.c b/arch/um/kernel/gmon_syms.c new file mode 100644 index 000000000000..2c86e7fdb014 --- /dev/null +++ b/arch/um/kernel/gmon_syms.c | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/module.h" | ||
7 | |||
8 | extern void __bb_init_func(void *); | ||
9 | EXPORT_SYMBOL(__bb_init_func); | ||
10 | |||
11 | /* This is defined (and referred to in profiling stub code) only by some GCC | ||
12 | * versions in libgcov. | ||
13 | * | ||
14 | * Since SuSE backported the fix, we cannot handle it depending on GCC version. | ||
15 | * So, unconditinally export it. But also give it a weak declaration, which will | ||
16 | * be overriden by any other one. | ||
17 | */ | ||
18 | |||
19 | extern void __gcov_init(void *) __attribute__((weak)); | ||
20 | EXPORT_SYMBOL(__gcov_init); | ||
21 | |||
22 | extern void __gcov_merge_add(void *) __attribute__((weak)); | ||
23 | EXPORT_SYMBOL(__gcov_merge_add); | ||
24 | |||
25 | /* | ||
26 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
27 | * Emacs will notice this stuff at the end of the file and automatically | ||
28 | * adjust the settings for this buffer only. This must remain at the end | ||
29 | * of the file. | ||
30 | * --------------------------------------------------------------------------- | ||
31 | * Local variables: | ||
32 | * c-file-style: "linux" | ||
33 | * End: | ||
34 | */ | ||
diff --git a/arch/um/kernel/gprof_syms.c b/arch/um/kernel/gprof_syms.c new file mode 100644 index 000000000000..9244f018d44c --- /dev/null +++ b/arch/um/kernel/gprof_syms.c | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/module.h" | ||
7 | |||
8 | extern void mcount(void); | ||
9 | EXPORT_SYMBOL(mcount); | ||
10 | |||
11 | /* | ||
12 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
13 | * Emacs will notice this stuff at the end of the file and automatically | ||
14 | * adjust the settings for this buffer only. This must remain at the end | ||
15 | * of the file. | ||
16 | * --------------------------------------------------------------------------- | ||
17 | * Local variables: | ||
18 | * c-file-style: "linux" | ||
19 | * End: | ||
20 | */ | ||
diff --git a/arch/um/kernel/helper.c b/arch/um/kernel/helper.c new file mode 100644 index 000000000000..13b1f5c2f7ee --- /dev/null +++ b/arch/um/kernel/helper.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <errno.h> | ||
10 | #include <sched.h> | ||
11 | #include <sys/signal.h> | ||
12 | #include <sys/wait.h> | ||
13 | #include "user.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "user_util.h" | ||
16 | #include "os.h" | ||
17 | |||
18 | struct helper_data { | ||
19 | void (*pre_exec)(void*); | ||
20 | void *pre_data; | ||
21 | char **argv; | ||
22 | int fd; | ||
23 | }; | ||
24 | |||
25 | /* Debugging aid, changed only from gdb */ | ||
26 | int helper_pause = 0; | ||
27 | |||
28 | static void helper_hup(int sig) | ||
29 | { | ||
30 | } | ||
31 | |||
32 | static int helper_child(void *arg) | ||
33 | { | ||
34 | struct helper_data *data = arg; | ||
35 | char **argv = data->argv; | ||
36 | int errval; | ||
37 | |||
38 | if(helper_pause){ | ||
39 | signal(SIGHUP, helper_hup); | ||
40 | pause(); | ||
41 | } | ||
42 | if(data->pre_exec != NULL) | ||
43 | (*data->pre_exec)(data->pre_data); | ||
44 | execvp(argv[0], argv); | ||
45 | errval = errno; | ||
46 | printk("execvp of '%s' failed - errno = %d\n", argv[0], errno); | ||
47 | os_write_file(data->fd, &errval, sizeof(errval)); | ||
48 | os_kill_process(os_getpid(), 0); | ||
49 | return(0); | ||
50 | } | ||
51 | |||
52 | /* Returns either the pid of the child process we run or -E* on failure. | ||
53 | * XXX The alloc_stack here breaks if this is called in the tracing thread */ | ||
54 | int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, | ||
55 | unsigned long *stack_out) | ||
56 | { | ||
57 | struct helper_data data; | ||
58 | unsigned long stack, sp; | ||
59 | int pid, fds[2], ret, n; | ||
60 | |||
61 | if((stack_out != NULL) && (*stack_out != 0)) | ||
62 | stack = *stack_out; | ||
63 | else stack = alloc_stack(0, um_in_interrupt()); | ||
64 | if(stack == 0) | ||
65 | return(-ENOMEM); | ||
66 | |||
67 | ret = os_pipe(fds, 1, 0); | ||
68 | if(ret < 0){ | ||
69 | printk("run_helper : pipe failed, ret = %d\n", -ret); | ||
70 | goto out_free; | ||
71 | } | ||
72 | |||
73 | ret = os_set_exec_close(fds[1], 1); | ||
74 | if(ret < 0){ | ||
75 | printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n", | ||
76 | -ret); | ||
77 | goto out_close; | ||
78 | } | ||
79 | |||
80 | sp = stack + page_size() - sizeof(void *); | ||
81 | data.pre_exec = pre_exec; | ||
82 | data.pre_data = pre_data; | ||
83 | data.argv = argv; | ||
84 | data.fd = fds[1]; | ||
85 | pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); | ||
86 | if(pid < 0){ | ||
87 | printk("run_helper : clone failed, errno = %d\n", errno); | ||
88 | ret = -errno; | ||
89 | goto out_close; | ||
90 | } | ||
91 | |||
92 | os_close_file(fds[1]); | ||
93 | fds[1] = -1; | ||
94 | |||
95 | /*Read the errno value from the child.*/ | ||
96 | n = os_read_file(fds[0], &ret, sizeof(ret)); | ||
97 | if(n < 0){ | ||
98 | printk("run_helper : read on pipe failed, ret = %d\n", -n); | ||
99 | ret = n; | ||
100 | os_kill_process(pid, 1); | ||
101 | } | ||
102 | else if(n != 0){ | ||
103 | CATCH_EINTR(n = waitpid(pid, NULL, 0)); | ||
104 | ret = -errno; | ||
105 | } else { | ||
106 | ret = pid; | ||
107 | } | ||
108 | |||
109 | out_close: | ||
110 | if (fds[1] != -1) | ||
111 | os_close_file(fds[1]); | ||
112 | os_close_file(fds[0]); | ||
113 | out_free: | ||
114 | if(stack_out == NULL) | ||
115 | free_stack(stack, 0); | ||
116 | else *stack_out = stack; | ||
117 | return(ret); | ||
118 | } | ||
119 | |||
120 | int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, | ||
121 | unsigned long *stack_out, int stack_order) | ||
122 | { | ||
123 | unsigned long stack, sp; | ||
124 | int pid, status; | ||
125 | |||
126 | stack = alloc_stack(stack_order, um_in_interrupt()); | ||
127 | if(stack == 0) return(-ENOMEM); | ||
128 | |||
129 | sp = stack + (page_size() << stack_order) - sizeof(void *); | ||
130 | pid = clone(proc, (void *) sp, flags | SIGCHLD, arg); | ||
131 | if(pid < 0){ | ||
132 | printk("run_helper_thread : clone failed, errno = %d\n", | ||
133 | errno); | ||
134 | return(-errno); | ||
135 | } | ||
136 | if(stack_out == NULL){ | ||
137 | CATCH_EINTR(pid = waitpid(pid, &status, 0)); | ||
138 | if(pid < 0){ | ||
139 | printk("run_helper_thread - wait failed, errno = %d\n", | ||
140 | errno); | ||
141 | pid = -errno; | ||
142 | } | ||
143 | if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) | ||
144 | printk("run_helper_thread - thread returned status " | ||
145 | "0x%x\n", status); | ||
146 | free_stack(stack, stack_order); | ||
147 | } | ||
148 | else *stack_out = stack; | ||
149 | return(pid); | ||
150 | } | ||
151 | |||
152 | int helper_wait(int pid, int block) | ||
153 | { | ||
154 | int ret; | ||
155 | |||
156 | CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG)); | ||
157 | if(ret < 0){ | ||
158 | printk("helper_wait : waitpid failed, errno = %d\n", errno); | ||
159 | return(-errno); | ||
160 | } | ||
161 | return(ret); | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
166 | * Emacs will notice this stuff at the end of the file and automatically | ||
167 | * adjust the settings for this buffer only. This must remain at the end | ||
168 | * of the file. | ||
169 | * --------------------------------------------------------------------------- | ||
170 | * Local variables: | ||
171 | * c-file-style: "linux" | ||
172 | * End: | ||
173 | */ | ||
diff --git a/arch/um/kernel/init_task.c b/arch/um/kernel/init_task.c new file mode 100644 index 000000000000..cd7c85be0a1b --- /dev/null +++ b/arch/um/kernel/init_task.c | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/mm.h" | ||
8 | #include "linux/module.h" | ||
9 | #include "linux/sched.h" | ||
10 | #include "linux/init_task.h" | ||
11 | #include "linux/mqueue.h" | ||
12 | #include "asm/uaccess.h" | ||
13 | #include "asm/pgtable.h" | ||
14 | #include "user_util.h" | ||
15 | #include "mem_user.h" | ||
16 | |||
17 | static struct fs_struct init_fs = INIT_FS; | ||
18 | struct mm_struct init_mm = INIT_MM(init_mm); | ||
19 | static struct files_struct init_files = INIT_FILES; | ||
20 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
21 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
22 | EXPORT_SYMBOL(init_mm); | ||
23 | |||
24 | /* | ||
25 | * Initial task structure. | ||
26 | * | ||
27 | * All other task structs will be allocated on slabs in fork.c | ||
28 | */ | ||
29 | |||
30 | struct task_struct init_task = INIT_TASK(init_task); | ||
31 | |||
32 | EXPORT_SYMBOL(init_task); | ||
33 | |||
34 | /* | ||
35 | * Initial thread structure. | ||
36 | * | ||
37 | * We need to make sure that this is 16384-byte aligned due to the | ||
38 | * way process stacks are handled. This is done by having a special | ||
39 | * "init_task" linker map entry.. | ||
40 | */ | ||
41 | |||
42 | union thread_union init_thread_union | ||
43 | __attribute__((__section__(".data.init_task"))) = | ||
44 | { INIT_THREAD_INFO(init_task) }; | ||
45 | |||
46 | void unprotect_stack(unsigned long stack) | ||
47 | { | ||
48 | protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, | ||
49 | 1, 1, 0, 1); | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
54 | * Emacs will notice this stuff at the end of the file and automatically | ||
55 | * adjust the settings for this buffer only. This must remain at the end | ||
56 | * of the file. | ||
57 | * --------------------------------------------------------------------------- | ||
58 | * Local variables: | ||
59 | * c-file-style: "linux" | ||
60 | * End: | ||
61 | */ | ||
diff --git a/arch/um/kernel/initrd_kern.c b/arch/um/kernel/initrd_kern.c new file mode 100644 index 000000000000..fc568af468b9 --- /dev/null +++ b/arch/um/kernel/initrd_kern.c | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/init.h" | ||
7 | #include "linux/bootmem.h" | ||
8 | #include "linux/initrd.h" | ||
9 | #include "asm/types.h" | ||
10 | #include "user_util.h" | ||
11 | #include "kern_util.h" | ||
12 | #include "initrd.h" | ||
13 | #include "init.h" | ||
14 | #include "os.h" | ||
15 | |||
16 | /* Changed by uml_initrd_setup, which is a setup */ | ||
17 | static char *initrd __initdata = NULL; | ||
18 | |||
19 | static int __init read_initrd(void) | ||
20 | { | ||
21 | void *area; | ||
22 | long long size; | ||
23 | int err; | ||
24 | |||
25 | if(initrd == NULL) return 0; | ||
26 | err = os_file_size(initrd, &size); | ||
27 | if(err) return 0; | ||
28 | area = alloc_bootmem(size); | ||
29 | if(area == NULL) return 0; | ||
30 | if(load_initrd(initrd, area, size) == -1) return 0; | ||
31 | initrd_start = (unsigned long) area; | ||
32 | initrd_end = initrd_start + size; | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | __uml_postsetup(read_initrd); | ||
37 | |||
38 | static int __init uml_initrd_setup(char *line, int *add) | ||
39 | { | ||
40 | initrd = line; | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | __uml_setup("initrd=", uml_initrd_setup, | ||
45 | "initrd=<initrd image>\n" | ||
46 | " This is used to boot UML from an initrd image. The argument is the\n" | ||
47 | " name of the file containing the image.\n\n" | ||
48 | ); | ||
49 | |||
50 | /* | ||
51 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
52 | * Emacs will notice this stuff at the end of the file and automatically | ||
53 | * adjust the settings for this buffer only. This must remain at the end | ||
54 | * of the file. | ||
55 | * --------------------------------------------------------------------------- | ||
56 | * Local variables: | ||
57 | * c-file-style: "linux" | ||
58 | * End: | ||
59 | */ | ||
diff --git a/arch/um/kernel/initrd_user.c b/arch/um/kernel/initrd_user.c new file mode 100644 index 000000000000..cb90681e151c --- /dev/null +++ b/arch/um/kernel/initrd_user.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <sys/types.h> | ||
8 | #include <sys/stat.h> | ||
9 | #include <errno.h> | ||
10 | |||
11 | #include "user_util.h" | ||
12 | #include "kern_util.h" | ||
13 | #include "user.h" | ||
14 | #include "initrd.h" | ||
15 | #include "os.h" | ||
16 | |||
17 | int load_initrd(char *filename, void *buf, int size) | ||
18 | { | ||
19 | int fd, n; | ||
20 | |||
21 | fd = os_open_file(filename, of_read(OPENFLAGS()), 0); | ||
22 | if(fd < 0){ | ||
23 | printk("Opening '%s' failed - err = %d\n", filename, -fd); | ||
24 | return(-1); | ||
25 | } | ||
26 | n = os_read_file(fd, buf, size); | ||
27 | if(n != size){ | ||
28 | printk("Read of %d bytes from '%s' failed, err = %d\n", size, | ||
29 | filename, -n); | ||
30 | return(-1); | ||
31 | } | ||
32 | |||
33 | os_close_file(fd); | ||
34 | return(0); | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
39 | * Emacs will notice this stuff at the end of the file and automatically | ||
40 | * adjust the settings for this buffer only. This must remain at the end | ||
41 | * of the file. | ||
42 | * --------------------------------------------------------------------------- | ||
43 | * Local variables: | ||
44 | * c-file-style: "linux" | ||
45 | * End: | ||
46 | */ | ||
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c new file mode 100644 index 000000000000..d71e8f00810f --- /dev/null +++ b/arch/um/kernel/irq.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: | ||
5 | * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar | ||
6 | */ | ||
7 | |||
8 | #include "linux/config.h" | ||
9 | #include "linux/kernel.h" | ||
10 | #include "linux/module.h" | ||
11 | #include "linux/smp.h" | ||
12 | #include "linux/irq.h" | ||
13 | #include "linux/kernel_stat.h" | ||
14 | #include "linux/interrupt.h" | ||
15 | #include "linux/random.h" | ||
16 | #include "linux/slab.h" | ||
17 | #include "linux/file.h" | ||
18 | #include "linux/proc_fs.h" | ||
19 | #include "linux/init.h" | ||
20 | #include "linux/seq_file.h" | ||
21 | #include "linux/profile.h" | ||
22 | #include "linux/hardirq.h" | ||
23 | #include "asm/irq.h" | ||
24 | #include "asm/hw_irq.h" | ||
25 | #include "asm/atomic.h" | ||
26 | #include "asm/signal.h" | ||
27 | #include "asm/system.h" | ||
28 | #include "asm/errno.h" | ||
29 | #include "asm/uaccess.h" | ||
30 | #include "user_util.h" | ||
31 | #include "kern_util.h" | ||
32 | #include "irq_user.h" | ||
33 | #include "irq_kern.h" | ||
34 | |||
35 | |||
36 | /* | ||
37 | * Generic, controller-independent functions: | ||
38 | */ | ||
39 | |||
40 | int show_interrupts(struct seq_file *p, void *v) | ||
41 | { | ||
42 | int i = *(loff_t *) v, j; | ||
43 | struct irqaction * action; | ||
44 | unsigned long flags; | ||
45 | |||
46 | if (i == 0) { | ||
47 | seq_printf(p, " "); | ||
48 | for_each_online_cpu(j) | ||
49 | seq_printf(p, "CPU%d ",j); | ||
50 | seq_putc(p, '\n'); | ||
51 | } | ||
52 | |||
53 | if (i < NR_IRQS) { | ||
54 | spin_lock_irqsave(&irq_desc[i].lock, flags); | ||
55 | action = irq_desc[i].action; | ||
56 | if (!action) | ||
57 | goto skip; | ||
58 | seq_printf(p, "%3d: ",i); | ||
59 | #ifndef CONFIG_SMP | ||
60 | seq_printf(p, "%10u ", kstat_irqs(i)); | ||
61 | #else | ||
62 | for_each_online_cpu(j) | ||
63 | seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); | ||
64 | #endif | ||
65 | seq_printf(p, " %14s", irq_desc[i].handler->typename); | ||
66 | seq_printf(p, " %s", action->name); | ||
67 | |||
68 | for (action=action->next; action; action = action->next) | ||
69 | seq_printf(p, ", %s", action->name); | ||
70 | |||
71 | seq_putc(p, '\n'); | ||
72 | skip: | ||
73 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | ||
74 | } else if (i == NR_IRQS) { | ||
75 | seq_putc(p, '\n'); | ||
76 | } | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * do_IRQ handles all normal device IRQ's (the special | ||
83 | * SMP cross-CPU interrupts have their own specific | ||
84 | * handlers). | ||
85 | */ | ||
86 | unsigned int do_IRQ(int irq, union uml_pt_regs *regs) | ||
87 | { | ||
88 | irq_enter(); | ||
89 | __do_IRQ(irq, (struct pt_regs *) regs); | ||
90 | irq_exit(); | ||
91 | return 1; | ||
92 | } | ||
93 | |||
94 | int um_request_irq(unsigned int irq, int fd, int type, | ||
95 | irqreturn_t (*handler)(int, void *, struct pt_regs *), | ||
96 | unsigned long irqflags, const char * devname, | ||
97 | void *dev_id) | ||
98 | { | ||
99 | int err; | ||
100 | |||
101 | err = request_irq(irq, handler, irqflags, devname, dev_id); | ||
102 | if(err) | ||
103 | return(err); | ||
104 | |||
105 | if(fd != -1) | ||
106 | err = activate_fd(irq, fd, type, dev_id); | ||
107 | return(err); | ||
108 | } | ||
109 | EXPORT_SYMBOL(um_request_irq); | ||
110 | EXPORT_SYMBOL(reactivate_fd); | ||
111 | |||
112 | static DEFINE_SPINLOCK(irq_spinlock); | ||
113 | |||
114 | unsigned long irq_lock(void) | ||
115 | { | ||
116 | unsigned long flags; | ||
117 | |||
118 | spin_lock_irqsave(&irq_spinlock, flags); | ||
119 | return(flags); | ||
120 | } | ||
121 | |||
122 | void irq_unlock(unsigned long flags) | ||
123 | { | ||
124 | spin_unlock_irqrestore(&irq_spinlock, flags); | ||
125 | } | ||
126 | |||
127 | /* presently hw_interrupt_type must define (startup || enable) && | ||
128 | * disable && end */ | ||
129 | static void dummy(unsigned int irq) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | static struct hw_interrupt_type SIGIO_irq_type = { | ||
134 | .typename = "SIGIO", | ||
135 | .disable = dummy, | ||
136 | .enable = dummy, | ||
137 | .ack = dummy, | ||
138 | .end = dummy | ||
139 | }; | ||
140 | |||
141 | static struct hw_interrupt_type SIGVTALRM_irq_type = { | ||
142 | .typename = "SIGVTALRM", | ||
143 | .shutdown = dummy, /* never called */ | ||
144 | .disable = dummy, | ||
145 | .enable = dummy, | ||
146 | .ack = dummy, | ||
147 | .end = dummy | ||
148 | }; | ||
149 | |||
150 | void __init init_IRQ(void) | ||
151 | { | ||
152 | int i; | ||
153 | |||
154 | irq_desc[TIMER_IRQ].status = IRQ_DISABLED; | ||
155 | irq_desc[TIMER_IRQ].action = NULL; | ||
156 | irq_desc[TIMER_IRQ].depth = 1; | ||
157 | irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; | ||
158 | enable_irq(TIMER_IRQ); | ||
159 | for(i=1;i<NR_IRQS;i++){ | ||
160 | irq_desc[i].status = IRQ_DISABLED; | ||
161 | irq_desc[i].action = NULL; | ||
162 | irq_desc[i].depth = 1; | ||
163 | irq_desc[i].handler = &SIGIO_irq_type; | ||
164 | enable_irq(i); | ||
165 | } | ||
166 | init_irq_signals(0); | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
171 | * Emacs will notice this stuff at the end of the file and automatically | ||
172 | * adjust the settings for this buffer only. This must remain at the end | ||
173 | * of the file. | ||
174 | * --------------------------------------------------------------------------- | ||
175 | * Local variables: | ||
176 | * c-file-style: "linux" | ||
177 | * End: | ||
178 | */ | ||
diff --git a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c new file mode 100644 index 000000000000..6d6f9484b884 --- /dev/null +++ b/arch/um/kernel/irq_user.c | |||
@@ -0,0 +1,443 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <unistd.h> | ||
8 | #include <errno.h> | ||
9 | #include <signal.h> | ||
10 | #include <string.h> | ||
11 | #include <sys/poll.h> | ||
12 | #include <sys/types.h> | ||
13 | #include <sys/time.h> | ||
14 | #include "user_util.h" | ||
15 | #include "kern_util.h" | ||
16 | #include "user.h" | ||
17 | #include "process.h" | ||
18 | #include "signal_user.h" | ||
19 | #include "sigio.h" | ||
20 | #include "irq_user.h" | ||
21 | #include "os.h" | ||
22 | |||
23 | struct irq_fd { | ||
24 | struct irq_fd *next; | ||
25 | void *id; | ||
26 | int fd; | ||
27 | int type; | ||
28 | int irq; | ||
29 | int pid; | ||
30 | int events; | ||
31 | int current_events; | ||
32 | int freed; | ||
33 | }; | ||
34 | |||
35 | static struct irq_fd *active_fds = NULL; | ||
36 | static struct irq_fd **last_irq_ptr = &active_fds; | ||
37 | |||
38 | static struct pollfd *pollfds = NULL; | ||
39 | static int pollfds_num = 0; | ||
40 | static int pollfds_size = 0; | ||
41 | |||
42 | extern int io_count, intr_count; | ||
43 | |||
44 | void sigio_handler(int sig, union uml_pt_regs *regs) | ||
45 | { | ||
46 | struct irq_fd *irq_fd, *next; | ||
47 | int i, n; | ||
48 | |||
49 | if(smp_sigio_handler()) return; | ||
50 | while(1){ | ||
51 | n = poll(pollfds, pollfds_num, 0); | ||
52 | if(n < 0){ | ||
53 | if(errno == EINTR) continue; | ||
54 | printk("sigio_handler : poll returned %d, " | ||
55 | "errno = %d\n", n, errno); | ||
56 | break; | ||
57 | } | ||
58 | if(n == 0) break; | ||
59 | |||
60 | irq_fd = active_fds; | ||
61 | for(i = 0; i < pollfds_num; i++){ | ||
62 | if(pollfds[i].revents != 0){ | ||
63 | irq_fd->current_events = pollfds[i].revents; | ||
64 | pollfds[i].fd = -1; | ||
65 | } | ||
66 | irq_fd = irq_fd->next; | ||
67 | } | ||
68 | |||
69 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){ | ||
70 | next = irq_fd->next; | ||
71 | if(irq_fd->current_events != 0){ | ||
72 | irq_fd->current_events = 0; | ||
73 | do_IRQ(irq_fd->irq, regs); | ||
74 | |||
75 | /* This is here because the next irq may be | ||
76 | * freed in the handler. If a console goes | ||
77 | * away, both the read and write irqs will be | ||
78 | * freed. After do_IRQ, ->next will point to | ||
79 | * a good IRQ. | ||
80 | * Irqs can't be freed inside their handlers, | ||
81 | * so the next best thing is to have them | ||
82 | * marked as needing freeing, so that they | ||
83 | * can be freed here. | ||
84 | */ | ||
85 | next = irq_fd->next; | ||
86 | if(irq_fd->freed){ | ||
87 | free_irq(irq_fd->irq, irq_fd->id); | ||
88 | free_irq_by_irq_and_dev(irq_fd->irq, | ||
89 | irq_fd->id); | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | int activate_ipi(int fd, int pid) | ||
97 | { | ||
98 | return(os_set_fd_async(fd, pid)); | ||
99 | } | ||
100 | |||
101 | static void maybe_sigio_broken(int fd, int type) | ||
102 | { | ||
103 | if(isatty(fd)){ | ||
104 | if((type == IRQ_WRITE) && !pty_output_sigio){ | ||
105 | write_sigio_workaround(); | ||
106 | add_sigio_fd(fd, 0); | ||
107 | } | ||
108 | else if((type == IRQ_READ) && !pty_close_sigio){ | ||
109 | write_sigio_workaround(); | ||
110 | add_sigio_fd(fd, 1); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | int activate_fd(int irq, int fd, int type, void *dev_id) | ||
116 | { | ||
117 | struct pollfd *tmp_pfd; | ||
118 | struct irq_fd *new_fd, *irq_fd; | ||
119 | unsigned long flags; | ||
120 | int pid, events, err, n, size; | ||
121 | |||
122 | pid = os_getpid(); | ||
123 | err = os_set_fd_async(fd, pid); | ||
124 | if(err < 0) | ||
125 | goto out; | ||
126 | |||
127 | new_fd = um_kmalloc(sizeof(*new_fd)); | ||
128 | err = -ENOMEM; | ||
129 | if(new_fd == NULL) | ||
130 | goto out; | ||
131 | |||
132 | if(type == IRQ_READ) events = POLLIN | POLLPRI; | ||
133 | else events = POLLOUT; | ||
134 | *new_fd = ((struct irq_fd) { .next = NULL, | ||
135 | .id = dev_id, | ||
136 | .fd = fd, | ||
137 | .type = type, | ||
138 | .irq = irq, | ||
139 | .pid = pid, | ||
140 | .events = events, | ||
141 | .current_events = 0, | ||
142 | .freed = 0 } ); | ||
143 | |||
144 | /* Critical section - locked by a spinlock because this stuff can | ||
145 | * be changed from interrupt handlers. The stuff above is done | ||
146 | * outside the lock because it allocates memory. | ||
147 | */ | ||
148 | |||
149 | /* Actually, it only looks like it can be called from interrupt | ||
150 | * context. The culprit is reactivate_fd, which calls | ||
151 | * maybe_sigio_broken, which calls write_sigio_workaround, | ||
152 | * which calls activate_fd. However, write_sigio_workaround should | ||
153 | * only be called once, at boot time. That would make it clear that | ||
154 | * this is called only from process context, and can be locked with | ||
155 | * a semaphore. | ||
156 | */ | ||
157 | flags = irq_lock(); | ||
158 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
159 | if((irq_fd->fd == fd) && (irq_fd->type == type)){ | ||
160 | printk("Registering fd %d twice\n", fd); | ||
161 | printk("Irqs : %d, %d\n", irq_fd->irq, irq); | ||
162 | printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id); | ||
163 | goto out_unlock; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | n = pollfds_num; | ||
168 | if(n == pollfds_size){ | ||
169 | while(1){ | ||
170 | /* Here we have to drop the lock in order to call | ||
171 | * kmalloc, which might sleep. If something else | ||
172 | * came in and changed the pollfds array, we free | ||
173 | * the buffer and try again. | ||
174 | */ | ||
175 | irq_unlock(flags); | ||
176 | size = (pollfds_num + 1) * sizeof(pollfds[0]); | ||
177 | tmp_pfd = um_kmalloc(size); | ||
178 | flags = irq_lock(); | ||
179 | if(tmp_pfd == NULL) | ||
180 | goto out_unlock; | ||
181 | if(n == pollfds_size) | ||
182 | break; | ||
183 | kfree(tmp_pfd); | ||
184 | } | ||
185 | if(pollfds != NULL){ | ||
186 | memcpy(tmp_pfd, pollfds, | ||
187 | sizeof(pollfds[0]) * pollfds_size); | ||
188 | kfree(pollfds); | ||
189 | } | ||
190 | pollfds = tmp_pfd; | ||
191 | pollfds_size++; | ||
192 | } | ||
193 | |||
194 | if(type == IRQ_WRITE) | ||
195 | fd = -1; | ||
196 | |||
197 | pollfds[pollfds_num] = ((struct pollfd) { .fd = fd, | ||
198 | .events = events, | ||
199 | .revents = 0 }); | ||
200 | pollfds_num++; | ||
201 | |||
202 | *last_irq_ptr = new_fd; | ||
203 | last_irq_ptr = &new_fd->next; | ||
204 | |||
205 | irq_unlock(flags); | ||
206 | |||
207 | /* This calls activate_fd, so it has to be outside the critical | ||
208 | * section. | ||
209 | */ | ||
210 | maybe_sigio_broken(fd, type); | ||
211 | |||
212 | return(0); | ||
213 | |||
214 | out_unlock: | ||
215 | irq_unlock(flags); | ||
216 | kfree(new_fd); | ||
217 | out: | ||
218 | return(err); | ||
219 | } | ||
220 | |||
221 | static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) | ||
222 | { | ||
223 | struct irq_fd **prev; | ||
224 | unsigned long flags; | ||
225 | int i = 0; | ||
226 | |||
227 | flags = irq_lock(); | ||
228 | prev = &active_fds; | ||
229 | while(*prev != NULL){ | ||
230 | if((*test)(*prev, arg)){ | ||
231 | struct irq_fd *old_fd = *prev; | ||
232 | if((pollfds[i].fd != -1) && | ||
233 | (pollfds[i].fd != (*prev)->fd)){ | ||
234 | printk("free_irq_by_cb - mismatch between " | ||
235 | "active_fds and pollfds, fd %d vs %d\n", | ||
236 | (*prev)->fd, pollfds[i].fd); | ||
237 | goto out; | ||
238 | } | ||
239 | memcpy(&pollfds[i], &pollfds[i + 1], | ||
240 | (pollfds_num - i - 1) * sizeof(pollfds[0])); | ||
241 | pollfds_num--; | ||
242 | if(last_irq_ptr == &old_fd->next) | ||
243 | last_irq_ptr = prev; | ||
244 | *prev = (*prev)->next; | ||
245 | if(old_fd->type == IRQ_WRITE) | ||
246 | ignore_sigio_fd(old_fd->fd); | ||
247 | kfree(old_fd); | ||
248 | continue; | ||
249 | } | ||
250 | prev = &(*prev)->next; | ||
251 | i++; | ||
252 | } | ||
253 | out: | ||
254 | irq_unlock(flags); | ||
255 | } | ||
256 | |||
257 | struct irq_and_dev { | ||
258 | int irq; | ||
259 | void *dev; | ||
260 | }; | ||
261 | |||
262 | static int same_irq_and_dev(struct irq_fd *irq, void *d) | ||
263 | { | ||
264 | struct irq_and_dev *data = d; | ||
265 | |||
266 | return((irq->irq == data->irq) && (irq->id == data->dev)); | ||
267 | } | ||
268 | |||
269 | void free_irq_by_irq_and_dev(unsigned int irq, void *dev) | ||
270 | { | ||
271 | struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq, | ||
272 | .dev = dev }); | ||
273 | |||
274 | free_irq_by_cb(same_irq_and_dev, &data); | ||
275 | } | ||
276 | |||
277 | static int same_fd(struct irq_fd *irq, void *fd) | ||
278 | { | ||
279 | return(irq->fd == *((int *) fd)); | ||
280 | } | ||
281 | |||
282 | void free_irq_by_fd(int fd) | ||
283 | { | ||
284 | free_irq_by_cb(same_fd, &fd); | ||
285 | } | ||
286 | |||
287 | static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) | ||
288 | { | ||
289 | struct irq_fd *irq; | ||
290 | int i = 0; | ||
291 | |||
292 | for(irq=active_fds; irq != NULL; irq = irq->next){ | ||
293 | if((irq->fd == fd) && (irq->irq == irqnum)) break; | ||
294 | i++; | ||
295 | } | ||
296 | if(irq == NULL){ | ||
297 | printk("find_irq_by_fd doesn't have descriptor %d\n", fd); | ||
298 | goto out; | ||
299 | } | ||
300 | if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){ | ||
301 | printk("find_irq_by_fd - mismatch between active_fds and " | ||
302 | "pollfds, fd %d vs %d, need %d\n", irq->fd, | ||
303 | pollfds[i].fd, fd); | ||
304 | irq = NULL; | ||
305 | goto out; | ||
306 | } | ||
307 | *index_out = i; | ||
308 | out: | ||
309 | return(irq); | ||
310 | } | ||
311 | |||
312 | void free_irq_later(int irq, void *dev_id) | ||
313 | { | ||
314 | struct irq_fd *irq_fd; | ||
315 | unsigned long flags; | ||
316 | |||
317 | flags = irq_lock(); | ||
318 | for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ | ||
319 | if((irq_fd->irq == irq) && (irq_fd->id == dev_id)) | ||
320 | break; | ||
321 | } | ||
322 | if(irq_fd == NULL){ | ||
323 | printk("free_irq_later found no irq, irq = %d, " | ||
324 | "dev_id = 0x%p\n", irq, dev_id); | ||
325 | goto out; | ||
326 | } | ||
327 | irq_fd->freed = 1; | ||
328 | out: | ||
329 | irq_unlock(flags); | ||
330 | } | ||
331 | |||
332 | void reactivate_fd(int fd, int irqnum) | ||
333 | { | ||
334 | struct irq_fd *irq; | ||
335 | unsigned long flags; | ||
336 | int i; | ||
337 | |||
338 | flags = irq_lock(); | ||
339 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
340 | if(irq == NULL){ | ||
341 | irq_unlock(flags); | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | pollfds[i].fd = irq->fd; | ||
346 | |||
347 | irq_unlock(flags); | ||
348 | |||
349 | /* This calls activate_fd, so it has to be outside the critical | ||
350 | * section. | ||
351 | */ | ||
352 | maybe_sigio_broken(fd, irq->type); | ||
353 | } | ||
354 | |||
355 | void deactivate_fd(int fd, int irqnum) | ||
356 | { | ||
357 | struct irq_fd *irq; | ||
358 | unsigned long flags; | ||
359 | int i; | ||
360 | |||
361 | flags = irq_lock(); | ||
362 | irq = find_irq_by_fd(fd, irqnum, &i); | ||
363 | if(irq == NULL) | ||
364 | goto out; | ||
365 | pollfds[i].fd = -1; | ||
366 | out: | ||
367 | irq_unlock(flags); | ||
368 | } | ||
369 | |||
370 | int deactivate_all_fds(void) | ||
371 | { | ||
372 | struct irq_fd *irq; | ||
373 | int err; | ||
374 | |||
375 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
376 | err = os_clear_fd_async(irq->fd); | ||
377 | if(err) | ||
378 | return(err); | ||
379 | } | ||
380 | /* If there is a signal already queued, after unblocking ignore it */ | ||
381 | set_handler(SIGIO, SIG_IGN, 0, -1); | ||
382 | |||
383 | return(0); | ||
384 | } | ||
385 | |||
386 | void forward_ipi(int fd, int pid) | ||
387 | { | ||
388 | int err; | ||
389 | |||
390 | err = os_set_owner(fd, pid); | ||
391 | if(err < 0) | ||
392 | printk("forward_ipi: set_owner failed, fd = %d, me = %d, " | ||
393 | "target = %d, err = %d\n", fd, os_getpid(), pid, -err); | ||
394 | } | ||
395 | |||
396 | void forward_interrupts(int pid) | ||
397 | { | ||
398 | struct irq_fd *irq; | ||
399 | unsigned long flags; | ||
400 | int err; | ||
401 | |||
402 | flags = irq_lock(); | ||
403 | for(irq=active_fds;irq != NULL;irq = irq->next){ | ||
404 | err = os_set_owner(irq->fd, pid); | ||
405 | if(err < 0){ | ||
406 | /* XXX Just remove the irq rather than | ||
407 | * print out an infinite stream of these | ||
408 | */ | ||
409 | printk("Failed to forward %d to pid %d, err = %d\n", | ||
410 | irq->fd, pid, -err); | ||
411 | } | ||
412 | |||
413 | irq->pid = pid; | ||
414 | } | ||
415 | irq_unlock(flags); | ||
416 | } | ||
417 | |||
418 | void init_irq_signals(int on_sigstack) | ||
419 | { | ||
420 | __sighandler_t h; | ||
421 | int flags; | ||
422 | |||
423 | flags = on_sigstack ? SA_ONSTACK : 0; | ||
424 | if(timer_irq_inited) h = (__sighandler_t) alarm_handler; | ||
425 | else h = boot_timer_handler; | ||
426 | |||
427 | set_handler(SIGVTALRM, h, flags | SA_RESTART, | ||
428 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1); | ||
429 | set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART, | ||
430 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
431 | signal(SIGWINCH, SIG_IGN); | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
436 | * Emacs will notice this stuff at the end of the file and automatically | ||
437 | * adjust the settings for this buffer only. This must remain at the end | ||
438 | * of the file. | ||
439 | * --------------------------------------------------------------------------- | ||
440 | * Local variables: | ||
441 | * c-file-style: "linux" | ||
442 | * End: | ||
443 | */ | ||
diff --git a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c new file mode 100644 index 000000000000..b41d3397d07b --- /dev/null +++ b/arch/um/kernel/ksyms.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/module.h" | ||
8 | #include "linux/string.h" | ||
9 | #include "linux/smp_lock.h" | ||
10 | #include "linux/spinlock.h" | ||
11 | #include "linux/highmem.h" | ||
12 | #include "asm/current.h" | ||
13 | #include "asm/delay.h" | ||
14 | #include "asm/processor.h" | ||
15 | #include "asm/unistd.h" | ||
16 | #include "asm/pgalloc.h" | ||
17 | #include "asm/pgtable.h" | ||
18 | #include "asm/page.h" | ||
19 | #include "asm/tlbflush.h" | ||
20 | #include "kern_util.h" | ||
21 | #include "user_util.h" | ||
22 | #include "mem_user.h" | ||
23 | #include "os.h" | ||
24 | #include "helper.h" | ||
25 | |||
26 | EXPORT_SYMBOL(stop); | ||
27 | EXPORT_SYMBOL(uml_physmem); | ||
28 | EXPORT_SYMBOL(set_signals); | ||
29 | EXPORT_SYMBOL(get_signals); | ||
30 | EXPORT_SYMBOL(kernel_thread); | ||
31 | EXPORT_SYMBOL(__const_udelay); | ||
32 | EXPORT_SYMBOL(__udelay); | ||
33 | EXPORT_SYMBOL(sys_waitpid); | ||
34 | EXPORT_SYMBOL(task_size); | ||
35 | EXPORT_SYMBOL(flush_tlb_range); | ||
36 | EXPORT_SYMBOL(host_task_size); | ||
37 | EXPORT_SYMBOL(arch_validate); | ||
38 | EXPORT_SYMBOL(get_kmem_end); | ||
39 | |||
40 | EXPORT_SYMBOL(page_to_phys); | ||
41 | EXPORT_SYMBOL(phys_to_page); | ||
42 | EXPORT_SYMBOL(high_physmem); | ||
43 | EXPORT_SYMBOL(empty_zero_page); | ||
44 | EXPORT_SYMBOL(um_virt_to_phys); | ||
45 | EXPORT_SYMBOL(__virt_to_page); | ||
46 | EXPORT_SYMBOL(to_phys); | ||
47 | EXPORT_SYMBOL(to_virt); | ||
48 | EXPORT_SYMBOL(mode_tt); | ||
49 | EXPORT_SYMBOL(handle_page_fault); | ||
50 | EXPORT_SYMBOL(find_iomem); | ||
51 | EXPORT_SYMBOL(end_iomem); | ||
52 | |||
53 | #ifdef CONFIG_MODE_TT | ||
54 | EXPORT_SYMBOL(strncpy_from_user_tt); | ||
55 | EXPORT_SYMBOL(copy_from_user_tt); | ||
56 | EXPORT_SYMBOL(copy_to_user_tt); | ||
57 | #endif | ||
58 | |||
59 | #ifdef CONFIG_MODE_SKAS | ||
60 | EXPORT_SYMBOL(strncpy_from_user_skas); | ||
61 | EXPORT_SYMBOL(copy_to_user_skas); | ||
62 | EXPORT_SYMBOL(copy_from_user_skas); | ||
63 | #endif | ||
64 | EXPORT_SYMBOL(uml_strdup); | ||
65 | |||
66 | EXPORT_SYMBOL(os_stat_fd); | ||
67 | EXPORT_SYMBOL(os_stat_file); | ||
68 | EXPORT_SYMBOL(os_access); | ||
69 | EXPORT_SYMBOL(os_print_error); | ||
70 | EXPORT_SYMBOL(os_get_exec_close); | ||
71 | EXPORT_SYMBOL(os_set_exec_close); | ||
72 | EXPORT_SYMBOL(os_getpid); | ||
73 | EXPORT_SYMBOL(os_open_file); | ||
74 | EXPORT_SYMBOL(os_read_file); | ||
75 | EXPORT_SYMBOL(os_write_file); | ||
76 | EXPORT_SYMBOL(os_seek_file); | ||
77 | EXPORT_SYMBOL(os_lock_file); | ||
78 | EXPORT_SYMBOL(os_ioctl_generic); | ||
79 | EXPORT_SYMBOL(os_pipe); | ||
80 | EXPORT_SYMBOL(os_file_type); | ||
81 | EXPORT_SYMBOL(os_file_mode); | ||
82 | EXPORT_SYMBOL(os_file_size); | ||
83 | EXPORT_SYMBOL(os_flush_stdout); | ||
84 | EXPORT_SYMBOL(os_close_file); | ||
85 | EXPORT_SYMBOL(os_set_fd_async); | ||
86 | EXPORT_SYMBOL(os_set_fd_block); | ||
87 | EXPORT_SYMBOL(helper_wait); | ||
88 | EXPORT_SYMBOL(os_shutdown_socket); | ||
89 | EXPORT_SYMBOL(os_create_unix_socket); | ||
90 | EXPORT_SYMBOL(os_connect_socket); | ||
91 | EXPORT_SYMBOL(os_accept_connection); | ||
92 | EXPORT_SYMBOL(os_rcv_fd); | ||
93 | EXPORT_SYMBOL(run_helper); | ||
94 | EXPORT_SYMBOL(start_thread); | ||
95 | EXPORT_SYMBOL(dump_thread); | ||
96 | |||
97 | EXPORT_SYMBOL(do_gettimeofday); | ||
98 | EXPORT_SYMBOL(do_settimeofday); | ||
99 | |||
100 | /* This is here because UML expands open to sys_open, not to a system | ||
101 | * call instruction. | ||
102 | */ | ||
103 | EXPORT_SYMBOL(sys_open); | ||
104 | EXPORT_SYMBOL(sys_lseek); | ||
105 | EXPORT_SYMBOL(sys_read); | ||
106 | EXPORT_SYMBOL(sys_wait4); | ||
107 | |||
108 | #ifdef CONFIG_SMP | ||
109 | |||
110 | /* required for SMP */ | ||
111 | |||
112 | extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); | ||
113 | EXPORT_SYMBOL(__write_lock_failed); | ||
114 | |||
115 | extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); | ||
116 | EXPORT_SYMBOL(__read_lock_failed); | ||
117 | |||
118 | #endif | ||
119 | |||
120 | #ifdef CONFIG_HIGHMEM | ||
121 | EXPORT_SYMBOL(kmap); | ||
122 | EXPORT_SYMBOL(kunmap); | ||
123 | EXPORT_SYMBOL(kmap_atomic); | ||
124 | EXPORT_SYMBOL(kunmap_atomic); | ||
125 | EXPORT_SYMBOL(kmap_atomic_to_page); | ||
126 | #endif | ||
127 | |||
128 | /* | ||
129 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
130 | * Emacs will notice this stuff at the end of the file and automatically | ||
131 | * adjust the settings for this buffer only. This must remain at the end | ||
132 | * of the file. | ||
133 | * --------------------------------------------------------------------------- | ||
134 | * Local variables: | ||
135 | * c-file-style: "linux" | ||
136 | * End: | ||
137 | */ | ||
diff --git a/arch/um/kernel/main.c b/arch/um/kernel/main.c new file mode 100644 index 000000000000..a17c49703f9b --- /dev/null +++ b/arch/um/kernel/main.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <string.h> | ||
10 | #include <signal.h> | ||
11 | #include <errno.h> | ||
12 | #include <sys/resource.h> | ||
13 | #include <sys/mman.h> | ||
14 | #include <sys/user.h> | ||
15 | #include <asm/page.h> | ||
16 | #include "user_util.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "mem_user.h" | ||
19 | #include "signal_user.h" | ||
20 | #include "time_user.h" | ||
21 | #include "irq_user.h" | ||
22 | #include "user.h" | ||
23 | #include "init.h" | ||
24 | #include "mode.h" | ||
25 | #include "choose-mode.h" | ||
26 | #include "uml-config.h" | ||
27 | #include "irq_user.h" | ||
28 | #include "time_user.h" | ||
29 | #include "os.h" | ||
30 | |||
31 | /* Set in set_stklim, which is called from main and __wrap_malloc. | ||
32 | * __wrap_malloc only calls it if main hasn't started. | ||
33 | */ | ||
34 | unsigned long stacksizelim; | ||
35 | |||
36 | /* Set in main */ | ||
37 | char *linux_prog; | ||
38 | |||
39 | #define PGD_BOUND (4 * 1024 * 1024) | ||
40 | #define STACKSIZE (8 * 1024 * 1024) | ||
41 | #define THREAD_NAME_LEN (256) | ||
42 | |||
43 | static void set_stklim(void) | ||
44 | { | ||
45 | struct rlimit lim; | ||
46 | |||
47 | if(getrlimit(RLIMIT_STACK, &lim) < 0){ | ||
48 | perror("getrlimit"); | ||
49 | exit(1); | ||
50 | } | ||
51 | if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ | ||
52 | lim.rlim_cur = STACKSIZE; | ||
53 | if(setrlimit(RLIMIT_STACK, &lim) < 0){ | ||
54 | perror("setrlimit"); | ||
55 | exit(1); | ||
56 | } | ||
57 | } | ||
58 | stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); | ||
59 | } | ||
60 | |||
61 | static __init void do_uml_initcalls(void) | ||
62 | { | ||
63 | initcall_t *call; | ||
64 | |||
65 | call = &__uml_initcall_start; | ||
66 | while (call < &__uml_initcall_end){; | ||
67 | (*call)(); | ||
68 | call++; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static void last_ditch_exit(int sig) | ||
73 | { | ||
74 | CHOOSE_MODE(kmalloc_ok = 0, (void) 0); | ||
75 | signal(SIGINT, SIG_DFL); | ||
76 | signal(SIGTERM, SIG_DFL); | ||
77 | signal(SIGHUP, SIG_DFL); | ||
78 | uml_cleanup(); | ||
79 | exit(1); | ||
80 | } | ||
81 | |||
82 | extern int uml_exitcode; | ||
83 | |||
84 | extern void scan_elf_aux( char **envp); | ||
85 | |||
86 | int main(int argc, char **argv, char **envp) | ||
87 | { | ||
88 | char **new_argv; | ||
89 | sigset_t mask; | ||
90 | int ret, i; | ||
91 | |||
92 | /* Enable all signals except SIGIO - in some environments, we can | ||
93 | * enter with some signals blocked | ||
94 | */ | ||
95 | |||
96 | sigemptyset(&mask); | ||
97 | sigaddset(&mask, SIGIO); | ||
98 | if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){ | ||
99 | perror("sigprocmask"); | ||
100 | exit(1); | ||
101 | } | ||
102 | |||
103 | #ifdef UML_CONFIG_MODE_TT | ||
104 | /* Allocate memory for thread command lines */ | ||
105 | if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ | ||
106 | |||
107 | char padding[THREAD_NAME_LEN] = { | ||
108 | [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' | ||
109 | }; | ||
110 | |||
111 | new_argv = malloc((argc + 2) * sizeof(char*)); | ||
112 | if(!new_argv) { | ||
113 | perror("Allocating extended argv"); | ||
114 | exit(1); | ||
115 | } | ||
116 | |||
117 | new_argv[0] = argv[0]; | ||
118 | new_argv[1] = padding; | ||
119 | |||
120 | for(i = 2; i <= argc; i++) | ||
121 | new_argv[i] = argv[i - 1]; | ||
122 | new_argv[argc + 1] = NULL; | ||
123 | |||
124 | execvp(new_argv[0], new_argv); | ||
125 | perror("execing with extended args"); | ||
126 | exit(1); | ||
127 | } | ||
128 | #endif | ||
129 | |||
130 | linux_prog = argv[0]; | ||
131 | |||
132 | set_stklim(); | ||
133 | |||
134 | new_argv = malloc((argc + 1) * sizeof(char *)); | ||
135 | if(new_argv == NULL){ | ||
136 | perror("Mallocing argv"); | ||
137 | exit(1); | ||
138 | } | ||
139 | for(i=0;i<argc;i++){ | ||
140 | new_argv[i] = strdup(argv[i]); | ||
141 | if(new_argv[i] == NULL){ | ||
142 | perror("Mallocing an arg"); | ||
143 | exit(1); | ||
144 | } | ||
145 | } | ||
146 | new_argv[argc] = NULL; | ||
147 | |||
148 | set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); | ||
149 | set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); | ||
150 | set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); | ||
151 | |||
152 | scan_elf_aux( envp); | ||
153 | |||
154 | do_uml_initcalls(); | ||
155 | ret = linux_main(argc, argv); | ||
156 | |||
157 | /* Disable SIGPROF - I have no idea why libc doesn't do this or turn | ||
158 | * off the profiling time, but UML dies with a SIGPROF just before | ||
159 | * exiting when profiling is active. | ||
160 | */ | ||
161 | change_sig(SIGPROF, 0); | ||
162 | |||
163 | /* Reboot */ | ||
164 | if(ret){ | ||
165 | int err; | ||
166 | |||
167 | printf("\n"); | ||
168 | |||
169 | /* stop timers and set SIG*ALRM to be ignored */ | ||
170 | disable_timer(); | ||
171 | |||
172 | /* disable SIGIO for the fds and set SIGIO to be ignored */ | ||
173 | err = deactivate_all_fds(); | ||
174 | if(err) | ||
175 | printf("deactivate_all_fds failed, errno = %d\n", | ||
176 | -err); | ||
177 | |||
178 | /* Let any pending signals fire now. This ensures | ||
179 | * that they won't be delivered after the exec, when | ||
180 | * they are definitely not expected. | ||
181 | */ | ||
182 | unblock_signals(); | ||
183 | |||
184 | execvp(new_argv[0], new_argv); | ||
185 | perror("Failed to exec kernel"); | ||
186 | ret = 1; | ||
187 | } | ||
188 | printf("\n"); | ||
189 | return(uml_exitcode); | ||
190 | } | ||
191 | |||
192 | #define CAN_KMALLOC() \ | ||
193 | (kmalloc_ok && CHOOSE_MODE((os_getpid() != tracing_pid), 1)) | ||
194 | |||
195 | extern void *__real_malloc(int); | ||
196 | |||
197 | void *__wrap_malloc(int size) | ||
198 | { | ||
199 | void *ret; | ||
200 | |||
201 | if(!CAN_KMALLOC()) | ||
202 | return(__real_malloc(size)); | ||
203 | else if(size <= PAGE_SIZE) /* finding contiguos pages can be hard*/ | ||
204 | ret = um_kmalloc(size); | ||
205 | else ret = um_vmalloc(size); | ||
206 | |||
207 | /* glibc people insist that if malloc fails, errno should be | ||
208 | * set by malloc as well. So we do. | ||
209 | */ | ||
210 | if(ret == NULL) | ||
211 | errno = ENOMEM; | ||
212 | |||
213 | return(ret); | ||
214 | } | ||
215 | |||
216 | void *__wrap_calloc(int n, int size) | ||
217 | { | ||
218 | void *ptr = __wrap_malloc(n * size); | ||
219 | |||
220 | if(ptr == NULL) return(NULL); | ||
221 | memset(ptr, 0, n * size); | ||
222 | return(ptr); | ||
223 | } | ||
224 | |||
225 | extern void __real_free(void *); | ||
226 | |||
227 | extern unsigned long high_physmem; | ||
228 | |||
229 | void __wrap_free(void *ptr) | ||
230 | { | ||
231 | unsigned long addr = (unsigned long) ptr; | ||
232 | |||
233 | /* We need to know how the allocation happened, so it can be correctly | ||
234 | * freed. This is done by seeing what region of memory the pointer is | ||
235 | * in - | ||
236 | * physical memory - kmalloc/kfree | ||
237 | * kernel virtual memory - vmalloc/vfree | ||
238 | * anywhere else - malloc/free | ||
239 | * If kmalloc is not yet possible, then either high_physmem and/or | ||
240 | * end_vm are still 0 (as at startup), in which case we call free, or | ||
241 | * we have set them, but anyway addr has not been allocated from those | ||
242 | * areas. So, in both cases __real_free is called. | ||
243 | * | ||
244 | * CAN_KMALLOC is checked because it would be bad to free a buffer | ||
245 | * with kmalloc/vmalloc after they have been turned off during | ||
246 | * shutdown. | ||
247 | * XXX: However, we sometimes shutdown CAN_KMALLOC temporarily, so | ||
248 | * there is a possibility for memory leaks. | ||
249 | */ | ||
250 | |||
251 | if((addr >= uml_physmem) && (addr < high_physmem)){ | ||
252 | if(CAN_KMALLOC()) | ||
253 | kfree(ptr); | ||
254 | } | ||
255 | else if((addr >= start_vm) && (addr < end_vm)){ | ||
256 | if(CAN_KMALLOC()) | ||
257 | vfree(ptr); | ||
258 | } | ||
259 | else __real_free(ptr); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
264 | * Emacs will notice this stuff at the end of the file and automatically | ||
265 | * adjust the settings for this buffer only. This must remain at the end | ||
266 | * of the file. | ||
267 | * --------------------------------------------------------------------------- | ||
268 | * Local variables: | ||
269 | * c-file-style: "linux" | ||
270 | * End: | ||
271 | */ | ||
diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c new file mode 100644 index 000000000000..f156661781cb --- /dev/null +++ b/arch/um/kernel/mem.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/stddef.h" | ||
7 | #include "linux/kernel.h" | ||
8 | #include "linux/mm.h" | ||
9 | #include "linux/bootmem.h" | ||
10 | #include "linux/swap.h" | ||
11 | #include "linux/highmem.h" | ||
12 | #include "linux/gfp.h" | ||
13 | #include "asm/page.h" | ||
14 | #include "asm/fixmap.h" | ||
15 | #include "asm/pgalloc.h" | ||
16 | #include "user_util.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "kern.h" | ||
19 | #include "mem_user.h" | ||
20 | #include "uml_uaccess.h" | ||
21 | #include "os.h" | ||
22 | |||
23 | extern char __binary_start; | ||
24 | |||
25 | /* Changed during early boot */ | ||
26 | unsigned long *empty_zero_page = NULL; | ||
27 | unsigned long *empty_bad_page = NULL; | ||
28 | pgd_t swapper_pg_dir[PTRS_PER_PGD]; | ||
29 | unsigned long highmem; | ||
30 | int kmalloc_ok = 0; | ||
31 | |||
32 | static unsigned long brk_end; | ||
33 | |||
34 | void unmap_physmem(void) | ||
35 | { | ||
36 | os_unmap_memory((void *) brk_end, uml_reserved - brk_end); | ||
37 | } | ||
38 | |||
39 | static void map_cb(void *unused) | ||
40 | { | ||
41 | map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0); | ||
42 | } | ||
43 | |||
44 | #ifdef CONFIG_HIGHMEM | ||
45 | static void setup_highmem(unsigned long highmem_start, | ||
46 | unsigned long highmem_len) | ||
47 | { | ||
48 | struct page *page; | ||
49 | unsigned long highmem_pfn; | ||
50 | int i; | ||
51 | |||
52 | highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT; | ||
53 | for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){ | ||
54 | page = &mem_map[highmem_pfn + i]; | ||
55 | ClearPageReserved(page); | ||
56 | set_bit(PG_highmem, &page->flags); | ||
57 | set_page_count(page, 1); | ||
58 | __free_page(page); | ||
59 | } | ||
60 | } | ||
61 | #endif | ||
62 | |||
63 | void mem_init(void) | ||
64 | { | ||
65 | unsigned long start; | ||
66 | |||
67 | max_low_pfn = (high_physmem - uml_physmem) >> PAGE_SHIFT; | ||
68 | |||
69 | /* clear the zero-page */ | ||
70 | memset((void *) empty_zero_page, 0, PAGE_SIZE); | ||
71 | |||
72 | /* Map in the area just after the brk now that kmalloc is about | ||
73 | * to be turned on. | ||
74 | */ | ||
75 | brk_end = (unsigned long) UML_ROUND_UP(sbrk(0)); | ||
76 | map_cb(NULL); | ||
77 | initial_thread_cb(map_cb, NULL); | ||
78 | free_bootmem(__pa(brk_end), uml_reserved - brk_end); | ||
79 | uml_reserved = brk_end; | ||
80 | |||
81 | /* Fill in any hole at the start of the binary */ | ||
82 | start = (unsigned long) &__binary_start & PAGE_MASK; | ||
83 | if(uml_physmem != start){ | ||
84 | map_memory(uml_physmem, __pa(uml_physmem), start - uml_physmem, | ||
85 | 1, 1, 0); | ||
86 | } | ||
87 | |||
88 | /* this will put all low memory onto the freelists */ | ||
89 | totalram_pages = free_all_bootmem(); | ||
90 | totalhigh_pages = highmem >> PAGE_SHIFT; | ||
91 | totalram_pages += totalhigh_pages; | ||
92 | num_physpages = totalram_pages; | ||
93 | max_pfn = totalram_pages; | ||
94 | printk(KERN_INFO "Memory: %luk available\n", | ||
95 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); | ||
96 | kmalloc_ok = 1; | ||
97 | |||
98 | #ifdef CONFIG_HIGHMEM | ||
99 | setup_highmem(end_iomem, highmem); | ||
100 | #endif | ||
101 | } | ||
102 | |||
103 | static void __init fixrange_init(unsigned long start, unsigned long end, | ||
104 | pgd_t *pgd_base) | ||
105 | { | ||
106 | pgd_t *pgd; | ||
107 | pmd_t *pmd; | ||
108 | pte_t *pte; | ||
109 | int i, j; | ||
110 | unsigned long vaddr; | ||
111 | |||
112 | vaddr = start; | ||
113 | i = pgd_index(vaddr); | ||
114 | j = pmd_index(vaddr); | ||
115 | pgd = pgd_base + i; | ||
116 | |||
117 | for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) { | ||
118 | pmd = (pmd_t *)pgd; | ||
119 | for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { | ||
120 | if (pmd_none(*pmd)) { | ||
121 | pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
122 | set_pmd(pmd, __pmd(_KERNPG_TABLE + | ||
123 | (unsigned long) __pa(pte))); | ||
124 | if (pte != pte_offset_kernel(pmd, 0)) | ||
125 | BUG(); | ||
126 | } | ||
127 | vaddr += PMD_SIZE; | ||
128 | } | ||
129 | j = 0; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | #ifdef CONFIG_HIGHMEM | ||
134 | pte_t *kmap_pte; | ||
135 | pgprot_t kmap_prot; | ||
136 | |||
137 | #define kmap_get_fixmap_pte(vaddr) \ | ||
138 | pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\ | ||
139 | (vaddr)), (vaddr)) | ||
140 | |||
141 | static void __init kmap_init(void) | ||
142 | { | ||
143 | unsigned long kmap_vstart; | ||
144 | |||
145 | /* cache the first kmap pte */ | ||
146 | kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); | ||
147 | kmap_pte = kmap_get_fixmap_pte(kmap_vstart); | ||
148 | |||
149 | kmap_prot = PAGE_KERNEL; | ||
150 | } | ||
151 | |||
152 | static void init_highmem(void) | ||
153 | { | ||
154 | pgd_t *pgd; | ||
155 | pud_t *pud; | ||
156 | pmd_t *pmd; | ||
157 | pte_t *pte; | ||
158 | unsigned long vaddr; | ||
159 | |||
160 | /* | ||
161 | * Permanent kmaps: | ||
162 | */ | ||
163 | vaddr = PKMAP_BASE; | ||
164 | fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir); | ||
165 | |||
166 | pgd = swapper_pg_dir + pgd_index(vaddr); | ||
167 | pud = pud_offset(pgd, vaddr); | ||
168 | pmd = pmd_offset(pud, vaddr); | ||
169 | pte = pte_offset_kernel(pmd, vaddr); | ||
170 | pkmap_page_table = pte; | ||
171 | |||
172 | kmap_init(); | ||
173 | } | ||
174 | #endif /* CONFIG_HIGHMEM */ | ||
175 | |||
176 | static void __init fixaddr_user_init( void) | ||
177 | { | ||
178 | #if CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA | ||
179 | long size = FIXADDR_USER_END - FIXADDR_USER_START; | ||
180 | pgd_t *pgd; | ||
181 | pud_t *pud; | ||
182 | pmd_t *pmd; | ||
183 | pte_t *pte; | ||
184 | unsigned long paddr, vaddr = FIXADDR_USER_START; | ||
185 | |||
186 | if ( ! size ) | ||
187 | return; | ||
188 | |||
189 | fixrange_init( FIXADDR_USER_START, FIXADDR_USER_END, swapper_pg_dir); | ||
190 | paddr = (unsigned long)alloc_bootmem_low_pages( size); | ||
191 | memcpy( (void *)paddr, (void *)FIXADDR_USER_START, size); | ||
192 | paddr = __pa(paddr); | ||
193 | for ( ; size > 0; size-=PAGE_SIZE, vaddr+=PAGE_SIZE, paddr+=PAGE_SIZE){ | ||
194 | pgd = swapper_pg_dir + pgd_index(vaddr); | ||
195 | pud = pud_offset(pgd, vaddr); | ||
196 | pmd = pmd_offset(pud, vaddr); | ||
197 | pte = pte_offset_kernel(pmd, vaddr); | ||
198 | pte_set_val( (*pte), paddr, PAGE_READONLY); | ||
199 | } | ||
200 | #endif | ||
201 | } | ||
202 | |||
203 | void paging_init(void) | ||
204 | { | ||
205 | unsigned long zones_size[MAX_NR_ZONES], vaddr; | ||
206 | int i; | ||
207 | |||
208 | empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
209 | empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
210 | for(i=0;i<sizeof(zones_size)/sizeof(zones_size[0]);i++) | ||
211 | zones_size[i] = 0; | ||
212 | zones_size[0] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT); | ||
213 | zones_size[2] = highmem >> PAGE_SHIFT; | ||
214 | free_area_init(zones_size); | ||
215 | |||
216 | /* | ||
217 | * Fixed mappings, only the page table structure has to be | ||
218 | * created - mappings will be set by set_fixmap(): | ||
219 | */ | ||
220 | vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; | ||
221 | fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir); | ||
222 | |||
223 | fixaddr_user_init(); | ||
224 | |||
225 | #ifdef CONFIG_HIGHMEM | ||
226 | init_highmem(); | ||
227 | #endif | ||
228 | } | ||
229 | |||
230 | struct page *arch_validate(struct page *page, int mask, int order) | ||
231 | { | ||
232 | unsigned long addr, zero = 0; | ||
233 | int i; | ||
234 | |||
235 | again: | ||
236 | if(page == NULL) return(page); | ||
237 | if(PageHighMem(page)) return(page); | ||
238 | |||
239 | addr = (unsigned long) page_address(page); | ||
240 | for(i = 0; i < (1 << order); i++){ | ||
241 | current->thread.fault_addr = (void *) addr; | ||
242 | if(__do_copy_to_user((void __user *) addr, &zero, | ||
243 | sizeof(zero), | ||
244 | ¤t->thread.fault_addr, | ||
245 | ¤t->thread.fault_catcher)){ | ||
246 | if(!(mask & __GFP_WAIT)) return(NULL); | ||
247 | else break; | ||
248 | } | ||
249 | addr += PAGE_SIZE; | ||
250 | } | ||
251 | |||
252 | if(i == (1 << order)) return(page); | ||
253 | page = alloc_pages(mask, order); | ||
254 | goto again; | ||
255 | } | ||
256 | |||
257 | /* This can't do anything because nothing in the kernel image can be freed | ||
258 | * since it's not in kernel physical memory. | ||
259 | */ | ||
260 | |||
261 | void free_initmem(void) | ||
262 | { | ||
263 | } | ||
264 | |||
265 | #ifdef CONFIG_BLK_DEV_INITRD | ||
266 | |||
267 | void free_initrd_mem(unsigned long start, unsigned long end) | ||
268 | { | ||
269 | if (start < end) | ||
270 | printk ("Freeing initrd memory: %ldk freed\n", | ||
271 | (end - start) >> 10); | ||
272 | for (; start < end; start += PAGE_SIZE) { | ||
273 | ClearPageReserved(virt_to_page(start)); | ||
274 | set_page_count(virt_to_page(start), 1); | ||
275 | free_page(start); | ||
276 | totalram_pages++; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | #endif | ||
281 | |||
282 | void show_mem(void) | ||
283 | { | ||
284 | int pfn, total = 0, reserved = 0; | ||
285 | int shared = 0, cached = 0; | ||
286 | int highmem = 0; | ||
287 | struct page *page; | ||
288 | |||
289 | printk("Mem-info:\n"); | ||
290 | show_free_areas(); | ||
291 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
292 | pfn = max_mapnr; | ||
293 | while(pfn-- > 0) { | ||
294 | page = pfn_to_page(pfn); | ||
295 | total++; | ||
296 | if(PageHighMem(page)) | ||
297 | highmem++; | ||
298 | if(PageReserved(page)) | ||
299 | reserved++; | ||
300 | else if(PageSwapCache(page)) | ||
301 | cached++; | ||
302 | else if(page_count(page)) | ||
303 | shared += page_count(page) - 1; | ||
304 | } | ||
305 | printk("%d pages of RAM\n", total); | ||
306 | printk("%d pages of HIGHMEM\n", highmem); | ||
307 | printk("%d reserved pages\n", reserved); | ||
308 | printk("%d pages shared\n", shared); | ||
309 | printk("%d pages swap cached\n", cached); | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Allocate and free page tables. | ||
314 | */ | ||
315 | |||
316 | pgd_t *pgd_alloc(struct mm_struct *mm) | ||
317 | { | ||
318 | pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL); | ||
319 | |||
320 | if (pgd) { | ||
321 | memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); | ||
322 | memcpy(pgd + USER_PTRS_PER_PGD, | ||
323 | swapper_pg_dir + USER_PTRS_PER_PGD, | ||
324 | (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); | ||
325 | } | ||
326 | return pgd; | ||
327 | } | ||
328 | |||
329 | void pgd_free(pgd_t *pgd) | ||
330 | { | ||
331 | free_page((unsigned long) pgd); | ||
332 | } | ||
333 | |||
334 | pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) | ||
335 | { | ||
336 | pte_t *pte; | ||
337 | |||
338 | pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); | ||
339 | return pte; | ||
340 | } | ||
341 | |||
342 | struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) | ||
343 | { | ||
344 | struct page *pte; | ||
345 | |||
346 | pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); | ||
347 | return pte; | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
352 | * Emacs will notice this stuff at the end of the file and automatically | ||
353 | * adjust the settings for this buffer only. This must remain at the end | ||
354 | * of the file. | ||
355 | * --------------------------------------------------------------------------- | ||
356 | * Local variables: | ||
357 | * c-file-style: "linux" | ||
358 | * End: | ||
359 | */ | ||
diff --git a/arch/um/kernel/mem_user.c b/arch/um/kernel/mem_user.c new file mode 100644 index 000000000000..4a663fd434bb --- /dev/null +++ b/arch/um/kernel/mem_user.c | |||
@@ -0,0 +1,273 @@ | |||
1 | /* | ||
2 | * arch/um/kernel/mem_user.c | ||
3 | * | ||
4 | * BRIEF MODULE DESCRIPTION | ||
5 | * user side memory routines for supporting IO memory inside user mode linux | ||
6 | * | ||
7 | * Copyright (C) 2001 RidgeRun, Inc. | ||
8 | * Author: RidgeRun, Inc. | ||
9 | * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | */ | ||
31 | |||
32 | #include <stdio.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <stddef.h> | ||
35 | #include <stdarg.h> | ||
36 | #include <unistd.h> | ||
37 | #include <errno.h> | ||
38 | #include <string.h> | ||
39 | #include <fcntl.h> | ||
40 | #include <sys/types.h> | ||
41 | #include <sys/mman.h> | ||
42 | #include "kern_util.h" | ||
43 | #include "user.h" | ||
44 | #include "user_util.h" | ||
45 | #include "mem_user.h" | ||
46 | #include "init.h" | ||
47 | #include "os.h" | ||
48 | #include "tempfile.h" | ||
49 | #include "kern_constants.h" | ||
50 | |||
51 | #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" | ||
52 | |||
53 | static int create_tmp_file(unsigned long len) | ||
54 | { | ||
55 | int fd, err; | ||
56 | char zero; | ||
57 | |||
58 | fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); | ||
59 | if(fd < 0) { | ||
60 | os_print_error(fd, "make_tempfile"); | ||
61 | exit(1); | ||
62 | } | ||
63 | |||
64 | err = os_mode_fd(fd, 0777); | ||
65 | if(err < 0){ | ||
66 | os_print_error(err, "os_mode_fd"); | ||
67 | exit(1); | ||
68 | } | ||
69 | err = os_seek_file(fd, len); | ||
70 | if(err < 0){ | ||
71 | os_print_error(err, "os_seek_file"); | ||
72 | exit(1); | ||
73 | } | ||
74 | zero = 0; | ||
75 | err = os_write_file(fd, &zero, 1); | ||
76 | if(err != 1){ | ||
77 | os_print_error(err, "os_write_file"); | ||
78 | exit(1); | ||
79 | } | ||
80 | |||
81 | return(fd); | ||
82 | } | ||
83 | |||
84 | void check_tmpexec(void) | ||
85 | { | ||
86 | void *addr; | ||
87 | int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); | ||
88 | |||
89 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, | ||
90 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); | ||
91 | printf("Checking PROT_EXEC mmap in /tmp..."); | ||
92 | fflush(stdout); | ||
93 | if(addr == MAP_FAILED){ | ||
94 | err = errno; | ||
95 | perror("failed"); | ||
96 | if(err == EPERM) | ||
97 | printf("/tmp must be not mounted noexec\n"); | ||
98 | exit(1); | ||
99 | } | ||
100 | printf("OK\n"); | ||
101 | munmap(addr, UM_KERN_PAGE_SIZE); | ||
102 | |||
103 | os_close_file(fd); | ||
104 | } | ||
105 | |||
106 | static int have_devanon = 0; | ||
107 | |||
108 | void check_devanon(void) | ||
109 | { | ||
110 | int fd; | ||
111 | |||
112 | printk("Checking for /dev/anon on the host..."); | ||
113 | fd = open("/dev/anon", O_RDWR); | ||
114 | if(fd < 0){ | ||
115 | printk("Not available (open failed with errno %d)\n", errno); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | printk("OK\n"); | ||
120 | have_devanon = 1; | ||
121 | } | ||
122 | |||
123 | static int create_anon_file(unsigned long len) | ||
124 | { | ||
125 | void *addr; | ||
126 | int fd; | ||
127 | |||
128 | fd = open("/dev/anon", O_RDWR); | ||
129 | if(fd < 0) { | ||
130 | os_print_error(fd, "opening /dev/anon"); | ||
131 | exit(1); | ||
132 | } | ||
133 | |||
134 | addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
135 | if(addr == MAP_FAILED){ | ||
136 | perror("mapping physmem file"); | ||
137 | exit(1); | ||
138 | } | ||
139 | munmap(addr, len); | ||
140 | |||
141 | return(fd); | ||
142 | } | ||
143 | |||
144 | int create_mem_file(unsigned long len) | ||
145 | { | ||
146 | int err, fd; | ||
147 | |||
148 | if(have_devanon) | ||
149 | fd = create_anon_file(len); | ||
150 | else fd = create_tmp_file(len); | ||
151 | |||
152 | err = os_set_exec_close(fd, 1); | ||
153 | if(err < 0) | ||
154 | os_print_error(err, "exec_close"); | ||
155 | return(fd); | ||
156 | } | ||
157 | |||
158 | struct iomem_region *iomem_regions = NULL; | ||
159 | int iomem_size = 0; | ||
160 | |||
161 | static int __init parse_iomem(char *str, int *add) | ||
162 | { | ||
163 | struct iomem_region *new; | ||
164 | struct uml_stat buf; | ||
165 | char *file, *driver; | ||
166 | int fd, err, size; | ||
167 | |||
168 | driver = str; | ||
169 | file = strchr(str,','); | ||
170 | if(file == NULL){ | ||
171 | printf("parse_iomem : failed to parse iomem\n"); | ||
172 | goto out; | ||
173 | } | ||
174 | *file = '\0'; | ||
175 | file++; | ||
176 | fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0); | ||
177 | if(fd < 0){ | ||
178 | os_print_error(fd, "parse_iomem - Couldn't open io file"); | ||
179 | goto out; | ||
180 | } | ||
181 | |||
182 | err = os_stat_fd(fd, &buf); | ||
183 | if(err < 0){ | ||
184 | os_print_error(err, "parse_iomem - cannot stat_fd file"); | ||
185 | goto out_close; | ||
186 | } | ||
187 | |||
188 | new = malloc(sizeof(*new)); | ||
189 | if(new == NULL){ | ||
190 | perror("Couldn't allocate iomem_region struct"); | ||
191 | goto out_close; | ||
192 | } | ||
193 | |||
194 | size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); | ||
195 | |||
196 | *new = ((struct iomem_region) { .next = iomem_regions, | ||
197 | .driver = driver, | ||
198 | .fd = fd, | ||
199 | .size = size, | ||
200 | .phys = 0, | ||
201 | .virt = 0 }); | ||
202 | iomem_regions = new; | ||
203 | iomem_size += new->size + UM_KERN_PAGE_SIZE; | ||
204 | |||
205 | return(0); | ||
206 | out_close: | ||
207 | os_close_file(fd); | ||
208 | out: | ||
209 | return(1); | ||
210 | } | ||
211 | |||
212 | __uml_setup("iomem=", parse_iomem, | ||
213 | "iomem=<name>,<file>\n" | ||
214 | " Configure <file> as an IO memory region named <name>.\n\n" | ||
215 | ); | ||
216 | |||
217 | int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, | ||
218 | int must_succeed) | ||
219 | { | ||
220 | int err; | ||
221 | |||
222 | err = os_protect_memory((void *) addr, len, r, w, x); | ||
223 | if(err < 0){ | ||
224 | if(must_succeed) | ||
225 | panic("protect failed, err = %d", -err); | ||
226 | else return(err); | ||
227 | } | ||
228 | return(0); | ||
229 | } | ||
230 | |||
231 | #if 0 | ||
232 | /* Debugging facility for dumping stuff out to the host, avoiding the timing | ||
233 | * problems that come with printf and breakpoints. | ||
234 | * Enable in case of emergency. | ||
235 | */ | ||
236 | |||
237 | int logging = 1; | ||
238 | int logging_fd = -1; | ||
239 | |||
240 | int logging_line = 0; | ||
241 | char logging_buf[512]; | ||
242 | |||
243 | void log(char *fmt, ...) | ||
244 | { | ||
245 | va_list ap; | ||
246 | struct timeval tv; | ||
247 | struct openflags flags; | ||
248 | |||
249 | if(logging == 0) return; | ||
250 | if(logging_fd < 0){ | ||
251 | flags = of_create(of_trunc(of_rdwr(OPENFLAGS()))); | ||
252 | logging_fd = os_open_file("log", flags, 0644); | ||
253 | } | ||
254 | gettimeofday(&tv, NULL); | ||
255 | sprintf(logging_buf, "%d\t %u.%u ", logging_line++, tv.tv_sec, | ||
256 | tv.tv_usec); | ||
257 | va_start(ap, fmt); | ||
258 | vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap); | ||
259 | va_end(ap); | ||
260 | write(logging_fd, logging_buf, strlen(logging_buf)); | ||
261 | } | ||
262 | #endif | ||
263 | |||
264 | /* | ||
265 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
266 | * Emacs will notice this stuff at the end of the file and automatically | ||
267 | * adjust the settings for this buffer only. This must remain at the end | ||
268 | * of the file. | ||
269 | * --------------------------------------------------------------------------- | ||
270 | * Local variables: | ||
271 | * c-file-style: "linux" | ||
272 | * End: | ||
273 | */ | ||
diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c new file mode 100644 index 000000000000..420e6d51fa0f --- /dev/null +++ b/arch/um/kernel/physmem.c | |||
@@ -0,0 +1,478 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/mm.h" | ||
7 | #include "linux/rbtree.h" | ||
8 | #include "linux/slab.h" | ||
9 | #include "linux/vmalloc.h" | ||
10 | #include "linux/bootmem.h" | ||
11 | #include "linux/module.h" | ||
12 | #include "asm/types.h" | ||
13 | #include "asm/pgtable.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "user_util.h" | ||
16 | #include "mode_kern.h" | ||
17 | #include "mem.h" | ||
18 | #include "mem_user.h" | ||
19 | #include "os.h" | ||
20 | #include "kern.h" | ||
21 | #include "init.h" | ||
22 | |||
23 | struct phys_desc { | ||
24 | struct rb_node rb; | ||
25 | int fd; | ||
26 | __u64 offset; | ||
27 | void *virt; | ||
28 | unsigned long phys; | ||
29 | struct list_head list; | ||
30 | }; | ||
31 | |||
32 | static struct rb_root phys_mappings = RB_ROOT; | ||
33 | |||
34 | static struct rb_node **find_rb(void *virt) | ||
35 | { | ||
36 | struct rb_node **n = &phys_mappings.rb_node; | ||
37 | struct phys_desc *d; | ||
38 | |||
39 | while(*n != NULL){ | ||
40 | d = rb_entry(*n, struct phys_desc, rb); | ||
41 | if(d->virt == virt) | ||
42 | return(n); | ||
43 | |||
44 | if(d->virt > virt) | ||
45 | n = &(*n)->rb_left; | ||
46 | else | ||
47 | n = &(*n)->rb_right; | ||
48 | } | ||
49 | |||
50 | return(n); | ||
51 | } | ||
52 | |||
53 | static struct phys_desc *find_phys_mapping(void *virt) | ||
54 | { | ||
55 | struct rb_node **n = find_rb(virt); | ||
56 | |||
57 | if(*n == NULL) | ||
58 | return(NULL); | ||
59 | |||
60 | return(rb_entry(*n, struct phys_desc, rb)); | ||
61 | } | ||
62 | |||
63 | static void insert_phys_mapping(struct phys_desc *desc) | ||
64 | { | ||
65 | struct rb_node **n = find_rb(desc->virt); | ||
66 | |||
67 | if(*n != NULL) | ||
68 | panic("Physical remapping for %p already present", | ||
69 | desc->virt); | ||
70 | |||
71 | rb_link_node(&desc->rb, (*n)->rb_parent, n); | ||
72 | rb_insert_color(&desc->rb, &phys_mappings); | ||
73 | } | ||
74 | |||
75 | LIST_HEAD(descriptor_mappings); | ||
76 | |||
77 | struct desc_mapping { | ||
78 | int fd; | ||
79 | struct list_head list; | ||
80 | struct list_head pages; | ||
81 | }; | ||
82 | |||
83 | static struct desc_mapping *find_mapping(int fd) | ||
84 | { | ||
85 | struct desc_mapping *desc; | ||
86 | struct list_head *ele; | ||
87 | |||
88 | list_for_each(ele, &descriptor_mappings){ | ||
89 | desc = list_entry(ele, struct desc_mapping, list); | ||
90 | if(desc->fd == fd) | ||
91 | return(desc); | ||
92 | } | ||
93 | |||
94 | return(NULL); | ||
95 | } | ||
96 | |||
97 | static struct desc_mapping *descriptor_mapping(int fd) | ||
98 | { | ||
99 | struct desc_mapping *desc; | ||
100 | |||
101 | desc = find_mapping(fd); | ||
102 | if(desc != NULL) | ||
103 | return(desc); | ||
104 | |||
105 | desc = kmalloc(sizeof(*desc), GFP_ATOMIC); | ||
106 | if(desc == NULL) | ||
107 | return(NULL); | ||
108 | |||
109 | *desc = ((struct desc_mapping) | ||
110 | { .fd = fd, | ||
111 | .list = LIST_HEAD_INIT(desc->list), | ||
112 | .pages = LIST_HEAD_INIT(desc->pages) }); | ||
113 | list_add(&desc->list, &descriptor_mappings); | ||
114 | |||
115 | return(desc); | ||
116 | } | ||
117 | |||
118 | int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) | ||
119 | { | ||
120 | struct desc_mapping *fd_maps; | ||
121 | struct phys_desc *desc; | ||
122 | unsigned long phys; | ||
123 | int err; | ||
124 | |||
125 | fd_maps = descriptor_mapping(fd); | ||
126 | if(fd_maps == NULL) | ||
127 | return(-ENOMEM); | ||
128 | |||
129 | phys = __pa(virt); | ||
130 | desc = find_phys_mapping(virt); | ||
131 | if(desc != NULL) | ||
132 | panic("Address 0x%p is already substituted\n", virt); | ||
133 | |||
134 | err = -ENOMEM; | ||
135 | desc = kmalloc(sizeof(*desc), GFP_ATOMIC); | ||
136 | if(desc == NULL) | ||
137 | goto out; | ||
138 | |||
139 | *desc = ((struct phys_desc) | ||
140 | { .fd = fd, | ||
141 | .offset = offset, | ||
142 | .virt = virt, | ||
143 | .phys = __pa(virt), | ||
144 | .list = LIST_HEAD_INIT(desc->list) }); | ||
145 | insert_phys_mapping(desc); | ||
146 | |||
147 | list_add(&desc->list, &fd_maps->pages); | ||
148 | |||
149 | virt = (void *) ((unsigned long) virt & PAGE_MASK); | ||
150 | err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); | ||
151 | if(!err) | ||
152 | goto out; | ||
153 | |||
154 | rb_erase(&desc->rb, &phys_mappings); | ||
155 | kfree(desc); | ||
156 | out: | ||
157 | return(err); | ||
158 | } | ||
159 | |||
160 | static int physmem_fd = -1; | ||
161 | |||
162 | static void remove_mapping(struct phys_desc *desc) | ||
163 | { | ||
164 | void *virt = desc->virt; | ||
165 | int err; | ||
166 | |||
167 | rb_erase(&desc->rb, &phys_mappings); | ||
168 | list_del(&desc->list); | ||
169 | kfree(desc); | ||
170 | |||
171 | err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0); | ||
172 | if(err) | ||
173 | panic("Failed to unmap block device page from physical memory, " | ||
174 | "errno = %d", -err); | ||
175 | } | ||
176 | |||
177 | int physmem_remove_mapping(void *virt) | ||
178 | { | ||
179 | struct phys_desc *desc; | ||
180 | |||
181 | virt = (void *) ((unsigned long) virt & PAGE_MASK); | ||
182 | desc = find_phys_mapping(virt); | ||
183 | if(desc == NULL) | ||
184 | return(0); | ||
185 | |||
186 | remove_mapping(desc); | ||
187 | return(1); | ||
188 | } | ||
189 | |||
190 | void physmem_forget_descriptor(int fd) | ||
191 | { | ||
192 | struct desc_mapping *desc; | ||
193 | struct phys_desc *page; | ||
194 | struct list_head *ele, *next; | ||
195 | __u64 offset; | ||
196 | void *addr; | ||
197 | int err; | ||
198 | |||
199 | desc = find_mapping(fd); | ||
200 | if(desc == NULL) | ||
201 | return; | ||
202 | |||
203 | list_for_each_safe(ele, next, &desc->pages){ | ||
204 | page = list_entry(ele, struct phys_desc, list); | ||
205 | offset = page->offset; | ||
206 | addr = page->virt; | ||
207 | remove_mapping(page); | ||
208 | err = os_seek_file(fd, offset); | ||
209 | if(err) | ||
210 | panic("physmem_forget_descriptor - failed to seek " | ||
211 | "to %lld in fd %d, error = %d\n", | ||
212 | offset, fd, -err); | ||
213 | err = os_read_file(fd, addr, PAGE_SIZE); | ||
214 | if(err < 0) | ||
215 | panic("physmem_forget_descriptor - failed to read " | ||
216 | "from fd %d to 0x%p, error = %d\n", | ||
217 | fd, addr, -err); | ||
218 | } | ||
219 | |||
220 | list_del(&desc->list); | ||
221 | kfree(desc); | ||
222 | } | ||
223 | |||
224 | EXPORT_SYMBOL(physmem_forget_descriptor); | ||
225 | EXPORT_SYMBOL(physmem_remove_mapping); | ||
226 | EXPORT_SYMBOL(physmem_subst_mapping); | ||
227 | |||
228 | void arch_free_page(struct page *page, int order) | ||
229 | { | ||
230 | void *virt; | ||
231 | int i; | ||
232 | |||
233 | for(i = 0; i < (1 << order); i++){ | ||
234 | virt = __va(page_to_phys(page + i)); | ||
235 | physmem_remove_mapping(virt); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | int is_remapped(void *virt) | ||
240 | { | ||
241 | struct phys_desc *desc = find_phys_mapping(virt); | ||
242 | |||
243 | return(desc != NULL); | ||
244 | } | ||
245 | |||
246 | /* Changed during early boot */ | ||
247 | unsigned long high_physmem; | ||
248 | |||
249 | extern unsigned long physmem_size; | ||
250 | |||
251 | void *to_virt(unsigned long phys) | ||
252 | { | ||
253 | return((void *) uml_physmem + phys); | ||
254 | } | ||
255 | |||
256 | unsigned long to_phys(void *virt) | ||
257 | { | ||
258 | return(((unsigned long) virt) - uml_physmem); | ||
259 | } | ||
260 | |||
261 | int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) | ||
262 | { | ||
263 | struct page *p, *map; | ||
264 | unsigned long phys_len, phys_pages, highmem_len, highmem_pages; | ||
265 | unsigned long iomem_len, iomem_pages, total_len, total_pages; | ||
266 | int i; | ||
267 | |||
268 | phys_pages = physmem >> PAGE_SHIFT; | ||
269 | phys_len = phys_pages * sizeof(struct page); | ||
270 | |||
271 | iomem_pages = iomem >> PAGE_SHIFT; | ||
272 | iomem_len = iomem_pages * sizeof(struct page); | ||
273 | |||
274 | highmem_pages = highmem >> PAGE_SHIFT; | ||
275 | highmem_len = highmem_pages * sizeof(struct page); | ||
276 | |||
277 | total_pages = phys_pages + iomem_pages + highmem_pages; | ||
278 | total_len = phys_len + iomem_pages + highmem_len; | ||
279 | |||
280 | if(kmalloc_ok){ | ||
281 | map = kmalloc(total_len, GFP_KERNEL); | ||
282 | if(map == NULL) | ||
283 | map = vmalloc(total_len); | ||
284 | } | ||
285 | else map = alloc_bootmem_low_pages(total_len); | ||
286 | |||
287 | if(map == NULL) | ||
288 | return(-ENOMEM); | ||
289 | |||
290 | for(i = 0; i < total_pages; i++){ | ||
291 | p = &map[i]; | ||
292 | set_page_count(p, 0); | ||
293 | SetPageReserved(p); | ||
294 | INIT_LIST_HEAD(&p->lru); | ||
295 | } | ||
296 | |||
297 | max_mapnr = total_pages; | ||
298 | return(0); | ||
299 | } | ||
300 | |||
301 | struct page *phys_to_page(const unsigned long phys) | ||
302 | { | ||
303 | return(&mem_map[phys >> PAGE_SHIFT]); | ||
304 | } | ||
305 | |||
306 | struct page *__virt_to_page(const unsigned long virt) | ||
307 | { | ||
308 | return(&mem_map[__pa(virt) >> PAGE_SHIFT]); | ||
309 | } | ||
310 | |||
311 | phys_t page_to_phys(struct page *page) | ||
312 | { | ||
313 | return((page - mem_map) << PAGE_SHIFT); | ||
314 | } | ||
315 | |||
316 | pte_t mk_pte(struct page *page, pgprot_t pgprot) | ||
317 | { | ||
318 | pte_t pte; | ||
319 | |||
320 | pte_set_val(pte, page_to_phys(page), pgprot); | ||
321 | if(pte_present(pte)) | ||
322 | pte_mknewprot(pte_mknewpage(pte)); | ||
323 | return(pte); | ||
324 | } | ||
325 | |||
326 | /* Changed during early boot */ | ||
327 | static unsigned long kmem_top = 0; | ||
328 | |||
329 | unsigned long get_kmem_end(void) | ||
330 | { | ||
331 | if(kmem_top == 0) | ||
332 | kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); | ||
333 | return(kmem_top); | ||
334 | } | ||
335 | |||
336 | void map_memory(unsigned long virt, unsigned long phys, unsigned long len, | ||
337 | int r, int w, int x) | ||
338 | { | ||
339 | __u64 offset; | ||
340 | int fd, err; | ||
341 | |||
342 | fd = phys_mapping(phys, &offset); | ||
343 | err = os_map_memory((void *) virt, fd, offset, len, r, w, x); | ||
344 | if(err) { | ||
345 | if(err == -ENOMEM) | ||
346 | printk("try increasing the host's " | ||
347 | "/proc/sys/vm/max_map_count to <physical " | ||
348 | "memory size>/4096\n"); | ||
349 | panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " | ||
350 | "err = %d\n", virt, fd, offset, len, r, w, x, err); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) | ||
355 | |||
356 | void setup_physmem(unsigned long start, unsigned long reserve_end, | ||
357 | unsigned long len, unsigned long highmem) | ||
358 | { | ||
359 | unsigned long reserve = reserve_end - start; | ||
360 | int pfn = PFN_UP(__pa(reserve_end)); | ||
361 | int delta = (len - reserve) >> PAGE_SHIFT; | ||
362 | int err, offset, bootmap_size; | ||
363 | |||
364 | physmem_fd = create_mem_file(len + highmem); | ||
365 | |||
366 | offset = uml_reserved - uml_physmem; | ||
367 | err = os_map_memory((void *) uml_reserved, physmem_fd, offset, | ||
368 | len - offset, 1, 1, 0); | ||
369 | if(err < 0){ | ||
370 | os_print_error(err, "Mapping memory"); | ||
371 | exit(1); | ||
372 | } | ||
373 | |||
374 | bootmap_size = init_bootmem(pfn, pfn + delta); | ||
375 | free_bootmem(__pa(reserve_end) + bootmap_size, | ||
376 | len - bootmap_size - reserve); | ||
377 | } | ||
378 | |||
379 | int phys_mapping(unsigned long phys, __u64 *offset_out) | ||
380 | { | ||
381 | struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK)); | ||
382 | int fd = -1; | ||
383 | |||
384 | if(desc != NULL){ | ||
385 | fd = desc->fd; | ||
386 | *offset_out = desc->offset; | ||
387 | } | ||
388 | else if(phys < physmem_size){ | ||
389 | fd = physmem_fd; | ||
390 | *offset_out = phys; | ||
391 | } | ||
392 | else if(phys < __pa(end_iomem)){ | ||
393 | struct iomem_region *region = iomem_regions; | ||
394 | |||
395 | while(region != NULL){ | ||
396 | if((phys >= region->phys) && | ||
397 | (phys < region->phys + region->size)){ | ||
398 | fd = region->fd; | ||
399 | *offset_out = phys - region->phys; | ||
400 | break; | ||
401 | } | ||
402 | region = region->next; | ||
403 | } | ||
404 | } | ||
405 | else if(phys < __pa(end_iomem) + highmem){ | ||
406 | fd = physmem_fd; | ||
407 | *offset_out = phys - iomem_size; | ||
408 | } | ||
409 | |||
410 | return(fd); | ||
411 | } | ||
412 | |||
413 | static int __init uml_mem_setup(char *line, int *add) | ||
414 | { | ||
415 | char *retptr; | ||
416 | physmem_size = memparse(line,&retptr); | ||
417 | return 0; | ||
418 | } | ||
419 | __uml_setup("mem=", uml_mem_setup, | ||
420 | "mem=<Amount of desired ram>\n" | ||
421 | " This controls how much \"physical\" memory the kernel allocates\n" | ||
422 | " for the system. The size is specified as a number followed by\n" | ||
423 | " one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" | ||
424 | " This is not related to the amount of memory in the host. It can\n" | ||
425 | " be more, and the excess, if it's ever used, will just be swapped out.\n" | ||
426 | " Example: mem=64M\n\n" | ||
427 | ); | ||
428 | |||
429 | unsigned long find_iomem(char *driver, unsigned long *len_out) | ||
430 | { | ||
431 | struct iomem_region *region = iomem_regions; | ||
432 | |||
433 | while(region != NULL){ | ||
434 | if(!strcmp(region->driver, driver)){ | ||
435 | *len_out = region->size; | ||
436 | return(region->virt); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | return(0); | ||
441 | } | ||
442 | |||
443 | int setup_iomem(void) | ||
444 | { | ||
445 | struct iomem_region *region = iomem_regions; | ||
446 | unsigned long iomem_start = high_physmem + PAGE_SIZE; | ||
447 | int err; | ||
448 | |||
449 | while(region != NULL){ | ||
450 | err = os_map_memory((void *) iomem_start, region->fd, 0, | ||
451 | region->size, 1, 1, 0); | ||
452 | if(err) | ||
453 | printk("Mapping iomem region for driver '%s' failed, " | ||
454 | "errno = %d\n", region->driver, -err); | ||
455 | else { | ||
456 | region->virt = iomem_start; | ||
457 | region->phys = __pa(region->virt); | ||
458 | } | ||
459 | |||
460 | iomem_start += region->size + PAGE_SIZE; | ||
461 | region = region->next; | ||
462 | } | ||
463 | |||
464 | return(0); | ||
465 | } | ||
466 | |||
467 | __initcall(setup_iomem); | ||
468 | |||
469 | /* | ||
470 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
471 | * Emacs will notice this stuff at the end of the file and automatically | ||
472 | * adjust the settings for this buffer only. This must remain at the end | ||
473 | * of the file. | ||
474 | * --------------------------------------------------------------------------- | ||
475 | * Local variables: | ||
476 | * c-file-style: "linux" | ||
477 | * End: | ||
478 | */ | ||
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c new file mode 100644 index 000000000000..f76a2692adca --- /dev/null +++ b/arch/um/kernel/process.c | |||
@@ -0,0 +1,423 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <signal.h> | ||
9 | #include <sched.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdarg.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <setjmp.h> | ||
14 | #include <sys/time.h> | ||
15 | #include <sys/wait.h> | ||
16 | #include <sys/mman.h> | ||
17 | #include <asm/unistd.h> | ||
18 | #include <asm/page.h> | ||
19 | #include "user_util.h" | ||
20 | #include "kern_util.h" | ||
21 | #include "user.h" | ||
22 | #include "process.h" | ||
23 | #include "signal_kern.h" | ||
24 | #include "signal_user.h" | ||
25 | #include "sysdep/ptrace.h" | ||
26 | #include "sysdep/sigcontext.h" | ||
27 | #include "irq_user.h" | ||
28 | #include "ptrace_user.h" | ||
29 | #include "time_user.h" | ||
30 | #include "init.h" | ||
31 | #include "os.h" | ||
32 | #include "uml-config.h" | ||
33 | #include "ptrace_user.h" | ||
34 | #include "choose-mode.h" | ||
35 | #include "mode.h" | ||
36 | #ifdef UML_CONFIG_MODE_SKAS | ||
37 | #include "skas.h" | ||
38 | #include "skas_ptrace.h" | ||
39 | #include "registers.h" | ||
40 | #endif | ||
41 | |||
42 | void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) | ||
43 | { | ||
44 | int flags = 0, pages; | ||
45 | |||
46 | if(sig_stack != NULL){ | ||
47 | pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER); | ||
48 | set_sigstack(sig_stack, pages * page_size()); | ||
49 | flags = SA_ONSTACK; | ||
50 | } | ||
51 | if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); | ||
52 | } | ||
53 | |||
54 | void init_new_thread_signals(int altstack) | ||
55 | { | ||
56 | int flags = altstack ? SA_ONSTACK : 0; | ||
57 | |||
58 | set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, | ||
59 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
60 | set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, | ||
61 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
62 | set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, | ||
63 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
64 | set_handler(SIGILL, (__sighandler_t) sig_handler, flags, | ||
65 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
66 | set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, | ||
67 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
68 | set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags, | ||
69 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
70 | set_handler(SIGUSR2, (__sighandler_t) sig_handler, | ||
71 | flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | ||
72 | signal(SIGHUP, SIG_IGN); | ||
73 | |||
74 | init_irq_signals(altstack); | ||
75 | } | ||
76 | |||
77 | struct tramp { | ||
78 | int (*tramp)(void *); | ||
79 | void *tramp_data; | ||
80 | unsigned long temp_stack; | ||
81 | int flags; | ||
82 | int pid; | ||
83 | }; | ||
84 | |||
85 | /* See above for why sigkill is here */ | ||
86 | |||
87 | int sigkill = SIGKILL; | ||
88 | |||
89 | int outer_tramp(void *arg) | ||
90 | { | ||
91 | struct tramp *t; | ||
92 | int sig = sigkill; | ||
93 | |||
94 | t = arg; | ||
95 | t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, | ||
96 | t->flags, t->tramp_data); | ||
97 | if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL); | ||
98 | kill(os_getpid(), sig); | ||
99 | _exit(0); | ||
100 | } | ||
101 | |||
102 | int start_fork_tramp(void *thread_arg, unsigned long temp_stack, | ||
103 | int clone_flags, int (*tramp)(void *)) | ||
104 | { | ||
105 | struct tramp arg; | ||
106 | unsigned long sp; | ||
107 | int new_pid, status, err; | ||
108 | |||
109 | /* The trampoline will run on the temporary stack */ | ||
110 | sp = stack_sp(temp_stack); | ||
111 | |||
112 | clone_flags |= CLONE_FILES | SIGCHLD; | ||
113 | |||
114 | arg.tramp = tramp; | ||
115 | arg.tramp_data = thread_arg; | ||
116 | arg.temp_stack = temp_stack; | ||
117 | arg.flags = clone_flags; | ||
118 | |||
119 | /* Start the process and wait for it to kill itself */ | ||
120 | new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg); | ||
121 | if(new_pid < 0) | ||
122 | return(new_pid); | ||
123 | |||
124 | CATCH_EINTR(err = waitpid(new_pid, &status, 0)); | ||
125 | if(err < 0) | ||
126 | panic("Waiting for outer trampoline failed - errno = %d", | ||
127 | errno); | ||
128 | |||
129 | if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) | ||
130 | panic("outer trampoline didn't exit with SIGKILL, " | ||
131 | "status = %d", status); | ||
132 | |||
133 | return(arg.pid); | ||
134 | } | ||
135 | |||
136 | static int ptrace_child(void *arg) | ||
137 | { | ||
138 | int ret; | ||
139 | int pid = os_getpid(), ppid = getppid(); | ||
140 | int sc_result; | ||
141 | |||
142 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ | ||
143 | perror("ptrace"); | ||
144 | os_kill_process(pid, 0); | ||
145 | } | ||
146 | os_stop_process(pid); | ||
147 | |||
148 | /*This syscall will be intercepted by the parent. Don't call more than | ||
149 | * once, please.*/ | ||
150 | sc_result = os_getpid(); | ||
151 | |||
152 | if (sc_result == pid) | ||
153 | ret = 1; /*Nothing modified by the parent, we are running | ||
154 | normally.*/ | ||
155 | else if (sc_result == ppid) | ||
156 | ret = 0; /*Expected in check_ptrace and check_sysemu when they | ||
157 | succeed in modifying the stack frame*/ | ||
158 | else | ||
159 | ret = 2; /*Serious trouble! This could be caused by a bug in | ||
160 | host 2.6 SKAS3/2.6 patch before release -V6, together | ||
161 | with a bug in the UML code itself.*/ | ||
162 | _exit(ret); | ||
163 | } | ||
164 | |||
165 | static int start_ptraced_child(void **stack_out) | ||
166 | { | ||
167 | void *stack; | ||
168 | unsigned long sp; | ||
169 | int pid, n, status; | ||
170 | |||
171 | stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, | ||
172 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
173 | if(stack == MAP_FAILED) | ||
174 | panic("check_ptrace : mmap failed, errno = %d", errno); | ||
175 | sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); | ||
176 | pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL); | ||
177 | if(pid < 0) | ||
178 | panic("check_ptrace : clone failed, errno = %d", errno); | ||
179 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
180 | if(n < 0) | ||
181 | panic("check_ptrace : wait failed, errno = %d", errno); | ||
182 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) | ||
183 | panic("check_ptrace : expected SIGSTOP, got status = %d", | ||
184 | status); | ||
185 | |||
186 | *stack_out = stack; | ||
187 | return(pid); | ||
188 | } | ||
189 | |||
190 | /* When testing for SYSEMU support, if it is one of the broken versions, we must | ||
191 | * just avoid using sysemu, not panic, but only if SYSEMU features are broken. | ||
192 | * So only for SYSEMU features we test mustpanic, while normal host features | ||
193 | * must work anyway!*/ | ||
194 | static int stop_ptraced_child(int pid, void *stack, int exitcode, int mustpanic) | ||
195 | { | ||
196 | int status, n, ret = 0; | ||
197 | |||
198 | if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) | ||
199 | panic("check_ptrace : ptrace failed, errno = %d", errno); | ||
200 | CATCH_EINTR(n = waitpid(pid, &status, 0)); | ||
201 | if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { | ||
202 | int exit_with = WEXITSTATUS(status); | ||
203 | if (exit_with == 2) | ||
204 | printk("check_ptrace : child exited with status 2. " | ||
205 | "Serious trouble happening! Try updating your " | ||
206 | "host skas patch!\nDisabling SYSEMU support."); | ||
207 | printk("check_ptrace : child exited with exitcode %d, while " | ||
208 | "expecting %d; status 0x%x", exit_with, | ||
209 | exitcode, status); | ||
210 | if (mustpanic) | ||
211 | panic("\n"); | ||
212 | else | ||
213 | printk("\n"); | ||
214 | ret = -1; | ||
215 | } | ||
216 | |||
217 | if(munmap(stack, PAGE_SIZE) < 0) | ||
218 | panic("check_ptrace : munmap failed, errno = %d", errno); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static int force_sysemu_disabled = 0; | ||
223 | |||
224 | static int __init nosysemu_cmd_param(char *str, int* add) | ||
225 | { | ||
226 | force_sysemu_disabled = 1; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | __uml_setup("nosysemu", nosysemu_cmd_param, | ||
231 | "nosysemu\n" | ||
232 | " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" | ||
233 | " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" | ||
234 | " behaviour of ptrace() and helps reducing host context switch rate.\n" | ||
235 | " To make it working, you need a kernel patch for your host, too.\n" | ||
236 | " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further information.\n\n"); | ||
237 | |||
238 | static void __init check_sysemu(void) | ||
239 | { | ||
240 | void *stack; | ||
241 | int pid, syscall, n, status, count=0; | ||
242 | |||
243 | printk("Checking syscall emulation patch for ptrace..."); | ||
244 | sysemu_supported = 0; | ||
245 | pid = start_ptraced_child(&stack); | ||
246 | |||
247 | if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) | ||
248 | goto fail; | ||
249 | |||
250 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
251 | if (n < 0) | ||
252 | panic("check_sysemu : wait failed, errno = %d", errno); | ||
253 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) | ||
254 | panic("check_sysemu : expected SIGTRAP, " | ||
255 | "got status = %d", status); | ||
256 | |||
257 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, | ||
258 | os_getpid()); | ||
259 | if(n < 0) | ||
260 | panic("check_sysemu : failed to modify system " | ||
261 | "call return, errno = %d", errno); | ||
262 | |||
263 | if (stop_ptraced_child(pid, stack, 0, 0) < 0) | ||
264 | goto fail_stopped; | ||
265 | |||
266 | sysemu_supported = 1; | ||
267 | printk("OK\n"); | ||
268 | set_using_sysemu(!force_sysemu_disabled); | ||
269 | |||
270 | printk("Checking advanced syscall emulation patch for ptrace..."); | ||
271 | pid = start_ptraced_child(&stack); | ||
272 | while(1){ | ||
273 | count++; | ||
274 | if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) | ||
275 | goto fail; | ||
276 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
277 | if(n < 0) | ||
278 | panic("check_ptrace : wait failed, errno = %d", errno); | ||
279 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) | ||
280 | panic("check_ptrace : expected (SIGTRAP|SYSCALL_TRAP), " | ||
281 | "got status = %d", status); | ||
282 | |||
283 | syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, | ||
284 | 0); | ||
285 | if(syscall == __NR_getpid){ | ||
286 | if (!count) | ||
287 | panic("check_ptrace : SYSEMU_SINGLESTEP doesn't singlestep"); | ||
288 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, | ||
289 | os_getpid()); | ||
290 | if(n < 0) | ||
291 | panic("check_sysemu : failed to modify system " | ||
292 | "call return, errno = %d", errno); | ||
293 | break; | ||
294 | } | ||
295 | } | ||
296 | if (stop_ptraced_child(pid, stack, 0, 0) < 0) | ||
297 | goto fail_stopped; | ||
298 | |||
299 | sysemu_supported = 2; | ||
300 | printk("OK\n"); | ||
301 | |||
302 | if ( !force_sysemu_disabled ) | ||
303 | set_using_sysemu(sysemu_supported); | ||
304 | return; | ||
305 | |||
306 | fail: | ||
307 | stop_ptraced_child(pid, stack, 1, 0); | ||
308 | fail_stopped: | ||
309 | printk("missing\n"); | ||
310 | } | ||
311 | |||
312 | void __init check_ptrace(void) | ||
313 | { | ||
314 | void *stack; | ||
315 | int pid, syscall, n, status; | ||
316 | |||
317 | printk("Checking that ptrace can change system call numbers..."); | ||
318 | pid = start_ptraced_child(&stack); | ||
319 | |||
320 | if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) | ||
321 | panic("check_ptrace: PTRACE_SETOPTIONS failed, errno = %d", errno); | ||
322 | |||
323 | while(1){ | ||
324 | if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) | ||
325 | panic("check_ptrace : ptrace failed, errno = %d", | ||
326 | errno); | ||
327 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
328 | if(n < 0) | ||
329 | panic("check_ptrace : wait failed, errno = %d", errno); | ||
330 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP + 0x80)) | ||
331 | panic("check_ptrace : expected SIGTRAP + 0x80, " | ||
332 | "got status = %d", status); | ||
333 | |||
334 | syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, | ||
335 | 0); | ||
336 | if(syscall == __NR_getpid){ | ||
337 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, | ||
338 | __NR_getppid); | ||
339 | if(n < 0) | ||
340 | panic("check_ptrace : failed to modify system " | ||
341 | "call, errno = %d", errno); | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | stop_ptraced_child(pid, stack, 0, 1); | ||
346 | printk("OK\n"); | ||
347 | check_sysemu(); | ||
348 | } | ||
349 | |||
350 | int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr) | ||
351 | { | ||
352 | sigjmp_buf buf; | ||
353 | int n; | ||
354 | |||
355 | *jmp_ptr = &buf; | ||
356 | n = sigsetjmp(buf, 1); | ||
357 | if(n != 0) | ||
358 | return(n); | ||
359 | (*fn)(arg); | ||
360 | return(0); | ||
361 | } | ||
362 | |||
363 | void forward_pending_sigio(int target) | ||
364 | { | ||
365 | sigset_t sigs; | ||
366 | |||
367 | if(sigpending(&sigs)) | ||
368 | panic("forward_pending_sigio : sigpending failed"); | ||
369 | if(sigismember(&sigs, SIGIO)) | ||
370 | kill(target, SIGIO); | ||
371 | } | ||
372 | |||
373 | #ifdef UML_CONFIG_MODE_SKAS | ||
374 | static inline int check_skas3_ptrace_support(void) | ||
375 | { | ||
376 | struct ptrace_faultinfo fi; | ||
377 | void *stack; | ||
378 | int pid, n, ret = 1; | ||
379 | |||
380 | printf("Checking for the skas3 patch in the host..."); | ||
381 | pid = start_ptraced_child(&stack); | ||
382 | |||
383 | n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); | ||
384 | if (n < 0) { | ||
385 | if(errno == EIO) | ||
386 | printf("not found\n"); | ||
387 | else { | ||
388 | perror("not found"); | ||
389 | } | ||
390 | ret = 0; | ||
391 | } else { | ||
392 | printf("found\n"); | ||
393 | } | ||
394 | |||
395 | init_registers(pid); | ||
396 | stop_ptraced_child(pid, stack, 1, 1); | ||
397 | |||
398 | return(ret); | ||
399 | } | ||
400 | |||
401 | int can_do_skas(void) | ||
402 | { | ||
403 | int ret = 1; | ||
404 | |||
405 | printf("Checking for /proc/mm..."); | ||
406 | if (os_access("/proc/mm", OS_ACC_W_OK) < 0) { | ||
407 | printf("not found\n"); | ||
408 | ret = 0; | ||
409 | goto out; | ||
410 | } else { | ||
411 | printf("found\n"); | ||
412 | } | ||
413 | |||
414 | ret = check_skas3_ptrace_support(); | ||
415 | out: | ||
416 | return ret; | ||
417 | } | ||
418 | #else | ||
419 | int can_do_skas(void) | ||
420 | { | ||
421 | return(0); | ||
422 | } | ||
423 | #endif | ||
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c new file mode 100644 index 000000000000..1d719d5b4bb9 --- /dev/null +++ b/arch/um/kernel/process_kern.c | |||
@@ -0,0 +1,500 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Copyright 2003 PathScale, Inc. | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include "linux/config.h" | ||
8 | #include "linux/kernel.h" | ||
9 | #include "linux/sched.h" | ||
10 | #include "linux/interrupt.h" | ||
11 | #include "linux/mm.h" | ||
12 | #include "linux/slab.h" | ||
13 | #include "linux/utsname.h" | ||
14 | #include "linux/fs.h" | ||
15 | #include "linux/utime.h" | ||
16 | #include "linux/smp_lock.h" | ||
17 | #include "linux/module.h" | ||
18 | #include "linux/init.h" | ||
19 | #include "linux/capability.h" | ||
20 | #include "linux/vmalloc.h" | ||
21 | #include "linux/spinlock.h" | ||
22 | #include "linux/proc_fs.h" | ||
23 | #include "linux/ptrace.h" | ||
24 | #include "linux/random.h" | ||
25 | #include "asm/unistd.h" | ||
26 | #include "asm/mman.h" | ||
27 | #include "asm/segment.h" | ||
28 | #include "asm/stat.h" | ||
29 | #include "asm/pgtable.h" | ||
30 | #include "asm/processor.h" | ||
31 | #include "asm/tlbflush.h" | ||
32 | #include "asm/uaccess.h" | ||
33 | #include "asm/user.h" | ||
34 | #include "user_util.h" | ||
35 | #include "kern_util.h" | ||
36 | #include "kern.h" | ||
37 | #include "signal_kern.h" | ||
38 | #include "signal_user.h" | ||
39 | #include "init.h" | ||
40 | #include "irq_user.h" | ||
41 | #include "mem_user.h" | ||
42 | #include "time_user.h" | ||
43 | #include "tlb.h" | ||
44 | #include "frame_kern.h" | ||
45 | #include "sigcontext.h" | ||
46 | #include "2_5compat.h" | ||
47 | #include "os.h" | ||
48 | #include "mode.h" | ||
49 | #include "mode_kern.h" | ||
50 | #include "choose-mode.h" | ||
51 | |||
52 | /* This is a per-cpu array. A processor only modifies its entry and it only | ||
53 | * cares about its entry, so it's OK if another processor is modifying its | ||
54 | * entry. | ||
55 | */ | ||
56 | struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; | ||
57 | |||
58 | struct task_struct *get_task(int pid, int require) | ||
59 | { | ||
60 | struct task_struct *ret; | ||
61 | |||
62 | read_lock(&tasklist_lock); | ||
63 | ret = find_task_by_pid(pid); | ||
64 | read_unlock(&tasklist_lock); | ||
65 | |||
66 | if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); | ||
67 | return(ret); | ||
68 | } | ||
69 | |||
70 | int external_pid(void *t) | ||
71 | { | ||
72 | struct task_struct *task = t ? t : current; | ||
73 | |||
74 | return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task)); | ||
75 | } | ||
76 | |||
77 | int pid_to_processor_id(int pid) | ||
78 | { | ||
79 | int i; | ||
80 | |||
81 | for(i = 0; i < ncpus; i++){ | ||
82 | if(cpu_tasks[i].pid == pid) return(i); | ||
83 | } | ||
84 | return(-1); | ||
85 | } | ||
86 | |||
87 | void free_stack(unsigned long stack, int order) | ||
88 | { | ||
89 | free_pages(stack, order); | ||
90 | } | ||
91 | |||
92 | unsigned long alloc_stack(int order, int atomic) | ||
93 | { | ||
94 | unsigned long page; | ||
95 | int flags = GFP_KERNEL; | ||
96 | |||
97 | if(atomic) flags |= GFP_ATOMIC; | ||
98 | page = __get_free_pages(flags, order); | ||
99 | if(page == 0) | ||
100 | return(0); | ||
101 | stack_protections(page); | ||
102 | return(page); | ||
103 | } | ||
104 | |||
105 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
106 | { | ||
107 | int pid; | ||
108 | |||
109 | current->thread.request.u.thread.proc = fn; | ||
110 | current->thread.request.u.thread.arg = arg; | ||
111 | pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0, NULL, 0, NULL, | ||
112 | NULL); | ||
113 | if(pid < 0) | ||
114 | panic("do_fork failed in kernel_thread, errno = %d", pid); | ||
115 | return(pid); | ||
116 | } | ||
117 | |||
118 | void switch_mm(struct mm_struct *prev, struct mm_struct *next, | ||
119 | struct task_struct *tsk) | ||
120 | { | ||
121 | int cpu = smp_processor_id(); | ||
122 | |||
123 | if (prev != next) | ||
124 | cpu_clear(cpu, prev->cpu_vm_mask); | ||
125 | cpu_set(cpu, next->cpu_vm_mask); | ||
126 | } | ||
127 | |||
128 | void set_current(void *t) | ||
129 | { | ||
130 | struct task_struct *task = t; | ||
131 | |||
132 | cpu_tasks[task->thread_info->cpu] = ((struct cpu_task) | ||
133 | { external_pid(task), task }); | ||
134 | } | ||
135 | |||
136 | void *_switch_to(void *prev, void *next, void *last) | ||
137 | { | ||
138 | return(CHOOSE_MODE(switch_to_tt(prev, next), | ||
139 | switch_to_skas(prev, next))); | ||
140 | } | ||
141 | |||
142 | void interrupt_end(void) | ||
143 | { | ||
144 | if(need_resched()) schedule(); | ||
145 | if(test_tsk_thread_flag(current, TIF_SIGPENDING)) do_signal(); | ||
146 | } | ||
147 | |||
148 | void release_thread(struct task_struct *task) | ||
149 | { | ||
150 | CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task)); | ||
151 | } | ||
152 | |||
153 | void exit_thread(void) | ||
154 | { | ||
155 | CHOOSE_MODE(exit_thread_tt(), exit_thread_skas()); | ||
156 | unprotect_stack((unsigned long) current_thread); | ||
157 | } | ||
158 | |||
159 | void *get_current(void) | ||
160 | { | ||
161 | return(current); | ||
162 | } | ||
163 | |||
164 | void prepare_to_copy(struct task_struct *tsk) | ||
165 | { | ||
166 | } | ||
167 | |||
168 | int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, | ||
169 | unsigned long stack_top, struct task_struct * p, | ||
170 | struct pt_regs *regs) | ||
171 | { | ||
172 | p->thread = (struct thread_struct) INIT_THREAD; | ||
173 | return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, | ||
174 | clone_flags, sp, stack_top, p, regs)); | ||
175 | } | ||
176 | |||
177 | void initial_thread_cb(void (*proc)(void *), void *arg) | ||
178 | { | ||
179 | int save_kmalloc_ok = kmalloc_ok; | ||
180 | |||
181 | kmalloc_ok = 0; | ||
182 | CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc, | ||
183 | arg); | ||
184 | kmalloc_ok = save_kmalloc_ok; | ||
185 | } | ||
186 | |||
187 | unsigned long stack_sp(unsigned long page) | ||
188 | { | ||
189 | return(page + PAGE_SIZE - sizeof(void *)); | ||
190 | } | ||
191 | |||
192 | int current_pid(void) | ||
193 | { | ||
194 | return(current->pid); | ||
195 | } | ||
196 | |||
197 | void default_idle(void) | ||
198 | { | ||
199 | uml_idle_timer(); | ||
200 | |||
201 | atomic_inc(&init_mm.mm_count); | ||
202 | current->mm = &init_mm; | ||
203 | current->active_mm = &init_mm; | ||
204 | |||
205 | while(1){ | ||
206 | /* endless idle loop with no priority at all */ | ||
207 | SET_PRI(current); | ||
208 | |||
209 | /* | ||
210 | * although we are an idle CPU, we do not want to | ||
211 | * get into the scheduler unnecessarily. | ||
212 | */ | ||
213 | if(need_resched()) | ||
214 | schedule(); | ||
215 | |||
216 | idle_sleep(10); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | void cpu_idle(void) | ||
221 | { | ||
222 | CHOOSE_MODE(init_idle_tt(), init_idle_skas()); | ||
223 | } | ||
224 | |||
225 | int page_size(void) | ||
226 | { | ||
227 | return(PAGE_SIZE); | ||
228 | } | ||
229 | |||
230 | unsigned long page_mask(void) | ||
231 | { | ||
232 | return(PAGE_MASK); | ||
233 | } | ||
234 | |||
235 | void *um_virt_to_phys(struct task_struct *task, unsigned long addr, | ||
236 | pte_t *pte_out) | ||
237 | { | ||
238 | pgd_t *pgd; | ||
239 | pud_t *pud; | ||
240 | pmd_t *pmd; | ||
241 | pte_t *pte; | ||
242 | |||
243 | if(task->mm == NULL) | ||
244 | return(ERR_PTR(-EINVAL)); | ||
245 | pgd = pgd_offset(task->mm, addr); | ||
246 | if(!pgd_present(*pgd)) | ||
247 | return(ERR_PTR(-EINVAL)); | ||
248 | |||
249 | pud = pud_offset(pgd, addr); | ||
250 | if(!pud_present(*pud)) | ||
251 | return(ERR_PTR(-EINVAL)); | ||
252 | |||
253 | pmd = pmd_offset(pud, addr); | ||
254 | if(!pmd_present(*pmd)) | ||
255 | return(ERR_PTR(-EINVAL)); | ||
256 | |||
257 | pte = pte_offset_kernel(pmd, addr); | ||
258 | if(!pte_present(*pte)) | ||
259 | return(ERR_PTR(-EINVAL)); | ||
260 | |||
261 | if(pte_out != NULL) | ||
262 | *pte_out = *pte; | ||
263 | return((void *) (pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); | ||
264 | } | ||
265 | |||
266 | char *current_cmd(void) | ||
267 | { | ||
268 | #if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM) | ||
269 | return("(Unknown)"); | ||
270 | #else | ||
271 | void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL); | ||
272 | return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr); | ||
273 | #endif | ||
274 | } | ||
275 | |||
276 | void force_sigbus(void) | ||
277 | { | ||
278 | printk(KERN_ERR "Killing pid %d because of a lack of memory\n", | ||
279 | current->pid); | ||
280 | lock_kernel(); | ||
281 | sigaddset(¤t->pending.signal, SIGBUS); | ||
282 | recalc_sigpending(); | ||
283 | current->flags |= PF_SIGNALED; | ||
284 | do_exit(SIGBUS | 0x80); | ||
285 | } | ||
286 | |||
287 | void dump_thread(struct pt_regs *regs, struct user *u) | ||
288 | { | ||
289 | } | ||
290 | |||
291 | void enable_hlt(void) | ||
292 | { | ||
293 | panic("enable_hlt"); | ||
294 | } | ||
295 | |||
296 | EXPORT_SYMBOL(enable_hlt); | ||
297 | |||
298 | void disable_hlt(void) | ||
299 | { | ||
300 | panic("disable_hlt"); | ||
301 | } | ||
302 | |||
303 | EXPORT_SYMBOL(disable_hlt); | ||
304 | |||
305 | void *um_kmalloc(int size) | ||
306 | { | ||
307 | return(kmalloc(size, GFP_KERNEL)); | ||
308 | } | ||
309 | |||
310 | void *um_kmalloc_atomic(int size) | ||
311 | { | ||
312 | return(kmalloc(size, GFP_ATOMIC)); | ||
313 | } | ||
314 | |||
315 | void *um_vmalloc(int size) | ||
316 | { | ||
317 | return(vmalloc(size)); | ||
318 | } | ||
319 | |||
320 | unsigned long get_fault_addr(void) | ||
321 | { | ||
322 | return((unsigned long) current->thread.fault_addr); | ||
323 | } | ||
324 | |||
325 | EXPORT_SYMBOL(get_fault_addr); | ||
326 | |||
327 | void not_implemented(void) | ||
328 | { | ||
329 | printk(KERN_DEBUG "Something isn't implemented in here\n"); | ||
330 | } | ||
331 | |||
332 | EXPORT_SYMBOL(not_implemented); | ||
333 | |||
334 | int user_context(unsigned long sp) | ||
335 | { | ||
336 | unsigned long stack; | ||
337 | |||
338 | stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER); | ||
339 | return(stack != (unsigned long) current_thread); | ||
340 | } | ||
341 | |||
342 | extern void remove_umid_dir(void); | ||
343 | |||
344 | __uml_exitcall(remove_umid_dir); | ||
345 | |||
346 | extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end; | ||
347 | |||
348 | void do_uml_exitcalls(void) | ||
349 | { | ||
350 | exitcall_t *call; | ||
351 | |||
352 | call = &__uml_exitcall_end; | ||
353 | while (--call >= &__uml_exitcall_begin) | ||
354 | (*call)(); | ||
355 | } | ||
356 | |||
357 | char *uml_strdup(char *string) | ||
358 | { | ||
359 | char *new; | ||
360 | |||
361 | new = kmalloc(strlen(string) + 1, GFP_KERNEL); | ||
362 | if(new == NULL) return(NULL); | ||
363 | strcpy(new, string); | ||
364 | return(new); | ||
365 | } | ||
366 | |||
367 | void *get_init_task(void) | ||
368 | { | ||
369 | return(&init_thread_union.thread_info.task); | ||
370 | } | ||
371 | |||
372 | int copy_to_user_proc(void __user *to, void *from, int size) | ||
373 | { | ||
374 | return(copy_to_user(to, from, size)); | ||
375 | } | ||
376 | |||
377 | int copy_from_user_proc(void *to, void __user *from, int size) | ||
378 | { | ||
379 | return(copy_from_user(to, from, size)); | ||
380 | } | ||
381 | |||
382 | int clear_user_proc(void __user *buf, int size) | ||
383 | { | ||
384 | return(clear_user(buf, size)); | ||
385 | } | ||
386 | |||
387 | int strlen_user_proc(char __user *str) | ||
388 | { | ||
389 | return(strlen_user(str)); | ||
390 | } | ||
391 | |||
392 | int smp_sigio_handler(void) | ||
393 | { | ||
394 | #ifdef CONFIG_SMP | ||
395 | int cpu = current_thread->cpu; | ||
396 | IPI_handler(cpu); | ||
397 | if(cpu != 0) | ||
398 | return(1); | ||
399 | #endif | ||
400 | return(0); | ||
401 | } | ||
402 | |||
403 | int um_in_interrupt(void) | ||
404 | { | ||
405 | return(in_interrupt()); | ||
406 | } | ||
407 | |||
408 | int cpu(void) | ||
409 | { | ||
410 | return(current_thread->cpu); | ||
411 | } | ||
412 | |||
413 | static atomic_t using_sysemu = ATOMIC_INIT(0); | ||
414 | int sysemu_supported; | ||
415 | |||
416 | void set_using_sysemu(int value) | ||
417 | { | ||
418 | if (value > sysemu_supported) | ||
419 | return; | ||
420 | atomic_set(&using_sysemu, value); | ||
421 | } | ||
422 | |||
423 | int get_using_sysemu(void) | ||
424 | { | ||
425 | return atomic_read(&using_sysemu); | ||
426 | } | ||
427 | |||
428 | static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data) | ||
429 | { | ||
430 | if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/ | ||
431 | *eof = 1; | ||
432 | |||
433 | return strlen(buf); | ||
434 | } | ||
435 | |||
436 | static int proc_write_sysemu(struct file *file,const char *buf, unsigned long count,void *data) | ||
437 | { | ||
438 | char tmp[2]; | ||
439 | |||
440 | if (copy_from_user(tmp, buf, 1)) | ||
441 | return -EFAULT; | ||
442 | |||
443 | if (tmp[0] >= '0' && tmp[0] <= '2') | ||
444 | set_using_sysemu(tmp[0] - '0'); | ||
445 | return count; /*We use the first char, but pretend to write everything*/ | ||
446 | } | ||
447 | |||
448 | int __init make_proc_sysemu(void) | ||
449 | { | ||
450 | struct proc_dir_entry *ent; | ||
451 | if (!sysemu_supported) | ||
452 | return 0; | ||
453 | |||
454 | ent = create_proc_entry("sysemu", 0600, &proc_root); | ||
455 | |||
456 | if (ent == NULL) | ||
457 | { | ||
458 | printk("Failed to register /proc/sysemu\n"); | ||
459 | return(0); | ||
460 | } | ||
461 | |||
462 | ent->read_proc = proc_read_sysemu; | ||
463 | ent->write_proc = proc_write_sysemu; | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | late_initcall(make_proc_sysemu); | ||
469 | |||
470 | int singlestepping(void * t) | ||
471 | { | ||
472 | struct task_struct *task = t ? t : current; | ||
473 | |||
474 | if ( ! (task->ptrace & PT_DTRACE) ) | ||
475 | return(0); | ||
476 | |||
477 | if (task->thread.singlestep_syscall) | ||
478 | return(1); | ||
479 | |||
480 | return 2; | ||
481 | } | ||
482 | |||
483 | unsigned long arch_align_stack(unsigned long sp) | ||
484 | { | ||
485 | if (randomize_va_space) | ||
486 | sp -= get_random_int() % 8192; | ||
487 | return sp & ~0xf; | ||
488 | } | ||
489 | |||
490 | |||
491 | /* | ||
492 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
493 | * Emacs will notice this stuff at the end of the file and automatically | ||
494 | * adjust the settings for this buffer only. This must remain at the end | ||
495 | * of the file. | ||
496 | * --------------------------------------------------------------------------- | ||
497 | * Local variables: | ||
498 | * c-file-style: "linux" | ||
499 | * End: | ||
500 | */ | ||
diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c new file mode 100644 index 000000000000..3a99ee6d94eb --- /dev/null +++ b/arch/um/kernel/ptrace.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/mm.h" | ||
8 | #include "linux/errno.h" | ||
9 | #include "linux/smp_lock.h" | ||
10 | #include "linux/security.h" | ||
11 | #include "linux/ptrace.h" | ||
12 | #include "linux/audit.h" | ||
13 | #ifdef CONFIG_PROC_MM | ||
14 | #include "linux/proc_mm.h" | ||
15 | #endif | ||
16 | #include "asm/ptrace.h" | ||
17 | #include "asm/uaccess.h" | ||
18 | #include "kern_util.h" | ||
19 | #include "skas_ptrace.h" | ||
20 | #include "sysdep/ptrace.h" | ||
21 | |||
22 | /* | ||
23 | * Called by kernel/ptrace.c when detaching.. | ||
24 | */ | ||
25 | void ptrace_disable(struct task_struct *child) | ||
26 | { | ||
27 | child->ptrace &= ~PT_DTRACE; | ||
28 | child->thread.singlestep_syscall = 0; | ||
29 | } | ||
30 | |||
31 | long sys_ptrace(long request, long pid, long addr, long data) | ||
32 | { | ||
33 | struct task_struct *child; | ||
34 | int i, ret; | ||
35 | |||
36 | lock_kernel(); | ||
37 | ret = -EPERM; | ||
38 | if (request == PTRACE_TRACEME) { | ||
39 | /* are we already being traced? */ | ||
40 | if (current->ptrace & PT_PTRACED) | ||
41 | goto out; | ||
42 | |||
43 | ret = security_ptrace(current->parent, current); | ||
44 | if (ret) | ||
45 | goto out; | ||
46 | |||
47 | /* set the ptrace bit in the process flags. */ | ||
48 | current->ptrace |= PT_PTRACED; | ||
49 | ret = 0; | ||
50 | goto out; | ||
51 | } | ||
52 | ret = -ESRCH; | ||
53 | read_lock(&tasklist_lock); | ||
54 | child = find_task_by_pid(pid); | ||
55 | if (child) | ||
56 | get_task_struct(child); | ||
57 | read_unlock(&tasklist_lock); | ||
58 | if (!child) | ||
59 | goto out; | ||
60 | |||
61 | ret = -EPERM; | ||
62 | if (pid == 1) /* you may not mess with init */ | ||
63 | goto out_tsk; | ||
64 | |||
65 | if (request == PTRACE_ATTACH) { | ||
66 | ret = ptrace_attach(child); | ||
67 | goto out_tsk; | ||
68 | } | ||
69 | |||
70 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
71 | if (ret < 0) | ||
72 | goto out_tsk; | ||
73 | |||
74 | switch (request) { | ||
75 | /* when I and D space are separate, these will need to be fixed. */ | ||
76 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
77 | case PTRACE_PEEKDATA: { | ||
78 | unsigned long tmp; | ||
79 | int copied; | ||
80 | |||
81 | ret = -EIO; | ||
82 | copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); | ||
83 | if (copied != sizeof(tmp)) | ||
84 | break; | ||
85 | ret = put_user(tmp, (unsigned long __user *) data); | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | /* read the word at location addr in the USER area. */ | ||
90 | case PTRACE_PEEKUSR: { | ||
91 | unsigned long tmp; | ||
92 | |||
93 | ret = -EIO; | ||
94 | if ((addr & 3) || addr < 0) | ||
95 | break; | ||
96 | |||
97 | tmp = 0; /* Default return condition */ | ||
98 | if(addr < MAX_REG_OFFSET){ | ||
99 | tmp = getreg(child, addr); | ||
100 | } | ||
101 | else if((addr >= offsetof(struct user, u_debugreg[0])) && | ||
102 | (addr <= offsetof(struct user, u_debugreg[7]))){ | ||
103 | addr -= offsetof(struct user, u_debugreg[0]); | ||
104 | addr = addr >> 2; | ||
105 | tmp = child->thread.arch.debugregs[addr]; | ||
106 | } | ||
107 | ret = put_user(tmp, (unsigned long __user *) data); | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | /* when I and D space are separate, this will have to be fixed. */ | ||
112 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
113 | case PTRACE_POKEDATA: | ||
114 | ret = -EIO; | ||
115 | if (access_process_vm(child, addr, &data, sizeof(data), | ||
116 | 1) != sizeof(data)) | ||
117 | break; | ||
118 | ret = 0; | ||
119 | break; | ||
120 | |||
121 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | ||
122 | ret = -EIO; | ||
123 | if ((addr & 3) || addr < 0) | ||
124 | break; | ||
125 | |||
126 | if (addr < MAX_REG_OFFSET) { | ||
127 | ret = putreg(child, addr, data); | ||
128 | break; | ||
129 | } | ||
130 | #if 0 /* XXX x86_64 */ | ||
131 | else if((addr >= offsetof(struct user, u_debugreg[0])) && | ||
132 | (addr <= offsetof(struct user, u_debugreg[7]))){ | ||
133 | addr -= offsetof(struct user, u_debugreg[0]); | ||
134 | addr = addr >> 2; | ||
135 | if((addr == 4) || (addr == 5)) break; | ||
136 | child->thread.arch.debugregs[addr] = data; | ||
137 | ret = 0; | ||
138 | } | ||
139 | #endif | ||
140 | |||
141 | break; | ||
142 | |||
143 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | ||
144 | case PTRACE_CONT: { /* restart after signal. */ | ||
145 | ret = -EIO; | ||
146 | if ((unsigned long) data > _NSIG) | ||
147 | break; | ||
148 | |||
149 | child->ptrace &= ~PT_DTRACE; | ||
150 | child->thread.singlestep_syscall = 0; | ||
151 | if (request == PTRACE_SYSCALL) { | ||
152 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
153 | } | ||
154 | else { | ||
155 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
156 | } | ||
157 | child->exit_code = data; | ||
158 | wake_up_process(child); | ||
159 | ret = 0; | ||
160 | break; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * make the child exit. Best I can do is send it a sigkill. | ||
165 | * perhaps it should be put in the status that it wants to | ||
166 | * exit. | ||
167 | */ | ||
168 | case PTRACE_KILL: { | ||
169 | ret = 0; | ||
170 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | ||
171 | break; | ||
172 | |||
173 | child->ptrace &= ~PT_DTRACE; | ||
174 | child->thread.singlestep_syscall = 0; | ||
175 | child->exit_code = SIGKILL; | ||
176 | wake_up_process(child); | ||
177 | break; | ||
178 | } | ||
179 | |||
180 | case PTRACE_SINGLESTEP: { /* set the trap flag. */ | ||
181 | ret = -EIO; | ||
182 | if ((unsigned long) data > _NSIG) | ||
183 | break; | ||
184 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
185 | child->ptrace |= PT_DTRACE; | ||
186 | child->thread.singlestep_syscall = 0; | ||
187 | child->exit_code = data; | ||
188 | /* give it a chance to run. */ | ||
189 | wake_up_process(child); | ||
190 | ret = 0; | ||
191 | break; | ||
192 | } | ||
193 | |||
194 | case PTRACE_DETACH: | ||
195 | /* detach a process that was attached. */ | ||
196 | ret = ptrace_detach(child, data); | ||
197 | break; | ||
198 | |||
199 | #ifdef PTRACE_GETREGS | ||
200 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | ||
201 | if (!access_ok(VERIFY_WRITE, (unsigned long *)data, | ||
202 | MAX_REG_OFFSET)) { | ||
203 | ret = -EIO; | ||
204 | break; | ||
205 | } | ||
206 | for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { | ||
207 | __put_user(getreg(child, i), | ||
208 | (unsigned long __user *) data); | ||
209 | data += sizeof(long); | ||
210 | } | ||
211 | ret = 0; | ||
212 | break; | ||
213 | } | ||
214 | #endif | ||
215 | #ifdef PTRACE_SETREGS | ||
216 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | ||
217 | unsigned long tmp = 0; | ||
218 | if (!access_ok(VERIFY_READ, (unsigned *)data, | ||
219 | MAX_REG_OFFSET)) { | ||
220 | ret = -EIO; | ||
221 | break; | ||
222 | } | ||
223 | for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { | ||
224 | __get_user(tmp, (unsigned long __user *) data); | ||
225 | putreg(child, i, tmp); | ||
226 | data += sizeof(long); | ||
227 | } | ||
228 | ret = 0; | ||
229 | break; | ||
230 | } | ||
231 | #endif | ||
232 | #ifdef PTRACE_GETFPREGS | ||
233 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | ||
234 | ret = get_fpregs(data, child); | ||
235 | break; | ||
236 | #endif | ||
237 | #ifdef PTRACE_SETFPREGS | ||
238 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | ||
239 | ret = set_fpregs(data, child); | ||
240 | break; | ||
241 | #endif | ||
242 | #ifdef PTRACE_GETFPXREGS | ||
243 | case PTRACE_GETFPXREGS: /* Get the child FPU state. */ | ||
244 | ret = get_fpxregs(data, child); | ||
245 | break; | ||
246 | #endif | ||
247 | #ifdef PTRACE_SETFPXREGS | ||
248 | case PTRACE_SETFPXREGS: /* Set the child FPU state. */ | ||
249 | ret = set_fpxregs(data, child); | ||
250 | break; | ||
251 | #endif | ||
252 | case PTRACE_FAULTINFO: { | ||
253 | struct ptrace_faultinfo fault; | ||
254 | |||
255 | fault = ((struct ptrace_faultinfo) | ||
256 | { .is_write = child->thread.err, | ||
257 | .addr = child->thread.cr2 }); | ||
258 | ret = copy_to_user((unsigned long __user *) data, &fault, | ||
259 | sizeof(fault)); | ||
260 | if(ret) | ||
261 | break; | ||
262 | break; | ||
263 | } | ||
264 | case PTRACE_SIGPENDING: | ||
265 | ret = copy_to_user((unsigned long __user *) data, | ||
266 | &child->pending.signal, | ||
267 | sizeof(child->pending.signal)); | ||
268 | break; | ||
269 | |||
270 | case PTRACE_LDT: { | ||
271 | struct ptrace_ldt ldt; | ||
272 | |||
273 | if(copy_from_user(&ldt, (unsigned long __user *) data, | ||
274 | sizeof(ldt))){ | ||
275 | ret = -EIO; | ||
276 | break; | ||
277 | } | ||
278 | |||
279 | /* This one is confusing, so just punt and return -EIO for | ||
280 | * now | ||
281 | */ | ||
282 | ret = -EIO; | ||
283 | break; | ||
284 | } | ||
285 | #ifdef CONFIG_PROC_MM | ||
286 | case PTRACE_SWITCH_MM: { | ||
287 | struct mm_struct *old = child->mm; | ||
288 | struct mm_struct *new = proc_mm_get_mm(data); | ||
289 | |||
290 | if(IS_ERR(new)){ | ||
291 | ret = PTR_ERR(new); | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | atomic_inc(&new->mm_users); | ||
296 | child->mm = new; | ||
297 | child->active_mm = new; | ||
298 | mmput(old); | ||
299 | ret = 0; | ||
300 | break; | ||
301 | } | ||
302 | #endif | ||
303 | default: | ||
304 | ret = ptrace_request(child, request, addr, data); | ||
305 | break; | ||
306 | } | ||
307 | out_tsk: | ||
308 | put_task_struct(child); | ||
309 | out: | ||
310 | unlock_kernel(); | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, | ||
315 | int error_code) | ||
316 | { | ||
317 | struct siginfo info; | ||
318 | |||
319 | memset(&info, 0, sizeof(info)); | ||
320 | info.si_signo = SIGTRAP; | ||
321 | info.si_code = TRAP_BRKPT; | ||
322 | |||
323 | /* User-mode eip? */ | ||
324 | info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL; | ||
325 | |||
326 | /* Send us the fakey SIGTRAP */ | ||
327 | force_sig_info(SIGTRAP, &info, tsk); | ||
328 | } | ||
329 | |||
330 | /* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and | ||
331 | * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check | ||
332 | */ | ||
333 | void syscall_trace(union uml_pt_regs *regs, int entryexit) | ||
334 | { | ||
335 | int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; | ||
336 | int tracesysgood; | ||
337 | |||
338 | if (unlikely(current->audit_context)) { | ||
339 | if (!entryexit) | ||
340 | audit_syscall_entry(current, | ||
341 | UPT_SYSCALL_NR(®s->regs), | ||
342 | UPT_SYSCALL_ARG1(®s->regs), | ||
343 | UPT_SYSCALL_ARG2(®s->regs), | ||
344 | UPT_SYSCALL_ARG3(®s->regs), | ||
345 | UPT_SYSCALL_ARG4(®s->regs)); | ||
346 | else | ||
347 | audit_syscall_exit(current, | ||
348 | UPT_SYSCALL_RET(®s->regs)); | ||
349 | } | ||
350 | |||
351 | /* Fake a debug trap */ | ||
352 | if (is_singlestep) | ||
353 | send_sigtrap(current, regs, 0); | ||
354 | |||
355 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
356 | return; | ||
357 | |||
358 | if (!(current->ptrace & PT_PTRACED)) | ||
359 | return; | ||
360 | |||
361 | /* the 0x80 provides a way for the tracing parent to distinguish | ||
362 | between a syscall stop and SIGTRAP delivery */ | ||
363 | tracesysgood = (current->ptrace & PT_TRACESYSGOOD); | ||
364 | ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0)); | ||
365 | |||
366 | if (entryexit) /* force do_signal() --> is_syscall() */ | ||
367 | set_thread_flag(TIF_SIGPENDING); | ||
368 | |||
369 | /* this isn't the same as continuing with a signal, but it will do | ||
370 | * for normal use. strace only continues with a signal if the | ||
371 | * stopping signal is not SIGTRAP. -brl | ||
372 | */ | ||
373 | if (current->exit_code) { | ||
374 | send_sig(current->exit_code, current, 1); | ||
375 | current->exit_code = 0; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
381 | * Emacs will notice this stuff at the end of the file and automatically | ||
382 | * adjust the settings for this buffer only. This must remain at the end | ||
383 | * of the file. | ||
384 | * --------------------------------------------------------------------------- | ||
385 | * Local variables: | ||
386 | * c-file-style: "linux" | ||
387 | * End: | ||
388 | */ | ||
diff --git a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c new file mode 100644 index 000000000000..207f89d74908 --- /dev/null +++ b/arch/um/kernel/reboot.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/module.h" | ||
7 | #include "linux/sched.h" | ||
8 | #include "user_util.h" | ||
9 | #include "kern_util.h" | ||
10 | #include "kern.h" | ||
11 | #include "os.h" | ||
12 | #include "mode.h" | ||
13 | #include "choose-mode.h" | ||
14 | |||
15 | #ifdef CONFIG_SMP | ||
16 | static void kill_idlers(int me) | ||
17 | { | ||
18 | #ifdef CONFIG_MODE_TT | ||
19 | struct task_struct *p; | ||
20 | int i; | ||
21 | |||
22 | for(i = 0; i < sizeof(idle_threads)/sizeof(idle_threads[0]); i++){ | ||
23 | p = idle_threads[i]; | ||
24 | if((p != NULL) && (p->thread.mode.tt.extern_pid != me)) | ||
25 | os_kill_process(p->thread.mode.tt.extern_pid, 0); | ||
26 | } | ||
27 | #endif | ||
28 | } | ||
29 | #endif | ||
30 | |||
31 | static void kill_off_processes(void) | ||
32 | { | ||
33 | CHOOSE_MODE(kill_off_processes_tt(), kill_off_processes_skas()); | ||
34 | #ifdef CONFIG_SMP | ||
35 | kill_idlers(os_getpid()); | ||
36 | #endif | ||
37 | } | ||
38 | |||
39 | void uml_cleanup(void) | ||
40 | { | ||
41 | kill_off_processes(); | ||
42 | do_uml_exitcalls(); | ||
43 | } | ||
44 | |||
45 | void machine_restart(char * __unused) | ||
46 | { | ||
47 | do_uml_exitcalls(); | ||
48 | kill_off_processes(); | ||
49 | CHOOSE_MODE(reboot_tt(), reboot_skas()); | ||
50 | } | ||
51 | |||
52 | EXPORT_SYMBOL(machine_restart); | ||
53 | |||
54 | void machine_power_off(void) | ||
55 | { | ||
56 | do_uml_exitcalls(); | ||
57 | kill_off_processes(); | ||
58 | CHOOSE_MODE(halt_tt(), halt_skas()); | ||
59 | } | ||
60 | |||
61 | EXPORT_SYMBOL(machine_power_off); | ||
62 | |||
63 | void machine_halt(void) | ||
64 | { | ||
65 | machine_power_off(); | ||
66 | } | ||
67 | |||
68 | EXPORT_SYMBOL(machine_halt); | ||
69 | |||
70 | /* | ||
71 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
72 | * Emacs will notice this stuff at the end of the file and automatically | ||
73 | * adjust the settings for this buffer only. This must remain at the end | ||
74 | * of the file. | ||
75 | * --------------------------------------------------------------------------- | ||
76 | * Local variables: | ||
77 | * c-file-style: "linux" | ||
78 | * End: | ||
79 | */ | ||
diff --git a/arch/um/kernel/resource.c b/arch/um/kernel/resource.c new file mode 100644 index 000000000000..32188e12e8af --- /dev/null +++ b/arch/um/kernel/resource.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/pci.h" | ||
7 | |||
8 | unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, | ||
9 | unsigned long start, unsigned long size) | ||
10 | { | ||
11 | return start; | ||
12 | } | ||
13 | |||
14 | /* | ||
15 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
16 | * Emacs will notice this stuff at the end of the file and automatically | ||
17 | * adjust the settings for this buffer only. This must remain at the end | ||
18 | * of the file. | ||
19 | * --------------------------------------------------------------------------- | ||
20 | * Local variables: | ||
21 | * c-file-style: "linux" | ||
22 | * End: | ||
23 | */ | ||
diff --git a/arch/um/kernel/sigio_kern.c b/arch/um/kernel/sigio_kern.c new file mode 100644 index 000000000000..229988463c4c --- /dev/null +++ b/arch/um/kernel/sigio_kern.c | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/kernel.h" | ||
7 | #include "linux/list.h" | ||
8 | #include "linux/slab.h" | ||
9 | #include "linux/signal.h" | ||
10 | #include "linux/interrupt.h" | ||
11 | #include "init.h" | ||
12 | #include "sigio.h" | ||
13 | #include "irq_user.h" | ||
14 | #include "irq_kern.h" | ||
15 | |||
16 | /* Protected by sigio_lock() called from write_sigio_workaround */ | ||
17 | static int sigio_irq_fd = -1; | ||
18 | |||
19 | static irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused) | ||
20 | { | ||
21 | read_sigio_fd(sigio_irq_fd); | ||
22 | reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ); | ||
23 | return(IRQ_HANDLED); | ||
24 | } | ||
25 | |||
26 | int write_sigio_irq(int fd) | ||
27 | { | ||
28 | int err; | ||
29 | |||
30 | err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt, | ||
31 | SA_INTERRUPT | SA_SAMPLE_RANDOM, "write sigio", | ||
32 | NULL); | ||
33 | if(err){ | ||
34 | printk("write_sigio_irq : um_request_irq failed, err = %d\n", | ||
35 | err); | ||
36 | return(-1); | ||
37 | } | ||
38 | sigio_irq_fd = fd; | ||
39 | return(0); | ||
40 | } | ||
41 | |||
42 | static DEFINE_SPINLOCK(sigio_spinlock); | ||
43 | |||
44 | void sigio_lock(void) | ||
45 | { | ||
46 | spin_lock(&sigio_spinlock); | ||
47 | } | ||
48 | |||
49 | void sigio_unlock(void) | ||
50 | { | ||
51 | spin_unlock(&sigio_spinlock); | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
56 | * Emacs will notice this stuff at the end of the file and automatically | ||
57 | * adjust the settings for this buffer only. This must remain at the end | ||
58 | * of the file. | ||
59 | * --------------------------------------------------------------------------- | ||
60 | * Local variables: | ||
61 | * c-file-style: "linux" | ||
62 | * End: | ||
63 | */ | ||
diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c new file mode 100644 index 000000000000..668df13d8c9d --- /dev/null +++ b/arch/um/kernel/sigio_user.c | |||
@@ -0,0 +1,431 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <termios.h> | ||
9 | #include <pty.h> | ||
10 | #include <signal.h> | ||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <sched.h> | ||
14 | #include <sys/socket.h> | ||
15 | #include <sys/poll.h> | ||
16 | #include "init.h" | ||
17 | #include "user.h" | ||
18 | #include "kern_util.h" | ||
19 | #include "user_util.h" | ||
20 | #include "sigio.h" | ||
21 | #include "helper.h" | ||
22 | #include "os.h" | ||
23 | |||
24 | /* Changed during early boot */ | ||
25 | int pty_output_sigio = 0; | ||
26 | int pty_close_sigio = 0; | ||
27 | |||
28 | /* Used as a flag during SIGIO testing early in boot */ | ||
29 | static volatile int got_sigio = 0; | ||
30 | |||
31 | void __init handler(int sig) | ||
32 | { | ||
33 | got_sigio = 1; | ||
34 | } | ||
35 | |||
36 | struct openpty_arg { | ||
37 | int master; | ||
38 | int slave; | ||
39 | int err; | ||
40 | }; | ||
41 | |||
42 | static void openpty_cb(void *arg) | ||
43 | { | ||
44 | struct openpty_arg *info = arg; | ||
45 | |||
46 | info->err = 0; | ||
47 | if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) | ||
48 | info->err = -errno; | ||
49 | } | ||
50 | |||
51 | void __init check_one_sigio(void (*proc)(int, int)) | ||
52 | { | ||
53 | struct sigaction old, new; | ||
54 | struct openpty_arg pty = { .master = -1, .slave = -1 }; | ||
55 | int master, slave, err; | ||
56 | |||
57 | initial_thread_cb(openpty_cb, &pty); | ||
58 | if(pty.err){ | ||
59 | printk("openpty failed, errno = %d\n", -pty.err); | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | master = pty.master; | ||
64 | slave = pty.slave; | ||
65 | |||
66 | if((master == -1) || (slave == -1)){ | ||
67 | printk("openpty failed to allocate a pty\n"); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | /* Not now, but complain so we now where we failed. */ | ||
72 | err = raw(master); | ||
73 | if (err < 0) | ||
74 | panic("check_sigio : __raw failed, errno = %d\n", -err); | ||
75 | |||
76 | err = os_sigio_async(master, slave); | ||
77 | if(err < 0) | ||
78 | panic("tty_fds : sigio_async failed, err = %d\n", -err); | ||
79 | |||
80 | if(sigaction(SIGIO, NULL, &old) < 0) | ||
81 | panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); | ||
82 | new = old; | ||
83 | new.sa_handler = handler; | ||
84 | if(sigaction(SIGIO, &new, NULL) < 0) | ||
85 | panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); | ||
86 | |||
87 | got_sigio = 0; | ||
88 | (*proc)(master, slave); | ||
89 | |||
90 | os_close_file(master); | ||
91 | os_close_file(slave); | ||
92 | |||
93 | if(sigaction(SIGIO, &old, NULL) < 0) | ||
94 | panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); | ||
95 | } | ||
96 | |||
97 | static void tty_output(int master, int slave) | ||
98 | { | ||
99 | int n; | ||
100 | char buf[512]; | ||
101 | |||
102 | printk("Checking that host ptys support output SIGIO..."); | ||
103 | |||
104 | memset(buf, 0, sizeof(buf)); | ||
105 | |||
106 | while(os_write_file(master, buf, sizeof(buf)) > 0) ; | ||
107 | if(errno != EAGAIN) | ||
108 | panic("check_sigio : write failed, errno = %d\n", errno); | ||
109 | while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; | ||
110 | |||
111 | if (got_sigio) { | ||
112 | printk("Yes\n"); | ||
113 | pty_output_sigio = 1; | ||
114 | } else if (n == -EAGAIN) { | ||
115 | printk("No, enabling workaround\n"); | ||
116 | } else { | ||
117 | panic("check_sigio : read failed, err = %d\n", n); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void tty_close(int master, int slave) | ||
122 | { | ||
123 | printk("Checking that host ptys support SIGIO on close..."); | ||
124 | |||
125 | os_close_file(slave); | ||
126 | if(got_sigio){ | ||
127 | printk("Yes\n"); | ||
128 | pty_close_sigio = 1; | ||
129 | } | ||
130 | else printk("No, enabling workaround\n"); | ||
131 | } | ||
132 | |||
133 | void __init check_sigio(void) | ||
134 | { | ||
135 | if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && | ||
136 | (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ | ||
137 | printk("No pseudo-terminals available - skipping pty SIGIO " | ||
138 | "check\n"); | ||
139 | return; | ||
140 | } | ||
141 | check_one_sigio(tty_output); | ||
142 | check_one_sigio(tty_close); | ||
143 | } | ||
144 | |||
145 | /* Protected by sigio_lock(), also used by sigio_cleanup, which is an | ||
146 | * exitcall. | ||
147 | */ | ||
148 | static int write_sigio_pid = -1; | ||
149 | |||
150 | /* These arrays are initialized before the sigio thread is started, and | ||
151 | * the descriptors closed after it is killed. So, it can't see them change. | ||
152 | * On the UML side, they are changed under the sigio_lock. | ||
153 | */ | ||
154 | static int write_sigio_fds[2] = { -1, -1 }; | ||
155 | static int sigio_private[2] = { -1, -1 }; | ||
156 | |||
157 | struct pollfds { | ||
158 | struct pollfd *poll; | ||
159 | int size; | ||
160 | int used; | ||
161 | }; | ||
162 | |||
163 | /* Protected by sigio_lock(). Used by the sigio thread, but the UML thread | ||
164 | * synchronizes with it. | ||
165 | */ | ||
166 | struct pollfds current_poll = { | ||
167 | .poll = NULL, | ||
168 | .size = 0, | ||
169 | .used = 0 | ||
170 | }; | ||
171 | |||
172 | struct pollfds next_poll = { | ||
173 | .poll = NULL, | ||
174 | .size = 0, | ||
175 | .used = 0 | ||
176 | }; | ||
177 | |||
178 | static int write_sigio_thread(void *unused) | ||
179 | { | ||
180 | struct pollfds *fds, tmp; | ||
181 | struct pollfd *p; | ||
182 | int i, n, respond_fd; | ||
183 | char c; | ||
184 | |||
185 | fds = ¤t_poll; | ||
186 | while(1){ | ||
187 | n = poll(fds->poll, fds->used, -1); | ||
188 | if(n < 0){ | ||
189 | if(errno == EINTR) continue; | ||
190 | printk("write_sigio_thread : poll returned %d, " | ||
191 | "errno = %d\n", n, errno); | ||
192 | } | ||
193 | for(i = 0; i < fds->used; i++){ | ||
194 | p = &fds->poll[i]; | ||
195 | if(p->revents == 0) continue; | ||
196 | if(p->fd == sigio_private[1]){ | ||
197 | n = os_read_file(sigio_private[1], &c, sizeof(c)); | ||
198 | if(n != sizeof(c)) | ||
199 | printk("write_sigio_thread : " | ||
200 | "read failed, err = %d\n", -n); | ||
201 | tmp = current_poll; | ||
202 | current_poll = next_poll; | ||
203 | next_poll = tmp; | ||
204 | respond_fd = sigio_private[1]; | ||
205 | } | ||
206 | else { | ||
207 | respond_fd = write_sigio_fds[1]; | ||
208 | fds->used--; | ||
209 | memmove(&fds->poll[i], &fds->poll[i + 1], | ||
210 | (fds->used - i) * sizeof(*fds->poll)); | ||
211 | } | ||
212 | |||
213 | n = os_write_file(respond_fd, &c, sizeof(c)); | ||
214 | if(n != sizeof(c)) | ||
215 | printk("write_sigio_thread : write failed, " | ||
216 | "err = %d\n", -n); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static int need_poll(int n) | ||
222 | { | ||
223 | if(n <= next_poll.size){ | ||
224 | next_poll.used = n; | ||
225 | return(0); | ||
226 | } | ||
227 | if(next_poll.poll != NULL) kfree(next_poll.poll); | ||
228 | next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd)); | ||
229 | if(next_poll.poll == NULL){ | ||
230 | printk("need_poll : failed to allocate new pollfds\n"); | ||
231 | next_poll.size = 0; | ||
232 | next_poll.used = 0; | ||
233 | return(-1); | ||
234 | } | ||
235 | next_poll.size = n; | ||
236 | next_poll.used = n; | ||
237 | return(0); | ||
238 | } | ||
239 | |||
240 | /* Must be called with sigio_lock held, because it's needed by the marked | ||
241 | * critical section. */ | ||
242 | static void update_thread(void) | ||
243 | { | ||
244 | unsigned long flags; | ||
245 | int n; | ||
246 | char c; | ||
247 | |||
248 | flags = set_signals(0); | ||
249 | n = os_write_file(sigio_private[0], &c, sizeof(c)); | ||
250 | if(n != sizeof(c)){ | ||
251 | printk("update_thread : write failed, err = %d\n", -n); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | n = os_read_file(sigio_private[0], &c, sizeof(c)); | ||
256 | if(n != sizeof(c)){ | ||
257 | printk("update_thread : read failed, err = %d\n", -n); | ||
258 | goto fail; | ||
259 | } | ||
260 | |||
261 | set_signals(flags); | ||
262 | return; | ||
263 | fail: | ||
264 | /* Critical section start */ | ||
265 | if(write_sigio_pid != -1) | ||
266 | os_kill_process(write_sigio_pid, 1); | ||
267 | write_sigio_pid = -1; | ||
268 | os_close_file(sigio_private[0]); | ||
269 | os_close_file(sigio_private[1]); | ||
270 | os_close_file(write_sigio_fds[0]); | ||
271 | os_close_file(write_sigio_fds[1]); | ||
272 | /* Critical section end */ | ||
273 | set_signals(flags); | ||
274 | } | ||
275 | |||
276 | int add_sigio_fd(int fd, int read) | ||
277 | { | ||
278 | int err = 0, i, n, events; | ||
279 | |||
280 | sigio_lock(); | ||
281 | for(i = 0; i < current_poll.used; i++){ | ||
282 | if(current_poll.poll[i].fd == fd) | ||
283 | goto out; | ||
284 | } | ||
285 | |||
286 | n = current_poll.used + 1; | ||
287 | err = need_poll(n); | ||
288 | if(err) | ||
289 | goto out; | ||
290 | |||
291 | for(i = 0; i < current_poll.used; i++) | ||
292 | next_poll.poll[i] = current_poll.poll[i]; | ||
293 | |||
294 | if(read) events = POLLIN; | ||
295 | else events = POLLOUT; | ||
296 | |||
297 | next_poll.poll[n - 1] = ((struct pollfd) { .fd = fd, | ||
298 | .events = events, | ||
299 | .revents = 0 }); | ||
300 | update_thread(); | ||
301 | out: | ||
302 | sigio_unlock(); | ||
303 | return(err); | ||
304 | } | ||
305 | |||
306 | int ignore_sigio_fd(int fd) | ||
307 | { | ||
308 | struct pollfd *p; | ||
309 | int err = 0, i, n = 0; | ||
310 | |||
311 | sigio_lock(); | ||
312 | for(i = 0; i < current_poll.used; i++){ | ||
313 | if(current_poll.poll[i].fd == fd) break; | ||
314 | } | ||
315 | if(i == current_poll.used) | ||
316 | goto out; | ||
317 | |||
318 | err = need_poll(current_poll.used - 1); | ||
319 | if(err) | ||
320 | goto out; | ||
321 | |||
322 | for(i = 0; i < current_poll.used; i++){ | ||
323 | p = ¤t_poll.poll[i]; | ||
324 | if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i]; | ||
325 | } | ||
326 | if(n == i){ | ||
327 | printk("ignore_sigio_fd : fd %d not found\n", fd); | ||
328 | err = -1; | ||
329 | goto out; | ||
330 | } | ||
331 | |||
332 | update_thread(); | ||
333 | out: | ||
334 | sigio_unlock(); | ||
335 | return(err); | ||
336 | } | ||
337 | |||
338 | static int setup_initial_poll(int fd) | ||
339 | { | ||
340 | struct pollfd *p; | ||
341 | |||
342 | p = um_kmalloc(sizeof(struct pollfd)); | ||
343 | if(p == NULL){ | ||
344 | printk("setup_initial_poll : failed to allocate poll\n"); | ||
345 | return(-1); | ||
346 | } | ||
347 | *p = ((struct pollfd) { .fd = fd, | ||
348 | .events = POLLIN, | ||
349 | .revents = 0 }); | ||
350 | current_poll = ((struct pollfds) { .poll = p, | ||
351 | .used = 1, | ||
352 | .size = 1 }); | ||
353 | return(0); | ||
354 | } | ||
355 | |||
356 | void write_sigio_workaround(void) | ||
357 | { | ||
358 | unsigned long stack; | ||
359 | int err; | ||
360 | |||
361 | sigio_lock(); | ||
362 | if(write_sigio_pid != -1) | ||
363 | goto out; | ||
364 | |||
365 | err = os_pipe(write_sigio_fds, 1, 1); | ||
366 | if(err < 0){ | ||
367 | printk("write_sigio_workaround - os_pipe 1 failed, " | ||
368 | "err = %d\n", -err); | ||
369 | goto out; | ||
370 | } | ||
371 | err = os_pipe(sigio_private, 1, 1); | ||
372 | if(err < 0){ | ||
373 | printk("write_sigio_workaround - os_pipe 2 failed, " | ||
374 | "err = %d\n", -err); | ||
375 | goto out_close1; | ||
376 | } | ||
377 | if(setup_initial_poll(sigio_private[1])) | ||
378 | goto out_close2; | ||
379 | |||
380 | write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, | ||
381 | CLONE_FILES | CLONE_VM, &stack, 0); | ||
382 | |||
383 | if(write_sigio_pid < 0) goto out_close2; | ||
384 | |||
385 | if(write_sigio_irq(write_sigio_fds[0])) | ||
386 | goto out_kill; | ||
387 | |||
388 | out: | ||
389 | sigio_unlock(); | ||
390 | return; | ||
391 | |||
392 | out_kill: | ||
393 | os_kill_process(write_sigio_pid, 1); | ||
394 | write_sigio_pid = -1; | ||
395 | out_close2: | ||
396 | os_close_file(sigio_private[0]); | ||
397 | os_close_file(sigio_private[1]); | ||
398 | out_close1: | ||
399 | os_close_file(write_sigio_fds[0]); | ||
400 | os_close_file(write_sigio_fds[1]); | ||
401 | sigio_unlock(); | ||
402 | } | ||
403 | |||
404 | int read_sigio_fd(int fd) | ||
405 | { | ||
406 | int n; | ||
407 | char c; | ||
408 | |||
409 | n = os_read_file(fd, &c, sizeof(c)); | ||
410 | if(n != sizeof(c)){ | ||
411 | if(n < 0) { | ||
412 | printk("read_sigio_fd - read failed, err = %d\n", -n); | ||
413 | return(n); | ||
414 | } | ||
415 | else { | ||
416 | printk("read_sigio_fd - short read, bytes = %d\n", n); | ||
417 | return(-EIO); | ||
418 | } | ||
419 | } | ||
420 | return(n); | ||
421 | } | ||
422 | |||
423 | static void sigio_cleanup(void) | ||
424 | { | ||
425 | if (write_sigio_pid != -1) { | ||
426 | os_kill_process(write_sigio_pid, 1); | ||
427 | write_sigio_pid = -1; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | __uml_exitcall(sigio_cleanup); | ||
diff --git a/arch/um/kernel/signal_kern.c b/arch/um/kernel/signal_kern.c new file mode 100644 index 000000000000..7807a3e8c426 --- /dev/null +++ b/arch/um/kernel/signal_kern.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/stddef.h" | ||
8 | #include "linux/sys.h" | ||
9 | #include "linux/sched.h" | ||
10 | #include "linux/wait.h" | ||
11 | #include "linux/kernel.h" | ||
12 | #include "linux/smp_lock.h" | ||
13 | #include "linux/module.h" | ||
14 | #include "linux/slab.h" | ||
15 | #include "linux/tty.h" | ||
16 | #include "linux/binfmts.h" | ||
17 | #include "linux/ptrace.h" | ||
18 | #include "asm/signal.h" | ||
19 | #include "asm/uaccess.h" | ||
20 | #include "asm/unistd.h" | ||
21 | #include "user_util.h" | ||
22 | #include "asm/ucontext.h" | ||
23 | #include "kern_util.h" | ||
24 | #include "signal_kern.h" | ||
25 | #include "signal_user.h" | ||
26 | #include "kern.h" | ||
27 | #include "frame_kern.h" | ||
28 | #include "sigcontext.h" | ||
29 | #include "mode.h" | ||
30 | |||
31 | EXPORT_SYMBOL(block_signals); | ||
32 | EXPORT_SYMBOL(unblock_signals); | ||
33 | |||
34 | #define _S(nr) (1<<((nr)-1)) | ||
35 | |||
36 | #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) | ||
37 | |||
38 | /* | ||
39 | * OK, we're invoking a handler | ||
40 | */ | ||
41 | static int handle_signal(struct pt_regs *regs, unsigned long signr, | ||
42 | struct k_sigaction *ka, siginfo_t *info, | ||
43 | sigset_t *oldset) | ||
44 | { | ||
45 | unsigned long sp; | ||
46 | int err; | ||
47 | |||
48 | /* Always make any pending restarted system calls return -EINTR */ | ||
49 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
50 | |||
51 | /* Did we come from a system call? */ | ||
52 | if(PT_REGS_SYSCALL_NR(regs) >= 0){ | ||
53 | /* If so, check system call restarting.. */ | ||
54 | switch(PT_REGS_SYSCALL_RET(regs)){ | ||
55 | case -ERESTART_RESTARTBLOCK: | ||
56 | case -ERESTARTNOHAND: | ||
57 | PT_REGS_SYSCALL_RET(regs) = -EINTR; | ||
58 | break; | ||
59 | |||
60 | case -ERESTARTSYS: | ||
61 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
62 | PT_REGS_SYSCALL_RET(regs) = -EINTR; | ||
63 | break; | ||
64 | } | ||
65 | /* fallthrough */ | ||
66 | case -ERESTARTNOINTR: | ||
67 | PT_REGS_RESTART_SYSCALL(regs); | ||
68 | PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); | ||
69 | break; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | sp = PT_REGS_SP(regs); | ||
74 | if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) | ||
75 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
76 | |||
77 | #ifdef CONFIG_ARCH_HAS_SC_SIGNALS | ||
78 | if(!(ka->sa.sa_flags & SA_SIGINFO)) | ||
79 | err = setup_signal_stack_sc(sp, signr, ka, regs, oldset); | ||
80 | else | ||
81 | #endif | ||
82 | err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset); | ||
83 | |||
84 | if(err){ | ||
85 | spin_lock_irq(¤t->sighand->siglock); | ||
86 | current->blocked = *oldset; | ||
87 | recalc_sigpending(); | ||
88 | spin_unlock_irq(¤t->sighand->siglock); | ||
89 | force_sigsegv(signr, current); | ||
90 | } | ||
91 | else if(!(ka->sa.sa_flags & SA_NODEFER)){ | ||
92 | spin_lock_irq(¤t->sighand->siglock); | ||
93 | sigorsets(¤t->blocked, ¤t->blocked, | ||
94 | &ka->sa.sa_mask); | ||
95 | sigaddset(¤t->blocked, signr); | ||
96 | recalc_sigpending(); | ||
97 | spin_unlock_irq(¤t->sighand->siglock); | ||
98 | } | ||
99 | |||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset) | ||
104 | { | ||
105 | struct k_sigaction ka_copy; | ||
106 | siginfo_t info; | ||
107 | int sig, handled_sig = 0; | ||
108 | |||
109 | while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){ | ||
110 | handled_sig = 1; | ||
111 | /* Whee! Actually deliver the signal. */ | ||
112 | if(!handle_signal(regs, sig, &ka_copy, &info, oldset)) | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | /* Did we come from a system call? */ | ||
117 | if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){ | ||
118 | /* Restart the system call - no handlers present */ | ||
119 | if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND || | ||
120 | PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS || | ||
121 | PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){ | ||
122 | PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); | ||
123 | PT_REGS_RESTART_SYSCALL(regs); | ||
124 | } | ||
125 | else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){ | ||
126 | PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall; | ||
127 | PT_REGS_RESTART_SYSCALL(regs); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* This closes a way to execute a system call on the host. If | ||
132 | * you set a breakpoint on a system call instruction and singlestep | ||
133 | * from it, the tracing thread used to PTRACE_SINGLESTEP the process | ||
134 | * rather than PTRACE_SYSCALL it, allowing the system call to execute | ||
135 | * on the host. The tracing thread will check this flag and | ||
136 | * PTRACE_SYSCALL if necessary. | ||
137 | */ | ||
138 | if(current->ptrace & PT_DTRACE) | ||
139 | current->thread.singlestep_syscall = | ||
140 | is_syscall(PT_REGS_IP(¤t->thread.regs)); | ||
141 | return(handled_sig); | ||
142 | } | ||
143 | |||
144 | int do_signal(void) | ||
145 | { | ||
146 | return(kern_do_signal(¤t->thread.regs, ¤t->blocked)); | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Atomically swap in the new signal mask, and wait for a signal. | ||
151 | */ | ||
152 | long sys_sigsuspend(int history0, int history1, old_sigset_t mask) | ||
153 | { | ||
154 | sigset_t saveset; | ||
155 | |||
156 | mask &= _BLOCKABLE; | ||
157 | spin_lock_irq(¤t->sighand->siglock); | ||
158 | saveset = current->blocked; | ||
159 | siginitset(¤t->blocked, mask); | ||
160 | recalc_sigpending(); | ||
161 | spin_unlock_irq(¤t->sighand->siglock); | ||
162 | |||
163 | PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR; | ||
164 | while (1) { | ||
165 | current->state = TASK_INTERRUPTIBLE; | ||
166 | schedule(); | ||
167 | if(kern_do_signal(¤t->thread.regs, &saveset)) | ||
168 | return(-EINTR); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) | ||
173 | { | ||
174 | sigset_t saveset, newset; | ||
175 | |||
176 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
177 | if (sigsetsize != sizeof(sigset_t)) | ||
178 | return -EINVAL; | ||
179 | |||
180 | if (copy_from_user(&newset, unewset, sizeof(newset))) | ||
181 | return -EFAULT; | ||
182 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
183 | |||
184 | spin_lock_irq(¤t->sighand->siglock); | ||
185 | saveset = current->blocked; | ||
186 | current->blocked = newset; | ||
187 | recalc_sigpending(); | ||
188 | spin_unlock_irq(¤t->sighand->siglock); | ||
189 | |||
190 | PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR; | ||
191 | while (1) { | ||
192 | current->state = TASK_INTERRUPTIBLE; | ||
193 | schedule(); | ||
194 | if (kern_do_signal(¤t->thread.regs, &saveset)) | ||
195 | return(-EINTR); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
200 | { | ||
201 | return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs))); | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
206 | * Emacs will notice this stuff at the end of the file and automatically | ||
207 | * adjust the settings for this buffer only. This must remain at the end | ||
208 | * of the file. | ||
209 | * --------------------------------------------------------------------------- | ||
210 | * Local variables: | ||
211 | * c-file-style: "linux" | ||
212 | * End: | ||
213 | */ | ||
diff --git a/arch/um/kernel/signal_user.c b/arch/um/kernel/signal_user.c new file mode 100644 index 000000000000..62f457835fb1 --- /dev/null +++ b/arch/um/kernel/signal_user.c | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <signal.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdarg.h> | ||
12 | #include <string.h> | ||
13 | #include <sys/mman.h> | ||
14 | #include "user_util.h" | ||
15 | #include "kern_util.h" | ||
16 | #include "user.h" | ||
17 | #include "signal_user.h" | ||
18 | #include "signal_kern.h" | ||
19 | #include "sysdep/sigcontext.h" | ||
20 | #include "sigcontext.h" | ||
21 | |||
22 | void set_sigstack(void *sig_stack, int size) | ||
23 | { | ||
24 | stack_t stack = ((stack_t) { .ss_flags = 0, | ||
25 | .ss_sp = (__ptr_t) sig_stack, | ||
26 | .ss_size = size - sizeof(void *) }); | ||
27 | |||
28 | if(sigaltstack(&stack, NULL) != 0) | ||
29 | panic("enabling signal stack failed, errno = %d\n", errno); | ||
30 | } | ||
31 | |||
32 | void set_handler(int sig, void (*handler)(int), int flags, ...) | ||
33 | { | ||
34 | struct sigaction action; | ||
35 | va_list ap; | ||
36 | int mask; | ||
37 | |||
38 | va_start(ap, flags); | ||
39 | action.sa_handler = handler; | ||
40 | sigemptyset(&action.sa_mask); | ||
41 | while((mask = va_arg(ap, int)) != -1){ | ||
42 | sigaddset(&action.sa_mask, mask); | ||
43 | } | ||
44 | va_end(ap); | ||
45 | action.sa_flags = flags; | ||
46 | action.sa_restorer = NULL; | ||
47 | if(sigaction(sig, &action, NULL) < 0) | ||
48 | panic("sigaction failed"); | ||
49 | } | ||
50 | |||
51 | int change_sig(int signal, int on) | ||
52 | { | ||
53 | sigset_t sigset, old; | ||
54 | |||
55 | sigemptyset(&sigset); | ||
56 | sigaddset(&sigset, signal); | ||
57 | sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old); | ||
58 | return(!sigismember(&old, signal)); | ||
59 | } | ||
60 | |||
61 | /* Both here and in set/get_signal we don't touch SIGPROF, because we must not | ||
62 | * disable profiling; it's safe because the profiling code does not interact | ||
63 | * with the kernel code at all.*/ | ||
64 | |||
65 | static void change_signals(int type) | ||
66 | { | ||
67 | sigset_t mask; | ||
68 | |||
69 | sigemptyset(&mask); | ||
70 | sigaddset(&mask, SIGVTALRM); | ||
71 | sigaddset(&mask, SIGALRM); | ||
72 | sigaddset(&mask, SIGIO); | ||
73 | if(sigprocmask(type, &mask, NULL) < 0) | ||
74 | panic("Failed to change signal mask - errno = %d", errno); | ||
75 | } | ||
76 | |||
77 | void block_signals(void) | ||
78 | { | ||
79 | change_signals(SIG_BLOCK); | ||
80 | } | ||
81 | |||
82 | void unblock_signals(void) | ||
83 | { | ||
84 | change_signals(SIG_UNBLOCK); | ||
85 | } | ||
86 | |||
87 | /* These are the asynchronous signals. SIGVTALRM and SIGARLM are handled | ||
88 | * together under SIGVTALRM_BIT. SIGPROF is excluded because we want to | ||
89 | * be able to profile all of UML, not just the non-critical sections. If | ||
90 | * profiling is not thread-safe, then that is not my problem. We can disable | ||
91 | * profiling when SMP is enabled in that case. | ||
92 | */ | ||
93 | #define SIGIO_BIT 0 | ||
94 | #define SIGVTALRM_BIT 1 | ||
95 | |||
96 | static int enable_mask(sigset_t *mask) | ||
97 | { | ||
98 | int sigs; | ||
99 | |||
100 | sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT; | ||
101 | sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT; | ||
102 | sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT; | ||
103 | return(sigs); | ||
104 | } | ||
105 | |||
106 | int get_signals(void) | ||
107 | { | ||
108 | sigset_t mask; | ||
109 | |||
110 | if(sigprocmask(SIG_SETMASK, NULL, &mask) < 0) | ||
111 | panic("Failed to get signal mask"); | ||
112 | return(enable_mask(&mask)); | ||
113 | } | ||
114 | |||
115 | int set_signals(int enable) | ||
116 | { | ||
117 | sigset_t mask; | ||
118 | int ret; | ||
119 | |||
120 | sigemptyset(&mask); | ||
121 | if(enable & (1 << SIGIO_BIT)) | ||
122 | sigaddset(&mask, SIGIO); | ||
123 | if(enable & (1 << SIGVTALRM_BIT)){ | ||
124 | sigaddset(&mask, SIGVTALRM); | ||
125 | sigaddset(&mask, SIGALRM); | ||
126 | } | ||
127 | |||
128 | /* This is safe - sigprocmask is guaranteed to copy locally the | ||
129 | * value of new_set, do his work and then, at the end, write to | ||
130 | * old_set. | ||
131 | */ | ||
132 | if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) | ||
133 | panic("Failed to enable signals"); | ||
134 | ret = enable_mask(&mask); | ||
135 | sigemptyset(&mask); | ||
136 | if((enable & (1 << SIGIO_BIT)) == 0) | ||
137 | sigaddset(&mask, SIGIO); | ||
138 | if((enable & (1 << SIGVTALRM_BIT)) == 0){ | ||
139 | sigaddset(&mask, SIGVTALRM); | ||
140 | sigaddset(&mask, SIGALRM); | ||
141 | } | ||
142 | if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) | ||
143 | panic("Failed to block signals"); | ||
144 | |||
145 | return(ret); | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
150 | * Emacs will notice this stuff at the end of the file and automatically | ||
151 | * adjust the settings for this buffer only. This must remain at the end | ||
152 | * of the file. | ||
153 | * --------------------------------------------------------------------------- | ||
154 | * Local variables: | ||
155 | * c-file-style: "linux" | ||
156 | * End: | ||
157 | */ | ||
diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile new file mode 100644 index 000000000000..d37d1bfcd6f7 --- /dev/null +++ b/arch/um/kernel/skas/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | # | ||
2 | # Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | obj-y := exec_kern.o mem.o mem_user.o mmu.o process.o process_kern.o \ | ||
7 | syscall_kern.o syscall_user.o time.o tlb.o trap_user.o uaccess.o \ | ||
8 | |||
9 | subdir- := util | ||
10 | |||
11 | USER_OBJS := process.o time.o | ||
12 | |||
13 | include arch/um/scripts/Makefile.rules | ||
diff --git a/arch/um/kernel/skas/exec_kern.c b/arch/um/kernel/skas/exec_kern.c new file mode 100644 index 000000000000..c6b4d5dba789 --- /dev/null +++ b/arch/um/kernel/skas/exec_kern.c | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/kernel.h" | ||
7 | #include "asm/current.h" | ||
8 | #include "asm/page.h" | ||
9 | #include "asm/signal.h" | ||
10 | #include "asm/ptrace.h" | ||
11 | #include "asm/uaccess.h" | ||
12 | #include "asm/mmu_context.h" | ||
13 | #include "tlb.h" | ||
14 | #include "skas.h" | ||
15 | #include "um_mmu.h" | ||
16 | #include "os.h" | ||
17 | |||
18 | void flush_thread_skas(void) | ||
19 | { | ||
20 | force_flush_all(); | ||
21 | switch_mm_skas(current->mm->context.skas.mm_fd); | ||
22 | } | ||
23 | |||
24 | void start_thread_skas(struct pt_regs *regs, unsigned long eip, | ||
25 | unsigned long esp) | ||
26 | { | ||
27 | set_fs(USER_DS); | ||
28 | PT_REGS_IP(regs) = eip; | ||
29 | PT_REGS_SP(regs) = esp; | ||
30 | } | ||
31 | |||
32 | /* | ||
33 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
34 | * Emacs will notice this stuff at the end of the file and automatically | ||
35 | * adjust the settings for this buffer only. This must remain at the end | ||
36 | * of the file. | ||
37 | * --------------------------------------------------------------------------- | ||
38 | * Local variables: | ||
39 | * c-file-style: "linux" | ||
40 | * End: | ||
41 | */ | ||
diff --git a/arch/um/kernel/skas/include/mmu-skas.h b/arch/um/kernel/skas/include/mmu-skas.h new file mode 100644 index 000000000000..4cd60d7213f3 --- /dev/null +++ b/arch/um/kernel/skas/include/mmu-skas.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SKAS_MMU_H | ||
7 | #define __SKAS_MMU_H | ||
8 | |||
9 | struct mmu_context_skas { | ||
10 | int mm_fd; | ||
11 | }; | ||
12 | |||
13 | #endif | ||
14 | |||
15 | /* | ||
16 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
17 | * Emacs will notice this stuff at the end of the file and automatically | ||
18 | * adjust the settings for this buffer only. This must remain at the end | ||
19 | * of the file. | ||
20 | * --------------------------------------------------------------------------- | ||
21 | * Local variables: | ||
22 | * c-file-style: "linux" | ||
23 | * End: | ||
24 | */ | ||
diff --git a/arch/um/kernel/skas/include/mode-skas.h b/arch/um/kernel/skas/include/mode-skas.h new file mode 100644 index 000000000000..c1e33bd788db --- /dev/null +++ b/arch/um/kernel/skas/include/mode-skas.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __MODE_SKAS_H__ | ||
7 | #define __MODE_SKAS_H__ | ||
8 | |||
9 | #include <sysdep/ptrace.h> | ||
10 | |||
11 | extern unsigned long exec_regs[]; | ||
12 | extern unsigned long exec_fp_regs[]; | ||
13 | extern unsigned long exec_fpx_regs[]; | ||
14 | extern int have_fpx_regs; | ||
15 | |||
16 | extern void user_time_init_skas(void); | ||
17 | extern void sig_handler_common_skas(int sig, void *sc_ptr); | ||
18 | extern void halt_skas(void); | ||
19 | extern void reboot_skas(void); | ||
20 | extern void kill_off_processes_skas(void); | ||
21 | extern int is_skas_winch(int pid, int fd, void *data); | ||
22 | |||
23 | #endif | ||
24 | |||
25 | /* | ||
26 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
27 | * Emacs will notice this stuff at the end of the file and automatically | ||
28 | * adjust the settings for this buffer only. This must remain at the end | ||
29 | * of the file. | ||
30 | * --------------------------------------------------------------------------- | ||
31 | * Local variables: | ||
32 | * c-file-style: "linux" | ||
33 | * End: | ||
34 | */ | ||
diff --git a/arch/um/kernel/skas/include/mode_kern-skas.h b/arch/um/kernel/skas/include/mode_kern-skas.h new file mode 100644 index 000000000000..94c564962378 --- /dev/null +++ b/arch/um/kernel/skas/include/mode_kern-skas.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SKAS_MODE_KERN_H__ | ||
7 | #define __SKAS_MODE_KERN_H__ | ||
8 | |||
9 | #include "linux/sched.h" | ||
10 | #include "asm/page.h" | ||
11 | #include "asm/ptrace.h" | ||
12 | |||
13 | extern void flush_thread_skas(void); | ||
14 | extern void *switch_to_skas(void *prev, void *next); | ||
15 | extern void start_thread_skas(struct pt_regs *regs, unsigned long eip, | ||
16 | unsigned long esp); | ||
17 | extern int copy_thread_skas(int nr, unsigned long clone_flags, | ||
18 | unsigned long sp, unsigned long stack_top, | ||
19 | struct task_struct *p, struct pt_regs *regs); | ||
20 | extern void release_thread_skas(struct task_struct *task); | ||
21 | extern void exit_thread_skas(void); | ||
22 | extern void initial_thread_cb_skas(void (*proc)(void *), void *arg); | ||
23 | extern void init_idle_skas(void); | ||
24 | extern void flush_tlb_kernel_range_skas(unsigned long start, | ||
25 | unsigned long end); | ||
26 | extern void flush_tlb_kernel_vm_skas(void); | ||
27 | extern void __flush_tlb_one_skas(unsigned long addr); | ||
28 | extern void flush_tlb_range_skas(struct vm_area_struct *vma, | ||
29 | unsigned long start, unsigned long end); | ||
30 | extern void flush_tlb_mm_skas(struct mm_struct *mm); | ||
31 | extern void force_flush_all_skas(void); | ||
32 | extern long execute_syscall_skas(void *r); | ||
33 | extern void before_mem_skas(unsigned long unused); | ||
34 | extern unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out, | ||
35 | unsigned long *task_size_out); | ||
36 | extern int start_uml_skas(void); | ||
37 | extern int external_pid_skas(struct task_struct *task); | ||
38 | extern int thread_pid_skas(struct task_struct *task); | ||
39 | |||
40 | #define kmem_end_skas (host_task_size - 1024 * 1024) | ||
41 | |||
42 | #endif | ||
43 | |||
44 | /* | ||
45 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
46 | * Emacs will notice this stuff at the end of the file and automatically | ||
47 | * adjust the settings for this buffer only. This must remain at the end | ||
48 | * of the file. | ||
49 | * --------------------------------------------------------------------------- | ||
50 | * Local variables: | ||
51 | * c-file-style: "linux" | ||
52 | * End: | ||
53 | */ | ||
diff --git a/arch/um/kernel/skas/include/proc_mm.h b/arch/um/kernel/skas/include/proc_mm.h new file mode 100644 index 000000000000..cce61a679052 --- /dev/null +++ b/arch/um/kernel/skas/include/proc_mm.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SKAS_PROC_MM_H | ||
7 | #define __SKAS_PROC_MM_H | ||
8 | |||
9 | #define MM_MMAP 54 | ||
10 | #define MM_MUNMAP 55 | ||
11 | #define MM_MPROTECT 56 | ||
12 | #define MM_COPY_SEGMENTS 57 | ||
13 | |||
14 | struct mm_mmap { | ||
15 | unsigned long addr; | ||
16 | unsigned long len; | ||
17 | unsigned long prot; | ||
18 | unsigned long flags; | ||
19 | unsigned long fd; | ||
20 | unsigned long offset; | ||
21 | }; | ||
22 | |||
23 | struct mm_munmap { | ||
24 | unsigned long addr; | ||
25 | unsigned long len; | ||
26 | }; | ||
27 | |||
28 | struct mm_mprotect { | ||
29 | unsigned long addr; | ||
30 | unsigned long len; | ||
31 | unsigned int prot; | ||
32 | }; | ||
33 | |||
34 | struct proc_mm_op { | ||
35 | int op; | ||
36 | union { | ||
37 | struct mm_mmap mmap; | ||
38 | struct mm_munmap munmap; | ||
39 | struct mm_mprotect mprotect; | ||
40 | int copy_segments; | ||
41 | } u; | ||
42 | }; | ||
43 | |||
44 | #endif | ||
45 | |||
46 | /* | ||
47 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
48 | * Emacs will notice this stuff at the end of the file and automatically | ||
49 | * adjust the settings for this buffer only. This must remain at the end | ||
50 | * of the file. | ||
51 | * --------------------------------------------------------------------------- | ||
52 | * Local variables: | ||
53 | * c-file-style: "linux" | ||
54 | * End: | ||
55 | */ | ||
diff --git a/arch/um/kernel/skas/include/skas.h b/arch/um/kernel/skas/include/skas.h new file mode 100644 index 000000000000..f0702c2c7204 --- /dev/null +++ b/arch/um/kernel/skas/include/skas.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SKAS_H | ||
7 | #define __SKAS_H | ||
8 | |||
9 | #include "sysdep/ptrace.h" | ||
10 | |||
11 | extern int userspace_pid[]; | ||
12 | |||
13 | extern void switch_threads(void *me, void *next); | ||
14 | extern void thread_wait(void *sw, void *fb); | ||
15 | extern void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, | ||
16 | void (*handler)(int)); | ||
17 | extern int start_idle_thread(void *stack, void *switch_buf_ptr, | ||
18 | void **fork_buf_ptr); | ||
19 | extern int user_thread(unsigned long stack, int flags); | ||
20 | extern void userspace(union uml_pt_regs *regs); | ||
21 | extern void new_thread_proc(void *stack, void (*handler)(int sig)); | ||
22 | extern void remove_sigstack(void); | ||
23 | extern void new_thread_handler(int sig); | ||
24 | extern void handle_syscall(union uml_pt_regs *regs); | ||
25 | extern void map(int fd, unsigned long virt, unsigned long len, int r, int w, | ||
26 | int x, int phys_fd, unsigned long long offset); | ||
27 | extern int unmap(int fd, void *addr, unsigned long len); | ||
28 | extern int protect(int fd, unsigned long addr, unsigned long len, | ||
29 | int r, int w, int x); | ||
30 | extern void user_signal(int sig, union uml_pt_regs *regs); | ||
31 | extern int new_mm(int from); | ||
32 | extern void start_userspace(int cpu); | ||
33 | extern long execute_syscall_skas(void *r); | ||
34 | |||
35 | #endif | ||
36 | |||
37 | /* | ||
38 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
39 | * Emacs will notice this stuff at the end of the file and automatically | ||
40 | * adjust the settings for this buffer only. This must remain at the end | ||
41 | * of the file. | ||
42 | * --------------------------------------------------------------------------- | ||
43 | * Local variables: | ||
44 | * c-file-style: "linux" | ||
45 | * End: | ||
46 | */ | ||
diff --git a/arch/um/kernel/skas/include/uaccess-skas.h b/arch/um/kernel/skas/include/uaccess-skas.h new file mode 100644 index 000000000000..11986c9b9ddf --- /dev/null +++ b/arch/um/kernel/skas/include/uaccess-skas.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SKAS_UACCESS_H | ||
7 | #define __SKAS_UACCESS_H | ||
8 | |||
9 | #include "asm/errno.h" | ||
10 | #include "asm/fixmap.h" | ||
11 | |||
12 | #define access_ok_skas(type, addr, size) \ | ||
13 | ((segment_eq(get_fs(), KERNEL_DS)) || \ | ||
14 | (((unsigned long) (addr) < TASK_SIZE) && \ | ||
15 | ((unsigned long) (addr) + (size) <= TASK_SIZE)) || \ | ||
16 | ((type == VERIFY_READ ) && \ | ||
17 | ((unsigned long) (addr) >= FIXADDR_USER_START) && \ | ||
18 | ((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \ | ||
19 | ((unsigned long) (addr) + (size) >= (unsigned long)(addr)))) | ||
20 | |||
21 | static inline int verify_area_skas(int type, const void * addr, | ||
22 | unsigned long size) | ||
23 | { | ||
24 | return(access_ok_skas(type, addr, size) ? 0 : -EFAULT); | ||
25 | } | ||
26 | |||
27 | extern int copy_from_user_skas(void *to, const void *from, int n); | ||
28 | extern int copy_to_user_skas(void *to, const void *from, int n); | ||
29 | extern int strncpy_from_user_skas(char *dst, const char *src, int count); | ||
30 | extern int __clear_user_skas(void *mem, int len); | ||
31 | extern int clear_user_skas(void *mem, int len); | ||
32 | extern int strnlen_user_skas(const void *str, int len); | ||
33 | |||
34 | #endif | ||
35 | |||
36 | /* | ||
37 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
38 | * Emacs will notice this stuff at the end of the file and automatically | ||
39 | * adjust the settings for this buffer only. This must remain at the end | ||
40 | * of the file. | ||
41 | * --------------------------------------------------------------------------- | ||
42 | * Local variables: | ||
43 | * c-file-style: "linux" | ||
44 | * End: | ||
45 | */ | ||
diff --git a/arch/um/kernel/skas/mem.c b/arch/um/kernel/skas/mem.c new file mode 100644 index 000000000000..438db2f43456 --- /dev/null +++ b/arch/um/kernel/skas/mem.c | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/mm.h" | ||
8 | #include "mem_user.h" | ||
9 | |||
10 | unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out, | ||
11 | unsigned long *task_size_out) | ||
12 | { | ||
13 | /* Round up to the nearest 4M */ | ||
14 | unsigned long top = ROUND_4M((unsigned long) &arg); | ||
15 | |||
16 | #ifdef CONFIG_HOST_TASK_SIZE | ||
17 | *host_size_out = CONFIG_HOST_TASK_SIZE; | ||
18 | *task_size_out = CONFIG_HOST_TASK_SIZE; | ||
19 | #else | ||
20 | *host_size_out = top; | ||
21 | *task_size_out = top; | ||
22 | #endif | ||
23 | return(((unsigned long) set_task_sizes_skas) & ~0xffffff); | ||
24 | } | ||
25 | |||
26 | /* | ||
27 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
28 | * Emacs will notice this stuff at the end of the file and automatically | ||
29 | * adjust the settings for this buffer only. This must remain at the end | ||
30 | * of the file. | ||
31 | * --------------------------------------------------------------------------- | ||
32 | * Local variables: | ||
33 | * c-file-style: "linux" | ||
34 | * End: | ||
35 | */ | ||
diff --git a/arch/um/kernel/skas/mem_user.c b/arch/um/kernel/skas/mem_user.c new file mode 100644 index 000000000000..1310bf1e88d1 --- /dev/null +++ b/arch/um/kernel/skas/mem_user.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <errno.h> | ||
7 | #include <sys/mman.h> | ||
8 | #include "mem_user.h" | ||
9 | #include "mem.h" | ||
10 | #include "user.h" | ||
11 | #include "os.h" | ||
12 | #include "proc_mm.h" | ||
13 | |||
14 | void map(int fd, unsigned long virt, unsigned long len, int r, int w, | ||
15 | int x, int phys_fd, unsigned long long offset) | ||
16 | { | ||
17 | struct proc_mm_op map; | ||
18 | int prot, n; | ||
19 | |||
20 | prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | | ||
21 | (x ? PROT_EXEC : 0); | ||
22 | |||
23 | map = ((struct proc_mm_op) { .op = MM_MMAP, | ||
24 | .u = | ||
25 | { .mmap = | ||
26 | { .addr = virt, | ||
27 | .len = len, | ||
28 | .prot = prot, | ||
29 | .flags = MAP_SHARED | | ||
30 | MAP_FIXED, | ||
31 | .fd = phys_fd, | ||
32 | .offset = offset | ||
33 | } } } ); | ||
34 | n = os_write_file(fd, &map, sizeof(map)); | ||
35 | if(n != sizeof(map)) | ||
36 | printk("map : /proc/mm map failed, err = %d\n", -n); | ||
37 | } | ||
38 | |||
39 | int unmap(int fd, void *addr, unsigned long len) | ||
40 | { | ||
41 | struct proc_mm_op unmap; | ||
42 | int n; | ||
43 | |||
44 | unmap = ((struct proc_mm_op) { .op = MM_MUNMAP, | ||
45 | .u = | ||
46 | { .munmap = | ||
47 | { .addr = (unsigned long) addr, | ||
48 | .len = len } } } ); | ||
49 | n = os_write_file(fd, &unmap, sizeof(unmap)); | ||
50 | if(n != sizeof(unmap)) { | ||
51 | if(n < 0) | ||
52 | return(n); | ||
53 | else if(n > 0) | ||
54 | return(-EIO); | ||
55 | } | ||
56 | |||
57 | return(0); | ||
58 | } | ||
59 | |||
60 | int protect(int fd, unsigned long addr, unsigned long len, int r, int w, | ||
61 | int x, int must_succeed) | ||
62 | { | ||
63 | struct proc_mm_op protect; | ||
64 | int prot, n; | ||
65 | |||
66 | prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | | ||
67 | (x ? PROT_EXEC : 0); | ||
68 | |||
69 | protect = ((struct proc_mm_op) { .op = MM_MPROTECT, | ||
70 | .u = | ||
71 | { .mprotect = | ||
72 | { .addr = (unsigned long) addr, | ||
73 | .len = len, | ||
74 | .prot = prot } } } ); | ||
75 | |||
76 | n = os_write_file(fd, &protect, sizeof(protect)); | ||
77 | if(n != sizeof(protect)) { | ||
78 | if(n == 0) return(0); | ||
79 | |||
80 | if(must_succeed) | ||
81 | panic("protect failed, err = %d", -n); | ||
82 | |||
83 | return(-EIO); | ||
84 | } | ||
85 | |||
86 | return(0); | ||
87 | } | ||
88 | |||
89 | void before_mem_skas(unsigned long unused) | ||
90 | { | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
95 | * Emacs will notice this stuff at the end of the file and automatically | ||
96 | * adjust the settings for this buffer only. This must remain at the end | ||
97 | * of the file. | ||
98 | * --------------------------------------------------------------------------- | ||
99 | * Local variables: | ||
100 | * c-file-style: "linux" | ||
101 | * End: | ||
102 | */ | ||
diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c new file mode 100644 index 000000000000..6cb9a6d028a9 --- /dev/null +++ b/arch/um/kernel/skas/mmu.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/list.h" | ||
8 | #include "linux/spinlock.h" | ||
9 | #include "linux/slab.h" | ||
10 | #include "asm/current.h" | ||
11 | #include "asm/segment.h" | ||
12 | #include "asm/mmu.h" | ||
13 | #include "os.h" | ||
14 | #include "skas.h" | ||
15 | |||
16 | int init_new_context_skas(struct task_struct *task, struct mm_struct *mm) | ||
17 | { | ||
18 | int from; | ||
19 | |||
20 | if((current->mm != NULL) && (current->mm != &init_mm)) | ||
21 | from = current->mm->context.skas.mm_fd; | ||
22 | else from = -1; | ||
23 | |||
24 | mm->context.skas.mm_fd = new_mm(from); | ||
25 | if(mm->context.skas.mm_fd < 0){ | ||
26 | printk("init_new_context_skas - new_mm failed, errno = %d\n", | ||
27 | mm->context.skas.mm_fd); | ||
28 | return(mm->context.skas.mm_fd); | ||
29 | } | ||
30 | |||
31 | return(0); | ||
32 | } | ||
33 | |||
34 | void destroy_context_skas(struct mm_struct *mm) | ||
35 | { | ||
36 | os_close_file(mm->context.skas.mm_fd); | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
41 | * Emacs will notice this stuff at the end of the file and automatically | ||
42 | * adjust the settings for this buffer only. This must remain at the end | ||
43 | * of the file. | ||
44 | * --------------------------------------------------------------------------- | ||
45 | * Local variables: | ||
46 | * c-file-style: "linux" | ||
47 | * End: | ||
48 | */ | ||
diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c new file mode 100644 index 000000000000..b4ffaaa81241 --- /dev/null +++ b/arch/um/kernel/skas/process.c | |||
@@ -0,0 +1,339 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <unistd.h> | ||
8 | #include <errno.h> | ||
9 | #include <signal.h> | ||
10 | #include <setjmp.h> | ||
11 | #include <sched.h> | ||
12 | #include <sys/wait.h> | ||
13 | #include <sys/mman.h> | ||
14 | #include <sys/user.h> | ||
15 | #include <asm/unistd.h> | ||
16 | #include "user.h" | ||
17 | #include "ptrace_user.h" | ||
18 | #include "time_user.h" | ||
19 | #include "sysdep/ptrace.h" | ||
20 | #include "user_util.h" | ||
21 | #include "kern_util.h" | ||
22 | #include "skas.h" | ||
23 | #include "sysdep/sigcontext.h" | ||
24 | #include "os.h" | ||
25 | #include "proc_mm.h" | ||
26 | #include "skas_ptrace.h" | ||
27 | #include "chan_user.h" | ||
28 | #include "signal_user.h" | ||
29 | #include "registers.h" | ||
30 | |||
31 | int is_skas_winch(int pid, int fd, void *data) | ||
32 | { | ||
33 | if(pid != os_getpid()) | ||
34 | return(0); | ||
35 | |||
36 | register_winch_irq(-1, fd, -1, data); | ||
37 | return(1); | ||
38 | } | ||
39 | |||
40 | static void handle_segv(int pid) | ||
41 | { | ||
42 | struct ptrace_faultinfo fault; | ||
43 | int err; | ||
44 | |||
45 | err = ptrace(PTRACE_FAULTINFO, pid, 0, &fault); | ||
46 | if(err) | ||
47 | panic("handle_segv - PTRACE_FAULTINFO failed, errno = %d\n", | ||
48 | errno); | ||
49 | |||
50 | segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 1, NULL); | ||
51 | } | ||
52 | |||
53 | /*To use the same value of using_sysemu as the caller, ask it that value (in local_using_sysemu)*/ | ||
54 | static void handle_trap(int pid, union uml_pt_regs *regs, int local_using_sysemu) | ||
55 | { | ||
56 | int err, status; | ||
57 | |||
58 | /* Mark this as a syscall */ | ||
59 | UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->skas.regs); | ||
60 | |||
61 | if (!local_using_sysemu) | ||
62 | { | ||
63 | err = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_getpid); | ||
64 | if(err < 0) | ||
65 | panic("handle_trap - nullifying syscall failed errno = %d\n", | ||
66 | errno); | ||
67 | |||
68 | err = ptrace(PTRACE_SYSCALL, pid, 0, 0); | ||
69 | if(err < 0) | ||
70 | panic("handle_trap - continuing to end of syscall failed, " | ||
71 | "errno = %d\n", errno); | ||
72 | |||
73 | CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); | ||
74 | if((err < 0) || !WIFSTOPPED(status) || | ||
75 | (WSTOPSIG(status) != SIGTRAP + 0x80)) | ||
76 | panic("handle_trap - failed to wait at end of syscall, " | ||
77 | "errno = %d, status = %d\n", errno, status); | ||
78 | } | ||
79 | |||
80 | handle_syscall(regs); | ||
81 | } | ||
82 | |||
83 | static int userspace_tramp(void *arg) | ||
84 | { | ||
85 | init_new_thread_signals(0); | ||
86 | enable_timer(); | ||
87 | ptrace(PTRACE_TRACEME, 0, 0, 0); | ||
88 | os_stop_process(os_getpid()); | ||
89 | return(0); | ||
90 | } | ||
91 | |||
92 | /* Each element set once, and only accessed by a single processor anyway */ | ||
93 | #undef NR_CPUS | ||
94 | #define NR_CPUS 1 | ||
95 | int userspace_pid[NR_CPUS]; | ||
96 | |||
97 | void start_userspace(int cpu) | ||
98 | { | ||
99 | void *stack; | ||
100 | unsigned long sp; | ||
101 | int pid, status, n; | ||
102 | |||
103 | stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, | ||
104 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
105 | if(stack == MAP_FAILED) | ||
106 | panic("start_userspace : mmap failed, errno = %d", errno); | ||
107 | sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); | ||
108 | |||
109 | pid = clone(userspace_tramp, (void *) sp, | ||
110 | CLONE_FILES | CLONE_VM | SIGCHLD, NULL); | ||
111 | if(pid < 0) | ||
112 | panic("start_userspace : clone failed, errno = %d", errno); | ||
113 | |||
114 | do { | ||
115 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
116 | if(n < 0) | ||
117 | panic("start_userspace : wait failed, errno = %d", | ||
118 | errno); | ||
119 | } while(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGVTALRM)); | ||
120 | |||
121 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) | ||
122 | panic("start_userspace : expected SIGSTOP, got status = %d", | ||
123 | status); | ||
124 | |||
125 | if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, (void *)PTRACE_O_TRACESYSGOOD) < 0) | ||
126 | panic("start_userspace : PTRACE_SETOPTIONS failed, errno=%d\n", | ||
127 | errno); | ||
128 | |||
129 | if(munmap(stack, PAGE_SIZE) < 0) | ||
130 | panic("start_userspace : munmap failed, errno = %d\n", errno); | ||
131 | |||
132 | userspace_pid[cpu] = pid; | ||
133 | } | ||
134 | |||
135 | void userspace(union uml_pt_regs *regs) | ||
136 | { | ||
137 | int err, status, op, pid = userspace_pid[0]; | ||
138 | int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/ | ||
139 | |||
140 | while(1){ | ||
141 | restore_registers(pid, regs); | ||
142 | |||
143 | /* Now we set local_using_sysemu to be used for one loop */ | ||
144 | local_using_sysemu = get_using_sysemu(); | ||
145 | |||
146 | op = SELECT_PTRACE_OPERATION(local_using_sysemu, singlestepping(NULL)); | ||
147 | |||
148 | err = ptrace(op, pid, 0, 0); | ||
149 | if(err) | ||
150 | panic("userspace - could not resume userspace process, " | ||
151 | "pid=%d, ptrace operation = %d, errno = %d\n", | ||
152 | op, errno); | ||
153 | |||
154 | CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); | ||
155 | if(err < 0) | ||
156 | panic("userspace - waitpid failed, errno = %d\n", | ||
157 | errno); | ||
158 | |||
159 | regs->skas.is_user = 1; | ||
160 | save_registers(pid, regs); | ||
161 | UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */ | ||
162 | |||
163 | if(WIFSTOPPED(status)){ | ||
164 | switch(WSTOPSIG(status)){ | ||
165 | case SIGSEGV: | ||
166 | handle_segv(pid); | ||
167 | break; | ||
168 | case SIGTRAP + 0x80: | ||
169 | handle_trap(pid, regs, local_using_sysemu); | ||
170 | break; | ||
171 | case SIGTRAP: | ||
172 | relay_signal(SIGTRAP, regs); | ||
173 | break; | ||
174 | case SIGIO: | ||
175 | case SIGVTALRM: | ||
176 | case SIGILL: | ||
177 | case SIGBUS: | ||
178 | case SIGFPE: | ||
179 | case SIGWINCH: | ||
180 | user_signal(WSTOPSIG(status), regs); | ||
181 | break; | ||
182 | default: | ||
183 | printk("userspace - child stopped with signal " | ||
184 | "%d\n", WSTOPSIG(status)); | ||
185 | } | ||
186 | interrupt_end(); | ||
187 | |||
188 | /* Avoid -ERESTARTSYS handling in host */ | ||
189 | PT_SYSCALL_NR(regs->skas.regs) = -1; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, | ||
195 | void (*handler)(int)) | ||
196 | { | ||
197 | unsigned long flags; | ||
198 | sigjmp_buf switch_buf, fork_buf; | ||
199 | |||
200 | *switch_buf_ptr = &switch_buf; | ||
201 | *fork_buf_ptr = &fork_buf; | ||
202 | |||
203 | /* Somewhat subtle - siglongjmp restores the signal mask before doing | ||
204 | * the longjmp. This means that when jumping from one stack to another | ||
205 | * when the target stack has interrupts enabled, an interrupt may occur | ||
206 | * on the source stack. This is bad when starting up a process because | ||
207 | * it's not supposed to get timer ticks until it has been scheduled. | ||
208 | * So, we disable interrupts around the sigsetjmp to ensure that | ||
209 | * they can't happen until we get back here where they are safe. | ||
210 | */ | ||
211 | flags = get_signals(); | ||
212 | block_signals(); | ||
213 | if(sigsetjmp(fork_buf, 1) == 0) | ||
214 | new_thread_proc(stack, handler); | ||
215 | |||
216 | remove_sigstack(); | ||
217 | |||
218 | set_signals(flags); | ||
219 | } | ||
220 | |||
221 | void thread_wait(void *sw, void *fb) | ||
222 | { | ||
223 | sigjmp_buf buf, **switch_buf = sw, *fork_buf; | ||
224 | |||
225 | *switch_buf = &buf; | ||
226 | fork_buf = fb; | ||
227 | if(sigsetjmp(buf, 1) == 0) | ||
228 | siglongjmp(*fork_buf, 1); | ||
229 | } | ||
230 | |||
231 | void switch_threads(void *me, void *next) | ||
232 | { | ||
233 | sigjmp_buf my_buf, **me_ptr = me, *next_buf = next; | ||
234 | |||
235 | *me_ptr = &my_buf; | ||
236 | if(sigsetjmp(my_buf, 1) == 0) | ||
237 | siglongjmp(*next_buf, 1); | ||
238 | } | ||
239 | |||
240 | static sigjmp_buf initial_jmpbuf; | ||
241 | |||
242 | /* XXX Make these percpu */ | ||
243 | static void (*cb_proc)(void *arg); | ||
244 | static void *cb_arg; | ||
245 | static sigjmp_buf *cb_back; | ||
246 | |||
247 | int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) | ||
248 | { | ||
249 | sigjmp_buf **switch_buf = switch_buf_ptr; | ||
250 | int n; | ||
251 | |||
252 | *fork_buf_ptr = &initial_jmpbuf; | ||
253 | n = sigsetjmp(initial_jmpbuf, 1); | ||
254 | if(n == 0) | ||
255 | new_thread_proc((void *) stack, new_thread_handler); | ||
256 | else if(n == 1) | ||
257 | remove_sigstack(); | ||
258 | else if(n == 2){ | ||
259 | (*cb_proc)(cb_arg); | ||
260 | siglongjmp(*cb_back, 1); | ||
261 | } | ||
262 | else if(n == 3){ | ||
263 | kmalloc_ok = 0; | ||
264 | return(0); | ||
265 | } | ||
266 | else if(n == 4){ | ||
267 | kmalloc_ok = 0; | ||
268 | return(1); | ||
269 | } | ||
270 | siglongjmp(**switch_buf, 1); | ||
271 | } | ||
272 | |||
273 | void remove_sigstack(void) | ||
274 | { | ||
275 | stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE, | ||
276 | .ss_sp = NULL, | ||
277 | .ss_size = 0 }); | ||
278 | |||
279 | if(sigaltstack(&stack, NULL) != 0) | ||
280 | panic("disabling signal stack failed, errno = %d\n", errno); | ||
281 | } | ||
282 | |||
283 | void initial_thread_cb_skas(void (*proc)(void *), void *arg) | ||
284 | { | ||
285 | sigjmp_buf here; | ||
286 | |||
287 | cb_proc = proc; | ||
288 | cb_arg = arg; | ||
289 | cb_back = &here; | ||
290 | |||
291 | block_signals(); | ||
292 | if(sigsetjmp(here, 1) == 0) | ||
293 | siglongjmp(initial_jmpbuf, 2); | ||
294 | unblock_signals(); | ||
295 | |||
296 | cb_proc = NULL; | ||
297 | cb_arg = NULL; | ||
298 | cb_back = NULL; | ||
299 | } | ||
300 | |||
301 | void halt_skas(void) | ||
302 | { | ||
303 | block_signals(); | ||
304 | siglongjmp(initial_jmpbuf, 3); | ||
305 | } | ||
306 | |||
307 | void reboot_skas(void) | ||
308 | { | ||
309 | block_signals(); | ||
310 | siglongjmp(initial_jmpbuf, 4); | ||
311 | } | ||
312 | |||
313 | void switch_mm_skas(int mm_fd) | ||
314 | { | ||
315 | int err; | ||
316 | |||
317 | #warning need cpu pid in switch_mm_skas | ||
318 | err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd); | ||
319 | if(err) | ||
320 | panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n", | ||
321 | errno); | ||
322 | } | ||
323 | |||
324 | void kill_off_processes_skas(void) | ||
325 | { | ||
326 | #warning need to loop over userspace_pids in kill_off_processes_skas | ||
327 | os_kill_ptraced_process(userspace_pid[0], 1); | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
332 | * Emacs will notice this stuff at the end of the file and automatically | ||
333 | * adjust the settings for this buffer only. This must remain at the end | ||
334 | * of the file. | ||
335 | * --------------------------------------------------------------------------- | ||
336 | * Local variables: | ||
337 | * c-file-style: "linux" | ||
338 | * End: | ||
339 | */ | ||
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c new file mode 100644 index 000000000000..5d096ea63b97 --- /dev/null +++ b/arch/um/kernel/skas/process_kern.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/slab.h" | ||
8 | #include "linux/ptrace.h" | ||
9 | #include "linux/proc_fs.h" | ||
10 | #include "linux/file.h" | ||
11 | #include "linux/errno.h" | ||
12 | #include "linux/init.h" | ||
13 | #include "asm/uaccess.h" | ||
14 | #include "asm/atomic.h" | ||
15 | #include "kern_util.h" | ||
16 | #include "time_user.h" | ||
17 | #include "signal_user.h" | ||
18 | #include "skas.h" | ||
19 | #include "os.h" | ||
20 | #include "user_util.h" | ||
21 | #include "tlb.h" | ||
22 | #include "kern.h" | ||
23 | #include "mode.h" | ||
24 | #include "proc_mm.h" | ||
25 | #include "registers.h" | ||
26 | |||
27 | void *switch_to_skas(void *prev, void *next) | ||
28 | { | ||
29 | struct task_struct *from, *to; | ||
30 | |||
31 | from = prev; | ||
32 | to = next; | ||
33 | |||
34 | /* XXX need to check runqueues[cpu].idle */ | ||
35 | if(current->pid == 0) | ||
36 | switch_timers(0); | ||
37 | |||
38 | to->thread.prev_sched = from; | ||
39 | set_current(to); | ||
40 | |||
41 | switch_threads(&from->thread.mode.skas.switch_buf, | ||
42 | to->thread.mode.skas.switch_buf); | ||
43 | |||
44 | if(current->pid == 0) | ||
45 | switch_timers(1); | ||
46 | |||
47 | return(current->thread.prev_sched); | ||
48 | } | ||
49 | |||
50 | extern void schedule_tail(struct task_struct *prev); | ||
51 | |||
52 | void new_thread_handler(int sig) | ||
53 | { | ||
54 | int (*fn)(void *), n; | ||
55 | void *arg; | ||
56 | |||
57 | fn = current->thread.request.u.thread.proc; | ||
58 | arg = current->thread.request.u.thread.arg; | ||
59 | change_sig(SIGUSR1, 1); | ||
60 | thread_wait(¤t->thread.mode.skas.switch_buf, | ||
61 | current->thread.mode.skas.fork_buf); | ||
62 | |||
63 | if(current->thread.prev_sched != NULL) | ||
64 | schedule_tail(current->thread.prev_sched); | ||
65 | current->thread.prev_sched = NULL; | ||
66 | |||
67 | /* The return value is 1 if the kernel thread execs a process, | ||
68 | * 0 if it just exits | ||
69 | */ | ||
70 | n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf); | ||
71 | if(n == 1) | ||
72 | userspace(¤t->thread.regs.regs); | ||
73 | else do_exit(0); | ||
74 | } | ||
75 | |||
76 | void new_thread_proc(void *stack, void (*handler)(int sig)) | ||
77 | { | ||
78 | init_new_thread_stack(stack, handler); | ||
79 | os_usr1_process(os_getpid()); | ||
80 | } | ||
81 | |||
82 | void release_thread_skas(struct task_struct *task) | ||
83 | { | ||
84 | } | ||
85 | |||
86 | void exit_thread_skas(void) | ||
87 | { | ||
88 | } | ||
89 | |||
90 | void fork_handler(int sig) | ||
91 | { | ||
92 | change_sig(SIGUSR1, 1); | ||
93 | thread_wait(¤t->thread.mode.skas.switch_buf, | ||
94 | current->thread.mode.skas.fork_buf); | ||
95 | |||
96 | force_flush_all(); | ||
97 | if(current->thread.prev_sched == NULL) | ||
98 | panic("blech"); | ||
99 | |||
100 | schedule_tail(current->thread.prev_sched); | ||
101 | current->thread.prev_sched = NULL; | ||
102 | |||
103 | userspace(¤t->thread.regs.regs); | ||
104 | } | ||
105 | |||
106 | int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, | ||
107 | unsigned long stack_top, struct task_struct * p, | ||
108 | struct pt_regs *regs) | ||
109 | { | ||
110 | void (*handler)(int); | ||
111 | |||
112 | if(current->thread.forking){ | ||
113 | memcpy(&p->thread.regs.regs.skas, | ||
114 | ¤t->thread.regs.regs.skas, | ||
115 | sizeof(p->thread.regs.regs.skas)); | ||
116 | REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.skas.regs, 0); | ||
117 | if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp; | ||
118 | |||
119 | handler = fork_handler; | ||
120 | } | ||
121 | else { | ||
122 | init_thread_registers(&p->thread.regs.regs); | ||
123 | p->thread.request.u.thread = current->thread.request.u.thread; | ||
124 | handler = new_thread_handler; | ||
125 | } | ||
126 | |||
127 | new_thread(p->thread_info, &p->thread.mode.skas.switch_buf, | ||
128 | &p->thread.mode.skas.fork_buf, handler); | ||
129 | return(0); | ||
130 | } | ||
131 | |||
132 | int new_mm(int from) | ||
133 | { | ||
134 | struct proc_mm_op copy; | ||
135 | int n, fd; | ||
136 | |||
137 | fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0); | ||
138 | if(fd < 0) | ||
139 | return(fd); | ||
140 | |||
141 | if(from != -1){ | ||
142 | copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, | ||
143 | .u = | ||
144 | { .copy_segments = from } } ); | ||
145 | n = os_write_file(fd, ©, sizeof(copy)); | ||
146 | if(n != sizeof(copy)) | ||
147 | printk("new_mm : /proc/mm copy_segments failed, " | ||
148 | "err = %d\n", -n); | ||
149 | } | ||
150 | |||
151 | return(fd); | ||
152 | } | ||
153 | |||
154 | void init_idle_skas(void) | ||
155 | { | ||
156 | cpu_tasks[current_thread->cpu].pid = os_getpid(); | ||
157 | default_idle(); | ||
158 | } | ||
159 | |||
160 | extern void start_kernel(void); | ||
161 | |||
162 | static int start_kernel_proc(void *unused) | ||
163 | { | ||
164 | int pid; | ||
165 | |||
166 | block_signals(); | ||
167 | pid = os_getpid(); | ||
168 | |||
169 | cpu_tasks[0].pid = pid; | ||
170 | cpu_tasks[0].task = current; | ||
171 | #ifdef CONFIG_SMP | ||
172 | cpu_online_map = cpumask_of_cpu(0); | ||
173 | #endif | ||
174 | start_kernel(); | ||
175 | return(0); | ||
176 | } | ||
177 | |||
178 | int start_uml_skas(void) | ||
179 | { | ||
180 | start_userspace(0); | ||
181 | |||
182 | init_new_thread_signals(1); | ||
183 | uml_idle_timer(); | ||
184 | |||
185 | init_task.thread.request.u.thread.proc = start_kernel_proc; | ||
186 | init_task.thread.request.u.thread.arg = NULL; | ||
187 | return(start_idle_thread(init_task.thread_info, | ||
188 | &init_task.thread.mode.skas.switch_buf, | ||
189 | &init_task.thread.mode.skas.fork_buf)); | ||
190 | } | ||
191 | |||
192 | int external_pid_skas(struct task_struct *task) | ||
193 | { | ||
194 | #warning Need to look up userspace_pid by cpu | ||
195 | return(userspace_pid[0]); | ||
196 | } | ||
197 | |||
198 | int thread_pid_skas(struct task_struct *task) | ||
199 | { | ||
200 | #warning Need to look up userspace_pid by cpu | ||
201 | return(userspace_pid[0]); | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
206 | * Emacs will notice this stuff at the end of the file and automatically | ||
207 | * adjust the settings for this buffer only. This must remain at the end | ||
208 | * of the file. | ||
209 | * --------------------------------------------------------------------------- | ||
210 | * Local variables: | ||
211 | * c-file-style: "linux" | ||
212 | * End: | ||
213 | */ | ||
diff --git a/arch/um/kernel/skas/syscall_kern.c b/arch/um/kernel/skas/syscall_kern.c new file mode 100644 index 000000000000..bdf040ce5b8e --- /dev/null +++ b/arch/um/kernel/skas/syscall_kern.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sys.h" | ||
7 | #include "linux/ptrace.h" | ||
8 | #include "asm/errno.h" | ||
9 | #include "asm/unistd.h" | ||
10 | #include "asm/ptrace.h" | ||
11 | #include "asm/current.h" | ||
12 | #include "sysdep/syscalls.h" | ||
13 | #include "kern_util.h" | ||
14 | |||
15 | extern syscall_handler_t *sys_call_table[]; | ||
16 | |||
17 | long execute_syscall_skas(void *r) | ||
18 | { | ||
19 | struct pt_regs *regs = r; | ||
20 | long res; | ||
21 | int syscall; | ||
22 | |||
23 | current->thread.nsyscalls++; | ||
24 | nsyscalls++; | ||
25 | syscall = UPT_SYSCALL_NR(®s->regs); | ||
26 | |||
27 | if((syscall >= NR_syscalls) || (syscall < 0)) | ||
28 | res = -ENOSYS; | ||
29 | else res = EXECUTE_SYSCALL(syscall, regs); | ||
30 | |||
31 | return(res); | ||
32 | } | ||
33 | |||
34 | /* | ||
35 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
36 | * Emacs will notice this stuff at the end of the file and automatically | ||
37 | * adjust the settings for this buffer only. This must remain at the end | ||
38 | * of the file. | ||
39 | * --------------------------------------------------------------------------- | ||
40 | * Local variables: | ||
41 | * c-file-style: "linux" | ||
42 | * End: | ||
43 | */ | ||
diff --git a/arch/um/kernel/skas/syscall_user.c b/arch/um/kernel/skas/syscall_user.c new file mode 100644 index 000000000000..2828e6e37721 --- /dev/null +++ b/arch/um/kernel/skas/syscall_user.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <signal.h> | ||
8 | #include "kern_util.h" | ||
9 | #include "uml-config.h" | ||
10 | #include "syscall_user.h" | ||
11 | #include "sysdep/ptrace.h" | ||
12 | #include "sysdep/sigcontext.h" | ||
13 | #include "skas.h" | ||
14 | |||
15 | void handle_syscall(union uml_pt_regs *regs) | ||
16 | { | ||
17 | long result; | ||
18 | #if UML_CONFIG_SYSCALL_DEBUG | ||
19 | int index; | ||
20 | |||
21 | index = record_syscall_start(UPT_SYSCALL_NR(regs)); | ||
22 | #endif | ||
23 | |||
24 | syscall_trace(regs, 0); | ||
25 | result = execute_syscall_skas(regs); | ||
26 | |||
27 | REGS_SET_SYSCALL_RETURN(regs->skas.regs, result); | ||
28 | |||
29 | syscall_trace(regs, 1); | ||
30 | #if UML_CONFIG_SYSCALL_DEBUG | ||
31 | record_syscall_end(index, result); | ||
32 | #endif | ||
33 | } | ||
34 | |||
35 | /* | ||
36 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
37 | * Emacs will notice this stuff at the end of the file and automatically | ||
38 | * adjust the settings for this buffer only. This must remain at the end | ||
39 | * of the file. | ||
40 | * --------------------------------------------------------------------------- | ||
41 | * Local variables: | ||
42 | * c-file-style: "linux" | ||
43 | * End: | ||
44 | */ | ||
diff --git a/arch/um/kernel/skas/time.c b/arch/um/kernel/skas/time.c new file mode 100644 index 000000000000..98091494b897 --- /dev/null +++ b/arch/um/kernel/skas/time.c | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <sys/signal.h> | ||
7 | #include <sys/time.h> | ||
8 | #include "time_user.h" | ||
9 | #include "process.h" | ||
10 | #include "user.h" | ||
11 | |||
12 | void user_time_init_skas(void) | ||
13 | { | ||
14 | if(signal(SIGALRM, (__sighandler_t) alarm_handler) == SIG_ERR) | ||
15 | panic("Couldn't set SIGALRM handler"); | ||
16 | if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR) | ||
17 | panic("Couldn't set SIGVTALRM handler"); | ||
18 | set_interval(ITIMER_VIRTUAL); | ||
19 | } | ||
20 | |||
21 | /* | ||
22 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
23 | * Emacs will notice this stuff at the end of the file and automatically | ||
24 | * adjust the settings for this buffer only. This must remain at the end | ||
25 | * of the file. | ||
26 | * --------------------------------------------------------------------------- | ||
27 | * Local variables: | ||
28 | * c-file-style: "linux" | ||
29 | * End: | ||
30 | */ | ||
diff --git a/arch/um/kernel/skas/tlb.c b/arch/um/kernel/skas/tlb.c new file mode 100644 index 000000000000..b8c5e71763d1 --- /dev/null +++ b/arch/um/kernel/skas/tlb.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Copyright 2003 PathScale, Inc. | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include "linux/stddef.h" | ||
8 | #include "linux/sched.h" | ||
9 | #include "linux/mm.h" | ||
10 | #include "asm/page.h" | ||
11 | #include "asm/pgtable.h" | ||
12 | #include "asm/mmu.h" | ||
13 | #include "user_util.h" | ||
14 | #include "mem_user.h" | ||
15 | #include "mem.h" | ||
16 | #include "skas.h" | ||
17 | #include "os.h" | ||
18 | #include "tlb.h" | ||
19 | |||
20 | static void do_ops(int fd, struct host_vm_op *ops, int last) | ||
21 | { | ||
22 | struct host_vm_op *op; | ||
23 | int i; | ||
24 | |||
25 | for(i = 0; i <= last; i++){ | ||
26 | op = &ops[i]; | ||
27 | switch(op->type){ | ||
28 | case MMAP: | ||
29 | map(fd, op->u.mmap.addr, op->u.mmap.len, | ||
30 | op->u.mmap.r, op->u.mmap.w, op->u.mmap.x, | ||
31 | op->u.mmap.fd, op->u.mmap.offset); | ||
32 | break; | ||
33 | case MUNMAP: | ||
34 | unmap(fd, (void *) op->u.munmap.addr, | ||
35 | op->u.munmap.len); | ||
36 | break; | ||
37 | case MPROTECT: | ||
38 | protect(fd, op->u.mprotect.addr, op->u.mprotect.len, | ||
39 | op->u.mprotect.r, op->u.mprotect.w, | ||
40 | op->u.mprotect.x); | ||
41 | break; | ||
42 | default: | ||
43 | printk("Unknown op type %d in do_ops\n", op->type); | ||
44 | break; | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static void fix_range(struct mm_struct *mm, unsigned long start_addr, | ||
50 | unsigned long end_addr, int force) | ||
51 | { | ||
52 | int fd = mm->context.skas.mm_fd; | ||
53 | |||
54 | fix_range_common(mm, start_addr, end_addr, force, fd, do_ops); | ||
55 | } | ||
56 | |||
57 | void __flush_tlb_one_skas(unsigned long addr) | ||
58 | { | ||
59 | flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE); | ||
60 | } | ||
61 | |||
62 | void flush_tlb_range_skas(struct vm_area_struct *vma, unsigned long start, | ||
63 | unsigned long end) | ||
64 | { | ||
65 | if(vma->vm_mm == NULL) | ||
66 | flush_tlb_kernel_range_common(start, end); | ||
67 | else fix_range(vma->vm_mm, start, end, 0); | ||
68 | } | ||
69 | |||
70 | void flush_tlb_mm_skas(struct mm_struct *mm) | ||
71 | { | ||
72 | /* Don't bother flushing if this address space is about to be | ||
73 | * destroyed. | ||
74 | */ | ||
75 | if(atomic_read(&mm->mm_users) == 0) | ||
76 | return; | ||
77 | |||
78 | fix_range(mm, 0, host_task_size, 0); | ||
79 | flush_tlb_kernel_range_common(start_vm, end_vm); | ||
80 | } | ||
81 | |||
82 | void force_flush_all_skas(void) | ||
83 | { | ||
84 | fix_range(current->mm, 0, host_task_size, 1); | ||
85 | } | ||
diff --git a/arch/um/kernel/skas/trap_user.c b/arch/um/kernel/skas/trap_user.c new file mode 100644 index 000000000000..8e9b46d4702e --- /dev/null +++ b/arch/um/kernel/skas/trap_user.c | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <signal.h> | ||
7 | #include <errno.h> | ||
8 | #include "sysdep/ptrace.h" | ||
9 | #include "signal_user.h" | ||
10 | #include "user_util.h" | ||
11 | #include "kern_util.h" | ||
12 | #include "task.h" | ||
13 | #include "sigcontext.h" | ||
14 | |||
15 | void sig_handler_common_skas(int sig, void *sc_ptr) | ||
16 | { | ||
17 | struct sigcontext *sc = sc_ptr; | ||
18 | struct skas_regs *r; | ||
19 | struct signal_info *info; | ||
20 | int save_errno = errno; | ||
21 | int save_user; | ||
22 | |||
23 | /* This is done because to allow SIGSEGV to be delivered inside a SEGV | ||
24 | * handler. This can happen in copy_user, and if SEGV is disabled, | ||
25 | * the process will die. | ||
26 | * XXX Figure out why this is better than SA_NODEFER | ||
27 | */ | ||
28 | if(sig == SIGSEGV) | ||
29 | change_sig(SIGSEGV, 1); | ||
30 | |||
31 | r = &TASK_REGS(get_current())->skas; | ||
32 | save_user = r->is_user; | ||
33 | r->is_user = 0; | ||
34 | r->fault_addr = SC_FAULT_ADDR(sc); | ||
35 | r->fault_type = SC_FAULT_TYPE(sc); | ||
36 | r->trap_type = SC_TRAP_TYPE(sc); | ||
37 | |||
38 | change_sig(SIGUSR1, 1); | ||
39 | info = &sig_info[sig]; | ||
40 | if(!info->is_irq) unblock_signals(); | ||
41 | |||
42 | (*info->handler)(sig, (union uml_pt_regs *) r); | ||
43 | |||
44 | errno = save_errno; | ||
45 | r->is_user = save_user; | ||
46 | } | ||
47 | |||
48 | void user_signal(int sig, union uml_pt_regs *regs) | ||
49 | { | ||
50 | struct signal_info *info; | ||
51 | |||
52 | regs->skas.is_user = 1; | ||
53 | regs->skas.fault_addr = 0; | ||
54 | regs->skas.fault_type = 0; | ||
55 | regs->skas.trap_type = 0; | ||
56 | info = &sig_info[sig]; | ||
57 | (*info->handler)(sig, regs); | ||
58 | |||
59 | unblock_signals(); | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
64 | * Emacs will notice this stuff at the end of the file and automatically | ||
65 | * adjust the settings for this buffer only. This must remain at the end | ||
66 | * of the file. | ||
67 | * --------------------------------------------------------------------------- | ||
68 | * Local variables: | ||
69 | * c-file-style: "linux" | ||
70 | * End: | ||
71 | */ | ||
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c new file mode 100644 index 000000000000..7575ec489b63 --- /dev/null +++ b/arch/um/kernel/skas/uaccess.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/stddef.h" | ||
7 | #include "linux/kernel.h" | ||
8 | #include "linux/string.h" | ||
9 | #include "linux/fs.h" | ||
10 | #include "linux/highmem.h" | ||
11 | #include "asm/page.h" | ||
12 | #include "asm/pgtable.h" | ||
13 | #include "asm/uaccess.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "user_util.h" | ||
16 | |||
17 | extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, | ||
18 | pte_t *pte_out); | ||
19 | |||
20 | static unsigned long maybe_map(unsigned long virt, int is_write) | ||
21 | { | ||
22 | pte_t pte; | ||
23 | int err; | ||
24 | |||
25 | void *phys = um_virt_to_phys(current, virt, &pte); | ||
26 | int dummy_code; | ||
27 | |||
28 | if(IS_ERR(phys) || (is_write && !pte_write(pte))){ | ||
29 | err = handle_page_fault(virt, 0, is_write, 1, &dummy_code); | ||
30 | if(err) | ||
31 | return(0); | ||
32 | phys = um_virt_to_phys(current, virt, NULL); | ||
33 | } | ||
34 | return((unsigned long) phys); | ||
35 | } | ||
36 | |||
37 | static int do_op(unsigned long addr, int len, int is_write, | ||
38 | int (*op)(unsigned long addr, int len, void *arg), void *arg) | ||
39 | { | ||
40 | struct page *page; | ||
41 | int n; | ||
42 | |||
43 | addr = maybe_map(addr, is_write); | ||
44 | if(addr == -1) | ||
45 | return(-1); | ||
46 | |||
47 | page = phys_to_page(addr); | ||
48 | addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); | ||
49 | n = (*op)(addr, len, arg); | ||
50 | kunmap(page); | ||
51 | |||
52 | return(n); | ||
53 | } | ||
54 | |||
55 | static void do_buffer_op(void *jmpbuf, void *arg_ptr) | ||
56 | { | ||
57 | va_list args; | ||
58 | unsigned long addr; | ||
59 | int len, is_write, size, remain, n; | ||
60 | int (*op)(unsigned long, int, void *); | ||
61 | void *arg; | ||
62 | int *res; | ||
63 | |||
64 | /* Some old gccs recognize __va_copy, but not va_copy */ | ||
65 | __va_copy(args, *(va_list *)arg_ptr); | ||
66 | addr = va_arg(args, unsigned long); | ||
67 | len = va_arg(args, int); | ||
68 | is_write = va_arg(args, int); | ||
69 | op = va_arg(args, void *); | ||
70 | arg = va_arg(args, void *); | ||
71 | res = va_arg(args, int *); | ||
72 | va_end(args); | ||
73 | size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); | ||
74 | remain = len; | ||
75 | |||
76 | current->thread.fault_catcher = jmpbuf; | ||
77 | n = do_op(addr, size, is_write, op, arg); | ||
78 | if(n != 0){ | ||
79 | *res = (n < 0 ? remain : 0); | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | addr += size; | ||
84 | remain -= size; | ||
85 | if(remain == 0){ | ||
86 | *res = 0; | ||
87 | goto out; | ||
88 | } | ||
89 | |||
90 | while(addr < ((addr + remain) & PAGE_MASK)){ | ||
91 | n = do_op(addr, PAGE_SIZE, is_write, op, arg); | ||
92 | if(n != 0){ | ||
93 | *res = (n < 0 ? remain : 0); | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | addr += PAGE_SIZE; | ||
98 | remain -= PAGE_SIZE; | ||
99 | } | ||
100 | if(remain == 0){ | ||
101 | *res = 0; | ||
102 | goto out; | ||
103 | } | ||
104 | |||
105 | n = do_op(addr, remain, is_write, op, arg); | ||
106 | if(n != 0) | ||
107 | *res = (n < 0 ? remain : 0); | ||
108 | else *res = 0; | ||
109 | out: | ||
110 | current->thread.fault_catcher = NULL; | ||
111 | } | ||
112 | |||
113 | static int buffer_op(unsigned long addr, int len, int is_write, | ||
114 | int (*op)(unsigned long addr, int len, void *arg), | ||
115 | void *arg) | ||
116 | { | ||
117 | int faulted, res; | ||
118 | |||
119 | faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, | ||
120 | &res); | ||
121 | if(!faulted) | ||
122 | return(res); | ||
123 | |||
124 | return(addr + len - (unsigned long) current->thread.fault_addr); | ||
125 | } | ||
126 | |||
127 | static int copy_chunk_from_user(unsigned long from, int len, void *arg) | ||
128 | { | ||
129 | unsigned long *to_ptr = arg, to = *to_ptr; | ||
130 | |||
131 | memcpy((void *) to, (void *) from, len); | ||
132 | *to_ptr += len; | ||
133 | return(0); | ||
134 | } | ||
135 | |||
136 | int copy_from_user_skas(void *to, const void __user *from, int n) | ||
137 | { | ||
138 | if(segment_eq(get_fs(), KERNEL_DS)){ | ||
139 | memcpy(to, (__force void*)from, n); | ||
140 | return(0); | ||
141 | } | ||
142 | |||
143 | return(access_ok_skas(VERIFY_READ, from, n) ? | ||
144 | buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to): | ||
145 | n); | ||
146 | } | ||
147 | |||
148 | static int copy_chunk_to_user(unsigned long to, int len, void *arg) | ||
149 | { | ||
150 | unsigned long *from_ptr = arg, from = *from_ptr; | ||
151 | |||
152 | memcpy((void *) to, (void *) from, len); | ||
153 | *from_ptr += len; | ||
154 | return(0); | ||
155 | } | ||
156 | |||
157 | int copy_to_user_skas(void __user *to, const void *from, int n) | ||
158 | { | ||
159 | if(segment_eq(get_fs(), KERNEL_DS)){ | ||
160 | memcpy((__force void*)to, from, n); | ||
161 | return(0); | ||
162 | } | ||
163 | |||
164 | return(access_ok_skas(VERIFY_WRITE, to, n) ? | ||
165 | buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) : | ||
166 | n); | ||
167 | } | ||
168 | |||
169 | static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) | ||
170 | { | ||
171 | char **to_ptr = arg, *to = *to_ptr; | ||
172 | int n; | ||
173 | |||
174 | strncpy(to, (void *) from, len); | ||
175 | n = strnlen(to, len); | ||
176 | *to_ptr += n; | ||
177 | |||
178 | if(n < len) | ||
179 | return(1); | ||
180 | return(0); | ||
181 | } | ||
182 | |||
183 | int strncpy_from_user_skas(char *dst, const char __user *src, int count) | ||
184 | { | ||
185 | int n; | ||
186 | char *ptr = dst; | ||
187 | |||
188 | if(segment_eq(get_fs(), KERNEL_DS)){ | ||
189 | strncpy(dst, (__force void*)src, count); | ||
190 | return(strnlen(dst, count)); | ||
191 | } | ||
192 | |||
193 | if(!access_ok_skas(VERIFY_READ, src, 1)) | ||
194 | return(-EFAULT); | ||
195 | |||
196 | n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, | ||
197 | &ptr); | ||
198 | if(n != 0) | ||
199 | return(-EFAULT); | ||
200 | return(strnlen(dst, count)); | ||
201 | } | ||
202 | |||
203 | static int clear_chunk(unsigned long addr, int len, void *unused) | ||
204 | { | ||
205 | memset((void *) addr, 0, len); | ||
206 | return(0); | ||
207 | } | ||
208 | |||
209 | int __clear_user_skas(void __user *mem, int len) | ||
210 | { | ||
211 | return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL)); | ||
212 | } | ||
213 | |||
214 | int clear_user_skas(void __user *mem, int len) | ||
215 | { | ||
216 | if(segment_eq(get_fs(), KERNEL_DS)){ | ||
217 | memset((__force void*)mem, 0, len); | ||
218 | return(0); | ||
219 | } | ||
220 | |||
221 | return(access_ok_skas(VERIFY_WRITE, mem, len) ? | ||
222 | buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len); | ||
223 | } | ||
224 | |||
225 | static int strnlen_chunk(unsigned long str, int len, void *arg) | ||
226 | { | ||
227 | int *len_ptr = arg, n; | ||
228 | |||
229 | n = strnlen((void *) str, len); | ||
230 | *len_ptr += n; | ||
231 | |||
232 | if(n < len) | ||
233 | return(1); | ||
234 | return(0); | ||
235 | } | ||
236 | |||
237 | int strnlen_user_skas(const void __user *str, int len) | ||
238 | { | ||
239 | int count = 0, n; | ||
240 | |||
241 | if(segment_eq(get_fs(), KERNEL_DS)) | ||
242 | return(strnlen((__force char*)str, len) + 1); | ||
243 | |||
244 | n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count); | ||
245 | if(n == 0) | ||
246 | return(count + 1); | ||
247 | return(-EFAULT); | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
252 | * Emacs will notice this stuff at the end of the file and automatically | ||
253 | * adjust the settings for this buffer only. This must remain at the end | ||
254 | * of the file. | ||
255 | * --------------------------------------------------------------------------- | ||
256 | * Local variables: | ||
257 | * c-file-style: "linux" | ||
258 | * End: | ||
259 | */ | ||
diff --git a/arch/um/kernel/skas/util/Makefile b/arch/um/kernel/skas/util/Makefile new file mode 100644 index 000000000000..17f5909d60f7 --- /dev/null +++ b/arch/um/kernel/skas/util/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | hostprogs-y := mk_ptregs | ||
2 | always := $(hostprogs-y) | ||
3 | |||
4 | mk_ptregs-objs := mk_ptregs-$(SUBARCH).o | ||
diff --git a/arch/um/kernel/skas/util/mk_ptregs-i386.c b/arch/um/kernel/skas/util/mk_ptregs-i386.c new file mode 100644 index 000000000000..0788dd05bcac --- /dev/null +++ b/arch/um/kernel/skas/util/mk_ptregs-i386.c | |||
@@ -0,0 +1,51 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <asm/ptrace.h> | ||
3 | #include <asm/user.h> | ||
4 | |||
5 | #define PRINT_REG(name, val) printf("#define HOST_%s %d\n", (name), (val)) | ||
6 | |||
7 | int main(int argc, char **argv) | ||
8 | { | ||
9 | printf("/* Automatically generated by " | ||
10 | "arch/um/kernel/skas/util/mk_ptregs */\n"); | ||
11 | printf("\n"); | ||
12 | printf("#ifndef __SKAS_PT_REGS_\n"); | ||
13 | printf("#define __SKAS_PT_REGS_\n"); | ||
14 | printf("\n"); | ||
15 | printf("#define HOST_FRAME_SIZE %d\n", FRAME_SIZE); | ||
16 | printf("#define HOST_FP_SIZE %d\n", | ||
17 | sizeof(struct user_i387_struct) / sizeof(unsigned long)); | ||
18 | printf("#define HOST_XFP_SIZE %d\n", | ||
19 | sizeof(struct user_fxsr_struct) / sizeof(unsigned long)); | ||
20 | |||
21 | PRINT_REG("IP", EIP); | ||
22 | PRINT_REG("SP", UESP); | ||
23 | PRINT_REG("EFLAGS", EFL); | ||
24 | PRINT_REG("EAX", EAX); | ||
25 | PRINT_REG("EBX", EBX); | ||
26 | PRINT_REG("ECX", ECX); | ||
27 | PRINT_REG("EDX", EDX); | ||
28 | PRINT_REG("ESI", ESI); | ||
29 | PRINT_REG("EDI", EDI); | ||
30 | PRINT_REG("EBP", EBP); | ||
31 | PRINT_REG("CS", CS); | ||
32 | PRINT_REG("SS", SS); | ||
33 | PRINT_REG("DS", DS); | ||
34 | PRINT_REG("FS", FS); | ||
35 | PRINT_REG("ES", ES); | ||
36 | PRINT_REG("GS", GS); | ||
37 | printf("\n"); | ||
38 | printf("#endif\n"); | ||
39 | return(0); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
44 | * Emacs will notice this stuff at the end of the file and automatically | ||
45 | * adjust the settings for this buffer only. This must remain at the end | ||
46 | * of the file. | ||
47 | * --------------------------------------------------------------------------- | ||
48 | * Local variables: | ||
49 | * c-file-style: "linux" | ||
50 | * End: | ||
51 | */ | ||
diff --git a/arch/um/kernel/skas/util/mk_ptregs-x86_64.c b/arch/um/kernel/skas/util/mk_ptregs-x86_64.c new file mode 100644 index 000000000000..67aee92a70ef --- /dev/null +++ b/arch/um/kernel/skas/util/mk_ptregs-x86_64.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright 2003 PathScale, Inc. | ||
3 | * | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <stdio.h> | ||
8 | #define __FRAME_OFFSETS | ||
9 | #include <asm/ptrace.h> | ||
10 | |||
11 | #define PRINT_REG(name, val) \ | ||
12 | printf("#define HOST_%s (%d / sizeof(unsigned long))\n", (name), (val)) | ||
13 | |||
14 | int main(int argc, char **argv) | ||
15 | { | ||
16 | printf("/* Automatically generated by " | ||
17 | "arch/um/kernel/skas/util/mk_ptregs */\n"); | ||
18 | printf("\n"); | ||
19 | printf("#ifndef __SKAS_PT_REGS_\n"); | ||
20 | printf("#define __SKAS_PT_REGS_\n"); | ||
21 | printf("#define HOST_FRAME_SIZE (%d / sizeof(unsigned long))\n", | ||
22 | FRAME_SIZE); | ||
23 | PRINT_REG("RBX", RBX); | ||
24 | PRINT_REG("RCX", RCX); | ||
25 | PRINT_REG("RDI", RDI); | ||
26 | PRINT_REG("RSI", RSI); | ||
27 | PRINT_REG("RDX", RDX); | ||
28 | PRINT_REG("RBP", RBP); | ||
29 | PRINT_REG("RAX", RAX); | ||
30 | PRINT_REG("R8", R8); | ||
31 | PRINT_REG("R9", R9); | ||
32 | PRINT_REG("R10", R10); | ||
33 | PRINT_REG("R11", R11); | ||
34 | PRINT_REG("R12", R12); | ||
35 | PRINT_REG("R13", R13); | ||
36 | PRINT_REG("R14", R14); | ||
37 | PRINT_REG("R15", R15); | ||
38 | PRINT_REG("ORIG_RAX", ORIG_RAX); | ||
39 | PRINT_REG("CS", CS); | ||
40 | PRINT_REG("SS", SS); | ||
41 | PRINT_REG("EFLAGS", EFLAGS); | ||
42 | #if 0 | ||
43 | PRINT_REG("FS", FS); | ||
44 | PRINT_REG("GS", GS); | ||
45 | PRINT_REG("DS", DS); | ||
46 | PRINT_REG("ES", ES); | ||
47 | #endif | ||
48 | |||
49 | PRINT_REG("IP", RIP); | ||
50 | PRINT_REG("SP", RSP); | ||
51 | printf("#define HOST_FP_SIZE 0\n"); | ||
52 | printf("#define HOST_XFP_SIZE 0\n"); | ||
53 | printf("\n"); | ||
54 | printf("\n"); | ||
55 | printf("#endif\n"); | ||
56 | return(0); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
61 | * Emacs will notice this stuff at the end of the file and automatically | ||
62 | * adjust the settings for this buffer only. This must remain at the end | ||
63 | * of the file. | ||
64 | * --------------------------------------------------------------------------- | ||
65 | * Local variables: | ||
66 | * c-file-style: "linux" | ||
67 | * End: | ||
68 | */ | ||
diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c new file mode 100644 index 000000000000..72113b0a96e7 --- /dev/null +++ b/arch/um/kernel/smp.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/percpu.h" | ||
8 | #include "asm/pgalloc.h" | ||
9 | #include "asm/tlb.h" | ||
10 | |||
11 | /* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */ | ||
12 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
13 | |||
14 | #ifdef CONFIG_SMP | ||
15 | |||
16 | #include "linux/sched.h" | ||
17 | #include "linux/module.h" | ||
18 | #include "linux/threads.h" | ||
19 | #include "linux/interrupt.h" | ||
20 | #include "linux/err.h" | ||
21 | #include "linux/hardirq.h" | ||
22 | #include "asm/smp.h" | ||
23 | #include "asm/processor.h" | ||
24 | #include "asm/spinlock.h" | ||
25 | #include "user_util.h" | ||
26 | #include "kern_util.h" | ||
27 | #include "kern.h" | ||
28 | #include "irq_user.h" | ||
29 | #include "os.h" | ||
30 | |||
31 | /* CPU online map, set by smp_boot_cpus */ | ||
32 | cpumask_t cpu_online_map = CPU_MASK_NONE; | ||
33 | cpumask_t cpu_possible_map = CPU_MASK_NONE; | ||
34 | |||
35 | EXPORT_SYMBOL(cpu_online_map); | ||
36 | EXPORT_SYMBOL(cpu_possible_map); | ||
37 | |||
38 | /* Per CPU bogomips and other parameters | ||
39 | * The only piece used here is the ipi pipe, which is set before SMP is | ||
40 | * started and never changed. | ||
41 | */ | ||
42 | struct cpuinfo_um cpu_data[NR_CPUS]; | ||
43 | |||
44 | /* A statistic, can be a little off */ | ||
45 | int num_reschedules_sent = 0; | ||
46 | |||
47 | /* Not changed after boot */ | ||
48 | struct task_struct *idle_threads[NR_CPUS]; | ||
49 | |||
50 | void smp_send_reschedule(int cpu) | ||
51 | { | ||
52 | os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); | ||
53 | num_reschedules_sent++; | ||
54 | } | ||
55 | |||
56 | void smp_send_stop(void) | ||
57 | { | ||
58 | int i; | ||
59 | |||
60 | printk(KERN_INFO "Stopping all CPUs..."); | ||
61 | for(i = 0; i < num_online_cpus(); i++){ | ||
62 | if(i == current_thread->cpu) | ||
63 | continue; | ||
64 | os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); | ||
65 | } | ||
66 | printk("done\n"); | ||
67 | } | ||
68 | |||
69 | static cpumask_t smp_commenced_mask = CPU_MASK_NONE; | ||
70 | static cpumask_t cpu_callin_map = CPU_MASK_NONE; | ||
71 | |||
72 | static int idle_proc(void *cpup) | ||
73 | { | ||
74 | int cpu = (int) cpup, err; | ||
75 | |||
76 | err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); | ||
77 | if(err < 0) | ||
78 | panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); | ||
79 | |||
80 | activate_ipi(cpu_data[cpu].ipi_pipe[0], | ||
81 | current->thread.mode.tt.extern_pid); | ||
82 | |||
83 | wmb(); | ||
84 | if (cpu_test_and_set(cpu, cpu_callin_map)) { | ||
85 | printk("huh, CPU#%d already present??\n", cpu); | ||
86 | BUG(); | ||
87 | } | ||
88 | |||
89 | while (!cpu_isset(cpu, smp_commenced_mask)) | ||
90 | cpu_relax(); | ||
91 | |||
92 | cpu_set(cpu, cpu_online_map); | ||
93 | default_idle(); | ||
94 | return(0); | ||
95 | } | ||
96 | |||
97 | static struct task_struct *idle_thread(int cpu) | ||
98 | { | ||
99 | struct task_struct *new_task; | ||
100 | unsigned char c; | ||
101 | |||
102 | current->thread.request.u.thread.proc = idle_proc; | ||
103 | current->thread.request.u.thread.arg = (void *) cpu; | ||
104 | new_task = fork_idle(cpu); | ||
105 | if(IS_ERR(new_task)) | ||
106 | panic("copy_process failed in idle_thread, error = %ld", | ||
107 | PTR_ERR(new_task)); | ||
108 | |||
109 | cpu_tasks[cpu] = ((struct cpu_task) | ||
110 | { .pid = new_task->thread.mode.tt.extern_pid, | ||
111 | .task = new_task } ); | ||
112 | idle_threads[cpu] = new_task; | ||
113 | CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c, | ||
114 | sizeof(c)), | ||
115 | ({ panic("skas mode doesn't support SMP"); })); | ||
116 | return(new_task); | ||
117 | } | ||
118 | |||
119 | void smp_prepare_cpus(unsigned int maxcpus) | ||
120 | { | ||
121 | struct task_struct *idle; | ||
122 | unsigned long waittime; | ||
123 | int err, cpu, me = smp_processor_id(); | ||
124 | int i; | ||
125 | |||
126 | for (i = 0; i < ncpus; ++i) | ||
127 | cpu_set(i, cpu_possible_map); | ||
128 | |||
129 | cpu_clear(me, cpu_online_map); | ||
130 | cpu_set(me, cpu_online_map); | ||
131 | cpu_set(me, cpu_callin_map); | ||
132 | |||
133 | err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); | ||
134 | if(err < 0) | ||
135 | panic("CPU#0 failed to create IPI pipe, errno = %d", -err); | ||
136 | |||
137 | activate_ipi(cpu_data[me].ipi_pipe[0], | ||
138 | current->thread.mode.tt.extern_pid); | ||
139 | |||
140 | for(cpu = 1; cpu < ncpus; cpu++){ | ||
141 | printk("Booting processor %d...\n", cpu); | ||
142 | |||
143 | idle = idle_thread(cpu); | ||
144 | |||
145 | init_idle(idle, cpu); | ||
146 | unhash_process(idle); | ||
147 | |||
148 | waittime = 200000000; | ||
149 | while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) | ||
150 | cpu_relax(); | ||
151 | |||
152 | if (cpu_isset(cpu, cpu_callin_map)) | ||
153 | printk("done\n"); | ||
154 | else printk("failed\n"); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | void smp_prepare_boot_cpu(void) | ||
159 | { | ||
160 | cpu_set(smp_processor_id(), cpu_online_map); | ||
161 | } | ||
162 | |||
163 | int __cpu_up(unsigned int cpu) | ||
164 | { | ||
165 | cpu_set(cpu, smp_commenced_mask); | ||
166 | while (!cpu_isset(cpu, cpu_online_map)) | ||
167 | mb(); | ||
168 | return(0); | ||
169 | } | ||
170 | |||
171 | int setup_profiling_timer(unsigned int multiplier) | ||
172 | { | ||
173 | printk(KERN_INFO "setup_profiling_timer\n"); | ||
174 | return(0); | ||
175 | } | ||
176 | |||
177 | void smp_call_function_slave(int cpu); | ||
178 | |||
179 | void IPI_handler(int cpu) | ||
180 | { | ||
181 | unsigned char c; | ||
182 | int fd; | ||
183 | |||
184 | fd = cpu_data[cpu].ipi_pipe[0]; | ||
185 | while (os_read_file(fd, &c, 1) == 1) { | ||
186 | switch (c) { | ||
187 | case 'C': | ||
188 | smp_call_function_slave(cpu); | ||
189 | break; | ||
190 | |||
191 | case 'R': | ||
192 | set_tsk_need_resched(current); | ||
193 | break; | ||
194 | |||
195 | case 'S': | ||
196 | printk("CPU#%d stopping\n", cpu); | ||
197 | while(1) | ||
198 | pause(); | ||
199 | break; | ||
200 | |||
201 | default: | ||
202 | printk("CPU#%d received unknown IPI [%c]!\n", cpu, c); | ||
203 | break; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | int hard_smp_processor_id(void) | ||
209 | { | ||
210 | return(pid_to_processor_id(os_getpid())); | ||
211 | } | ||
212 | |||
213 | static DEFINE_SPINLOCK(call_lock); | ||
214 | static atomic_t scf_started; | ||
215 | static atomic_t scf_finished; | ||
216 | static void (*func)(void *info); | ||
217 | static void *info; | ||
218 | |||
219 | void smp_call_function_slave(int cpu) | ||
220 | { | ||
221 | atomic_inc(&scf_started); | ||
222 | (*func)(info); | ||
223 | atomic_inc(&scf_finished); | ||
224 | } | ||
225 | |||
226 | int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, | ||
227 | int wait) | ||
228 | { | ||
229 | int cpus = num_online_cpus() - 1; | ||
230 | int i; | ||
231 | |||
232 | if (!cpus) | ||
233 | return 0; | ||
234 | |||
235 | /* Can deadlock when called with interrupts disabled */ | ||
236 | WARN_ON(irqs_disabled()); | ||
237 | |||
238 | spin_lock_bh(&call_lock); | ||
239 | atomic_set(&scf_started, 0); | ||
240 | atomic_set(&scf_finished, 0); | ||
241 | func = _func; | ||
242 | info = _info; | ||
243 | |||
244 | for_each_online_cpu(i) | ||
245 | os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); | ||
246 | |||
247 | while (atomic_read(&scf_started) != cpus) | ||
248 | barrier(); | ||
249 | |||
250 | if (wait) | ||
251 | while (atomic_read(&scf_finished) != cpus) | ||
252 | barrier(); | ||
253 | |||
254 | spin_unlock_bh(&call_lock); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | #endif | ||
259 | |||
260 | /* | ||
261 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
262 | * Emacs will notice this stuff at the end of the file and automatically | ||
263 | * adjust the settings for this buffer only. This must remain at the end | ||
264 | * of the file. | ||
265 | * --------------------------------------------------------------------------- | ||
266 | * Local variables: | ||
267 | * c-file-style: "linux" | ||
268 | * End: | ||
269 | */ | ||
diff --git a/arch/um/kernel/sys_call_table.c b/arch/um/kernel/sys_call_table.c new file mode 100644 index 000000000000..7fc06c85b29d --- /dev/null +++ b/arch/um/kernel/sys_call_table.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Copyright 2003 PathScale, Inc. | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include "linux/config.h" | ||
8 | #include "linux/unistd.h" | ||
9 | #include "linux/sys.h" | ||
10 | #include "linux/swap.h" | ||
11 | #include "linux/syscalls.h" | ||
12 | #include "linux/sysctl.h" | ||
13 | #include "asm/signal.h" | ||
14 | #include "sysdep/syscalls.h" | ||
15 | #include "kern_util.h" | ||
16 | |||
17 | #ifdef CONFIG_NFSD | ||
18 | #define NFSSERVCTL sys_nfsservctl | ||
19 | #else | ||
20 | #define NFSSERVCTL sys_ni_syscall | ||
21 | #endif | ||
22 | |||
23 | #define LAST_GENERIC_SYSCALL __NR_keyctl | ||
24 | |||
25 | #if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL | ||
26 | #define LAST_SYSCALL LAST_GENERIC_SYSCALL | ||
27 | #else | ||
28 | #define LAST_SYSCALL LAST_ARCH_SYSCALL | ||
29 | #endif | ||
30 | |||
31 | extern syscall_handler_t sys_fork; | ||
32 | extern syscall_handler_t sys_execve; | ||
33 | extern syscall_handler_t um_time; | ||
34 | extern syscall_handler_t um_stime; | ||
35 | extern syscall_handler_t sys_pipe; | ||
36 | extern syscall_handler_t sys_olduname; | ||
37 | extern syscall_handler_t sys_sigaction; | ||
38 | extern syscall_handler_t sys_sigsuspend; | ||
39 | extern syscall_handler_t old_readdir; | ||
40 | extern syscall_handler_t sys_uname; | ||
41 | extern syscall_handler_t sys_ipc; | ||
42 | extern syscall_handler_t sys_sigreturn; | ||
43 | extern syscall_handler_t sys_clone; | ||
44 | extern syscall_handler_t sys_rt_sigreturn; | ||
45 | extern syscall_handler_t sys_sigaltstack; | ||
46 | extern syscall_handler_t sys_vfork; | ||
47 | extern syscall_handler_t old_select; | ||
48 | extern syscall_handler_t sys_modify_ldt; | ||
49 | extern syscall_handler_t sys_rt_sigsuspend; | ||
50 | extern syscall_handler_t sys_mbind; | ||
51 | extern syscall_handler_t sys_get_mempolicy; | ||
52 | extern syscall_handler_t sys_set_mempolicy; | ||
53 | extern syscall_handler_t sys_sys_setaltroot; | ||
54 | |||
55 | syscall_handler_t *sys_call_table[] = { | ||
56 | [ __NR_restart_syscall ] = (syscall_handler_t *) sys_restart_syscall, | ||
57 | [ __NR_exit ] = (syscall_handler_t *) sys_exit, | ||
58 | [ __NR_fork ] = (syscall_handler_t *) sys_fork, | ||
59 | [ __NR_read ] = (syscall_handler_t *) sys_read, | ||
60 | [ __NR_write ] = (syscall_handler_t *) sys_write, | ||
61 | |||
62 | /* These three are declared differently in asm/unistd.h */ | ||
63 | [ __NR_open ] = (syscall_handler_t *) sys_open, | ||
64 | [ __NR_close ] = (syscall_handler_t *) sys_close, | ||
65 | [ __NR_creat ] = (syscall_handler_t *) sys_creat, | ||
66 | [ __NR_link ] = (syscall_handler_t *) sys_link, | ||
67 | [ __NR_unlink ] = (syscall_handler_t *) sys_unlink, | ||
68 | [ __NR_execve ] = (syscall_handler_t *) sys_execve, | ||
69 | |||
70 | /* declared differently in kern_util.h */ | ||
71 | [ __NR_chdir ] = (syscall_handler_t *) sys_chdir, | ||
72 | [ __NR_time ] = um_time, | ||
73 | [ __NR_mknod ] = (syscall_handler_t *) sys_mknod, | ||
74 | [ __NR_chmod ] = (syscall_handler_t *) sys_chmod, | ||
75 | [ __NR_lchown ] = (syscall_handler_t *) sys_lchown16, | ||
76 | [ __NR_lseek ] = (syscall_handler_t *) sys_lseek, | ||
77 | [ __NR_getpid ] = (syscall_handler_t *) sys_getpid, | ||
78 | [ __NR_mount ] = (syscall_handler_t *) sys_mount, | ||
79 | [ __NR_setuid ] = (syscall_handler_t *) sys_setuid16, | ||
80 | [ __NR_getuid ] = (syscall_handler_t *) sys_getuid16, | ||
81 | [ __NR_ptrace ] = (syscall_handler_t *) sys_ptrace, | ||
82 | [ __NR_alarm ] = (syscall_handler_t *) sys_alarm, | ||
83 | [ __NR_pause ] = (syscall_handler_t *) sys_pause, | ||
84 | [ __NR_utime ] = (syscall_handler_t *) sys_utime, | ||
85 | [ __NR_access ] = (syscall_handler_t *) sys_access, | ||
86 | [ __NR_sync ] = (syscall_handler_t *) sys_sync, | ||
87 | [ __NR_kill ] = (syscall_handler_t *) sys_kill, | ||
88 | [ __NR_rename ] = (syscall_handler_t *) sys_rename, | ||
89 | [ __NR_mkdir ] = (syscall_handler_t *) sys_mkdir, | ||
90 | [ __NR_rmdir ] = (syscall_handler_t *) sys_rmdir, | ||
91 | |||
92 | /* Declared differently in asm/unistd.h */ | ||
93 | [ __NR_dup ] = (syscall_handler_t *) sys_dup, | ||
94 | [ __NR_pipe ] = (syscall_handler_t *) sys_pipe, | ||
95 | [ __NR_times ] = (syscall_handler_t *) sys_times, | ||
96 | [ __NR_brk ] = (syscall_handler_t *) sys_brk, | ||
97 | [ __NR_setgid ] = (syscall_handler_t *) sys_setgid16, | ||
98 | [ __NR_getgid ] = (syscall_handler_t *) sys_getgid16, | ||
99 | [ __NR_geteuid ] = (syscall_handler_t *) sys_geteuid16, | ||
100 | [ __NR_getegid ] = (syscall_handler_t *) sys_getegid16, | ||
101 | [ __NR_acct ] = (syscall_handler_t *) sys_acct, | ||
102 | [ __NR_umount2 ] = (syscall_handler_t *) sys_umount, | ||
103 | [ __NR_ioctl ] = (syscall_handler_t *) sys_ioctl, | ||
104 | [ __NR_fcntl ] = (syscall_handler_t *) sys_fcntl, | ||
105 | [ __NR_setpgid ] = (syscall_handler_t *) sys_setpgid, | ||
106 | [ __NR_umask ] = (syscall_handler_t *) sys_umask, | ||
107 | [ __NR_chroot ] = (syscall_handler_t *) sys_chroot, | ||
108 | [ __NR_ustat ] = (syscall_handler_t *) sys_ustat, | ||
109 | [ __NR_dup2 ] = (syscall_handler_t *) sys_dup2, | ||
110 | [ __NR_getppid ] = (syscall_handler_t *) sys_getppid, | ||
111 | [ __NR_getpgrp ] = (syscall_handler_t *) sys_getpgrp, | ||
112 | [ __NR_setsid ] = (syscall_handler_t *) sys_setsid, | ||
113 | [ __NR_setreuid ] = (syscall_handler_t *) sys_setreuid16, | ||
114 | [ __NR_setregid ] = (syscall_handler_t *) sys_setregid16, | ||
115 | [ __NR_sethostname ] = (syscall_handler_t *) sys_sethostname, | ||
116 | [ __NR_setrlimit ] = (syscall_handler_t *) sys_setrlimit, | ||
117 | [ __NR_getrlimit ] = (syscall_handler_t *) sys_old_getrlimit, | ||
118 | [ __NR_getrusage ] = (syscall_handler_t *) sys_getrusage, | ||
119 | [ __NR_gettimeofday ] = (syscall_handler_t *) sys_gettimeofday, | ||
120 | [ __NR_settimeofday ] = (syscall_handler_t *) sys_settimeofday, | ||
121 | [ __NR_getgroups ] = (syscall_handler_t *) sys_getgroups16, | ||
122 | [ __NR_setgroups ] = (syscall_handler_t *) sys_setgroups16, | ||
123 | [ __NR_symlink ] = (syscall_handler_t *) sys_symlink, | ||
124 | [ __NR_readlink ] = (syscall_handler_t *) sys_readlink, | ||
125 | [ __NR_uselib ] = (syscall_handler_t *) sys_uselib, | ||
126 | [ __NR_swapon ] = (syscall_handler_t *) sys_swapon, | ||
127 | [ __NR_reboot ] = (syscall_handler_t *) sys_reboot, | ||
128 | [ __NR_munmap ] = (syscall_handler_t *) sys_munmap, | ||
129 | [ __NR_truncate ] = (syscall_handler_t *) sys_truncate, | ||
130 | [ __NR_ftruncate ] = (syscall_handler_t *) sys_ftruncate, | ||
131 | [ __NR_fchmod ] = (syscall_handler_t *) sys_fchmod, | ||
132 | [ __NR_fchown ] = (syscall_handler_t *) sys_fchown16, | ||
133 | [ __NR_getpriority ] = (syscall_handler_t *) sys_getpriority, | ||
134 | [ __NR_setpriority ] = (syscall_handler_t *) sys_setpriority, | ||
135 | [ __NR_statfs ] = (syscall_handler_t *) sys_statfs, | ||
136 | [ __NR_fstatfs ] = (syscall_handler_t *) sys_fstatfs, | ||
137 | [ __NR_ioperm ] = (syscall_handler_t *) sys_ni_syscall, | ||
138 | [ __NR_syslog ] = (syscall_handler_t *) sys_syslog, | ||
139 | [ __NR_setitimer ] = (syscall_handler_t *) sys_setitimer, | ||
140 | [ __NR_getitimer ] = (syscall_handler_t *) sys_getitimer, | ||
141 | [ __NR_stat ] = (syscall_handler_t *) sys_newstat, | ||
142 | [ __NR_lstat ] = (syscall_handler_t *) sys_newlstat, | ||
143 | [ __NR_fstat ] = (syscall_handler_t *) sys_newfstat, | ||
144 | [ __NR_vhangup ] = (syscall_handler_t *) sys_vhangup, | ||
145 | [ __NR_wait4 ] = (syscall_handler_t *) sys_wait4, | ||
146 | [ __NR_swapoff ] = (syscall_handler_t *) sys_swapoff, | ||
147 | [ __NR_sysinfo ] = (syscall_handler_t *) sys_sysinfo, | ||
148 | [ __NR_fsync ] = (syscall_handler_t *) sys_fsync, | ||
149 | [ __NR_clone ] = (syscall_handler_t *) sys_clone, | ||
150 | [ __NR_setdomainname ] = (syscall_handler_t *) sys_setdomainname, | ||
151 | [ __NR_uname ] = (syscall_handler_t *) sys_newuname, | ||
152 | [ __NR_adjtimex ] = (syscall_handler_t *) sys_adjtimex, | ||
153 | [ __NR_mprotect ] = (syscall_handler_t *) sys_mprotect, | ||
154 | [ __NR_create_module ] = (syscall_handler_t *) sys_ni_syscall, | ||
155 | [ __NR_init_module ] = (syscall_handler_t *) sys_init_module, | ||
156 | [ __NR_delete_module ] = (syscall_handler_t *) sys_delete_module, | ||
157 | [ __NR_get_kernel_syms ] = (syscall_handler_t *) sys_ni_syscall, | ||
158 | [ __NR_quotactl ] = (syscall_handler_t *) sys_quotactl, | ||
159 | [ __NR_getpgid ] = (syscall_handler_t *) sys_getpgid, | ||
160 | [ __NR_fchdir ] = (syscall_handler_t *) sys_fchdir, | ||
161 | [ __NR_sysfs ] = (syscall_handler_t *) sys_sysfs, | ||
162 | [ __NR_personality ] = (syscall_handler_t *) sys_personality, | ||
163 | [ __NR_afs_syscall ] = (syscall_handler_t *) sys_ni_syscall, | ||
164 | [ __NR_setfsuid ] = (syscall_handler_t *) sys_setfsuid16, | ||
165 | [ __NR_setfsgid ] = (syscall_handler_t *) sys_setfsgid16, | ||
166 | [ __NR_getdents ] = (syscall_handler_t *) sys_getdents, | ||
167 | [ __NR_flock ] = (syscall_handler_t *) sys_flock, | ||
168 | [ __NR_msync ] = (syscall_handler_t *) sys_msync, | ||
169 | [ __NR_readv ] = (syscall_handler_t *) sys_readv, | ||
170 | [ __NR_writev ] = (syscall_handler_t *) sys_writev, | ||
171 | [ __NR_getsid ] = (syscall_handler_t *) sys_getsid, | ||
172 | [ __NR_fdatasync ] = (syscall_handler_t *) sys_fdatasync, | ||
173 | [ __NR__sysctl ] = (syscall_handler_t *) sys_sysctl, | ||
174 | [ __NR_mlock ] = (syscall_handler_t *) sys_mlock, | ||
175 | [ __NR_munlock ] = (syscall_handler_t *) sys_munlock, | ||
176 | [ __NR_mlockall ] = (syscall_handler_t *) sys_mlockall, | ||
177 | [ __NR_munlockall ] = (syscall_handler_t *) sys_munlockall, | ||
178 | [ __NR_sched_setparam ] = (syscall_handler_t *) sys_sched_setparam, | ||
179 | [ __NR_sched_getparam ] = (syscall_handler_t *) sys_sched_getparam, | ||
180 | [ __NR_sched_setscheduler ] = (syscall_handler_t *) sys_sched_setscheduler, | ||
181 | [ __NR_sched_getscheduler ] = (syscall_handler_t *) sys_sched_getscheduler, | ||
182 | [ __NR_sched_yield ] = (syscall_handler_t *) yield, | ||
183 | [ __NR_sched_get_priority_max ] = (syscall_handler_t *) sys_sched_get_priority_max, | ||
184 | [ __NR_sched_get_priority_min ] = (syscall_handler_t *) sys_sched_get_priority_min, | ||
185 | [ __NR_sched_rr_get_interval ] = (syscall_handler_t *) sys_sched_rr_get_interval, | ||
186 | [ __NR_nanosleep ] = (syscall_handler_t *) sys_nanosleep, | ||
187 | [ __NR_mremap ] = (syscall_handler_t *) sys_mremap, | ||
188 | [ __NR_setresuid ] = (syscall_handler_t *) sys_setresuid16, | ||
189 | [ __NR_getresuid ] = (syscall_handler_t *) sys_getresuid16, | ||
190 | [ __NR_query_module ] = (syscall_handler_t *) sys_ni_syscall, | ||
191 | [ __NR_poll ] = (syscall_handler_t *) sys_poll, | ||
192 | [ __NR_nfsservctl ] = (syscall_handler_t *) NFSSERVCTL, | ||
193 | [ __NR_setresgid ] = (syscall_handler_t *) sys_setresgid16, | ||
194 | [ __NR_getresgid ] = (syscall_handler_t *) sys_getresgid16, | ||
195 | [ __NR_prctl ] = (syscall_handler_t *) sys_prctl, | ||
196 | [ __NR_rt_sigreturn ] = (syscall_handler_t *) sys_rt_sigreturn, | ||
197 | [ __NR_rt_sigaction ] = (syscall_handler_t *) sys_rt_sigaction, | ||
198 | [ __NR_rt_sigprocmask ] = (syscall_handler_t *) sys_rt_sigprocmask, | ||
199 | [ __NR_rt_sigpending ] = (syscall_handler_t *) sys_rt_sigpending, | ||
200 | [ __NR_rt_sigtimedwait ] = (syscall_handler_t *) sys_rt_sigtimedwait, | ||
201 | [ __NR_rt_sigqueueinfo ] = (syscall_handler_t *) sys_rt_sigqueueinfo, | ||
202 | [ __NR_rt_sigsuspend ] = (syscall_handler_t *) sys_rt_sigsuspend, | ||
203 | [ __NR_pread64 ] = (syscall_handler_t *) sys_pread64, | ||
204 | [ __NR_pwrite64 ] = (syscall_handler_t *) sys_pwrite64, | ||
205 | [ __NR_chown ] = (syscall_handler_t *) sys_chown16, | ||
206 | [ __NR_getcwd ] = (syscall_handler_t *) sys_getcwd, | ||
207 | [ __NR_capget ] = (syscall_handler_t *) sys_capget, | ||
208 | [ __NR_capset ] = (syscall_handler_t *) sys_capset, | ||
209 | [ __NR_sigaltstack ] = (syscall_handler_t *) sys_sigaltstack, | ||
210 | [ __NR_sendfile ] = (syscall_handler_t *) sys_sendfile, | ||
211 | [ __NR_getpmsg ] = (syscall_handler_t *) sys_ni_syscall, | ||
212 | [ __NR_putpmsg ] = (syscall_handler_t *) sys_ni_syscall, | ||
213 | [ __NR_vfork ] = (syscall_handler_t *) sys_vfork, | ||
214 | [ __NR_getdents64 ] = (syscall_handler_t *) sys_getdents64, | ||
215 | [ __NR_gettid ] = (syscall_handler_t *) sys_gettid, | ||
216 | [ __NR_readahead ] = (syscall_handler_t *) sys_readahead, | ||
217 | [ __NR_setxattr ] = (syscall_handler_t *) sys_setxattr, | ||
218 | [ __NR_lsetxattr ] = (syscall_handler_t *) sys_lsetxattr, | ||
219 | [ __NR_fsetxattr ] = (syscall_handler_t *) sys_fsetxattr, | ||
220 | [ __NR_getxattr ] = (syscall_handler_t *) sys_getxattr, | ||
221 | [ __NR_lgetxattr ] = (syscall_handler_t *) sys_lgetxattr, | ||
222 | [ __NR_fgetxattr ] = (syscall_handler_t *) sys_fgetxattr, | ||
223 | [ __NR_listxattr ] = (syscall_handler_t *) sys_listxattr, | ||
224 | [ __NR_llistxattr ] = (syscall_handler_t *) sys_llistxattr, | ||
225 | [ __NR_flistxattr ] = (syscall_handler_t *) sys_flistxattr, | ||
226 | [ __NR_removexattr ] = (syscall_handler_t *) sys_removexattr, | ||
227 | [ __NR_lremovexattr ] = (syscall_handler_t *) sys_lremovexattr, | ||
228 | [ __NR_fremovexattr ] = (syscall_handler_t *) sys_fremovexattr, | ||
229 | [ __NR_tkill ] = (syscall_handler_t *) sys_tkill, | ||
230 | [ __NR_futex ] = (syscall_handler_t *) sys_futex, | ||
231 | [ __NR_sched_setaffinity ] = (syscall_handler_t *) sys_sched_setaffinity, | ||
232 | [ __NR_sched_getaffinity ] = (syscall_handler_t *) sys_sched_getaffinity, | ||
233 | [ __NR_io_setup ] = (syscall_handler_t *) sys_io_setup, | ||
234 | [ __NR_io_destroy ] = (syscall_handler_t *) sys_io_destroy, | ||
235 | [ __NR_io_getevents ] = (syscall_handler_t *) sys_io_getevents, | ||
236 | [ __NR_io_submit ] = (syscall_handler_t *) sys_io_submit, | ||
237 | [ __NR_io_cancel ] = (syscall_handler_t *) sys_io_cancel, | ||
238 | [ __NR_exit_group ] = (syscall_handler_t *) sys_exit_group, | ||
239 | [ __NR_lookup_dcookie ] = (syscall_handler_t *) sys_lookup_dcookie, | ||
240 | [ __NR_epoll_create ] = (syscall_handler_t *) sys_epoll_create, | ||
241 | [ __NR_epoll_ctl ] = (syscall_handler_t *) sys_epoll_ctl, | ||
242 | [ __NR_epoll_wait ] = (syscall_handler_t *) sys_epoll_wait, | ||
243 | [ __NR_remap_file_pages ] = (syscall_handler_t *) sys_remap_file_pages, | ||
244 | [ __NR_set_tid_address ] = (syscall_handler_t *) sys_set_tid_address, | ||
245 | [ __NR_timer_create ] = (syscall_handler_t *) sys_timer_create, | ||
246 | [ __NR_timer_settime ] = (syscall_handler_t *) sys_timer_settime, | ||
247 | [ __NR_timer_gettime ] = (syscall_handler_t *) sys_timer_gettime, | ||
248 | [ __NR_timer_getoverrun ] = (syscall_handler_t *) sys_timer_getoverrun, | ||
249 | [ __NR_timer_delete ] = (syscall_handler_t *) sys_timer_delete, | ||
250 | [ __NR_clock_settime ] = (syscall_handler_t *) sys_clock_settime, | ||
251 | [ __NR_clock_gettime ] = (syscall_handler_t *) sys_clock_gettime, | ||
252 | [ __NR_clock_getres ] = (syscall_handler_t *) sys_clock_getres, | ||
253 | [ __NR_clock_nanosleep ] = (syscall_handler_t *) sys_clock_nanosleep, | ||
254 | [ __NR_tgkill ] = (syscall_handler_t *) sys_tgkill, | ||
255 | [ __NR_utimes ] = (syscall_handler_t *) sys_utimes, | ||
256 | [ __NR_fadvise64 ] = (syscall_handler_t *) sys_fadvise64, | ||
257 | [ __NR_vserver ] = (syscall_handler_t *) sys_ni_syscall, | ||
258 | [ __NR_mbind ] = (syscall_handler_t *) sys_mbind, | ||
259 | [ __NR_get_mempolicy ] = (syscall_handler_t *) sys_get_mempolicy, | ||
260 | [ __NR_set_mempolicy ] = (syscall_handler_t *) sys_set_mempolicy, | ||
261 | [ __NR_mq_open ] = (syscall_handler_t *) sys_mq_open, | ||
262 | [ __NR_mq_unlink ] = (syscall_handler_t *) sys_mq_unlink, | ||
263 | [ __NR_mq_timedsend ] = (syscall_handler_t *) sys_mq_timedsend, | ||
264 | [ __NR_mq_timedreceive ] = (syscall_handler_t *) sys_mq_timedreceive, | ||
265 | [ __NR_mq_notify ] = (syscall_handler_t *) sys_mq_notify, | ||
266 | [ __NR_mq_getsetattr ] = (syscall_handler_t *) sys_mq_getsetattr, | ||
267 | [ __NR_kexec_load ] = (syscall_handler_t *) sys_ni_syscall, | ||
268 | [ __NR_waitid ] = (syscall_handler_t *) sys_waitid, | ||
269 | [ __NR_add_key ] = (syscall_handler_t *) sys_add_key, | ||
270 | [ __NR_request_key ] = (syscall_handler_t *) sys_request_key, | ||
271 | [ __NR_keyctl ] = (syscall_handler_t *) sys_keyctl, | ||
272 | |||
273 | ARCH_SYSCALLS | ||
274 | [ LAST_SYSCALL + 1 ... NR_syscalls ] = | ||
275 | (syscall_handler_t *) sys_ni_syscall | ||
276 | }; | ||
diff --git a/arch/um/kernel/syscall_kern.c b/arch/um/kernel/syscall_kern.c new file mode 100644 index 000000000000..42731e04f50f --- /dev/null +++ b/arch/um/kernel/syscall_kern.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/file.h" | ||
8 | #include "linux/smp_lock.h" | ||
9 | #include "linux/mm.h" | ||
10 | #include "linux/utsname.h" | ||
11 | #include "linux/msg.h" | ||
12 | #include "linux/shm.h" | ||
13 | #include "linux/sys.h" | ||
14 | #include "linux/syscalls.h" | ||
15 | #include "linux/unistd.h" | ||
16 | #include "linux/slab.h" | ||
17 | #include "linux/utime.h" | ||
18 | #include "asm/mman.h" | ||
19 | #include "asm/uaccess.h" | ||
20 | #include "asm/ipc.h" | ||
21 | #include "kern_util.h" | ||
22 | #include "user_util.h" | ||
23 | #include "sysdep/syscalls.h" | ||
24 | #include "mode_kern.h" | ||
25 | #include "choose-mode.h" | ||
26 | |||
27 | /* Unlocked, I don't care if this is a bit off */ | ||
28 | int nsyscalls = 0; | ||
29 | |||
30 | long sys_fork(void) | ||
31 | { | ||
32 | long ret; | ||
33 | |||
34 | current->thread.forking = 1; | ||
35 | ret = do_fork(SIGCHLD, 0, NULL, 0, NULL, NULL); | ||
36 | current->thread.forking = 0; | ||
37 | return(ret); | ||
38 | } | ||
39 | |||
40 | long sys_vfork(void) | ||
41 | { | ||
42 | long ret; | ||
43 | |||
44 | current->thread.forking = 1; | ||
45 | ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0, NULL, | ||
46 | NULL); | ||
47 | current->thread.forking = 0; | ||
48 | return(ret); | ||
49 | } | ||
50 | |||
51 | /* common code for old and new mmaps */ | ||
52 | long sys_mmap2(unsigned long addr, unsigned long len, | ||
53 | unsigned long prot, unsigned long flags, | ||
54 | unsigned long fd, unsigned long pgoff) | ||
55 | { | ||
56 | long error = -EBADF; | ||
57 | struct file * file = NULL; | ||
58 | |||
59 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
60 | if (!(flags & MAP_ANONYMOUS)) { | ||
61 | file = fget(fd); | ||
62 | if (!file) | ||
63 | goto out; | ||
64 | } | ||
65 | |||
66 | down_write(¤t->mm->mmap_sem); | ||
67 | error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); | ||
68 | up_write(¤t->mm->mmap_sem); | ||
69 | |||
70 | if (file) | ||
71 | fput(file); | ||
72 | out: | ||
73 | return error; | ||
74 | } | ||
75 | |||
76 | long old_mmap(unsigned long addr, unsigned long len, | ||
77 | unsigned long prot, unsigned long flags, | ||
78 | unsigned long fd, unsigned long offset) | ||
79 | { | ||
80 | long err = -EINVAL; | ||
81 | if (offset & ~PAGE_MASK) | ||
82 | goto out; | ||
83 | |||
84 | err = sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); | ||
85 | out: | ||
86 | return err; | ||
87 | } | ||
88 | /* | ||
89 | * sys_pipe() is the normal C calling standard for creating | ||
90 | * a pipe. It's not the way unix traditionally does this, though. | ||
91 | */ | ||
92 | long sys_pipe(unsigned long __user * fildes) | ||
93 | { | ||
94 | int fd[2]; | ||
95 | long error; | ||
96 | |||
97 | error = do_pipe(fd); | ||
98 | if (!error) { | ||
99 | if (copy_to_user(fildes, fd, sizeof(fd))) | ||
100 | error = -EFAULT; | ||
101 | } | ||
102 | return error; | ||
103 | } | ||
104 | |||
105 | |||
106 | long sys_uname(struct old_utsname * name) | ||
107 | { | ||
108 | long err; | ||
109 | if (!name) | ||
110 | return -EFAULT; | ||
111 | down_read(&uts_sem); | ||
112 | err=copy_to_user(name, &system_utsname, sizeof (*name)); | ||
113 | up_read(&uts_sem); | ||
114 | return err?-EFAULT:0; | ||
115 | } | ||
116 | |||
117 | long sys_olduname(struct oldold_utsname * name) | ||
118 | { | ||
119 | long error; | ||
120 | |||
121 | if (!name) | ||
122 | return -EFAULT; | ||
123 | if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) | ||
124 | return -EFAULT; | ||
125 | |||
126 | down_read(&uts_sem); | ||
127 | |||
128 | error = __copy_to_user(&name->sysname,&system_utsname.sysname, | ||
129 | __OLD_UTS_LEN); | ||
130 | error |= __put_user(0,name->sysname+__OLD_UTS_LEN); | ||
131 | error |= __copy_to_user(&name->nodename,&system_utsname.nodename, | ||
132 | __OLD_UTS_LEN); | ||
133 | error |= __put_user(0,name->nodename+__OLD_UTS_LEN); | ||
134 | error |= __copy_to_user(&name->release,&system_utsname.release, | ||
135 | __OLD_UTS_LEN); | ||
136 | error |= __put_user(0,name->release+__OLD_UTS_LEN); | ||
137 | error |= __copy_to_user(&name->version,&system_utsname.version, | ||
138 | __OLD_UTS_LEN); | ||
139 | error |= __put_user(0,name->version+__OLD_UTS_LEN); | ||
140 | error |= __copy_to_user(&name->machine,&system_utsname.machine, | ||
141 | __OLD_UTS_LEN); | ||
142 | error |= __put_user(0,name->machine+__OLD_UTS_LEN); | ||
143 | |||
144 | up_read(&uts_sem); | ||
145 | |||
146 | error = error ? -EFAULT : 0; | ||
147 | |||
148 | return error; | ||
149 | } | ||
150 | |||
151 | DEFINE_SPINLOCK(syscall_lock); | ||
152 | |||
153 | static int syscall_index = 0; | ||
154 | |||
155 | int next_syscall_index(int limit) | ||
156 | { | ||
157 | int ret; | ||
158 | |||
159 | spin_lock(&syscall_lock); | ||
160 | ret = syscall_index; | ||
161 | if(++syscall_index == limit) | ||
162 | syscall_index = 0; | ||
163 | spin_unlock(&syscall_lock); | ||
164 | return(ret); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
169 | * Emacs will notice this stuff at the end of the file and automatically | ||
170 | * adjust the settings for this buffer only. This must remain at the end | ||
171 | * of the file. | ||
172 | * --------------------------------------------------------------------------- | ||
173 | * Local variables: | ||
174 | * c-file-style: "linux" | ||
175 | * End: | ||
176 | */ | ||
diff --git a/arch/um/kernel/syscall_user.c b/arch/um/kernel/syscall_user.c new file mode 100644 index 000000000000..01b711e00a85 --- /dev/null +++ b/arch/um/kernel/syscall_user.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <sys/time.h> | ||
8 | #include "kern_util.h" | ||
9 | #include "syscall_user.h" | ||
10 | |||
11 | struct { | ||
12 | int syscall; | ||
13 | int pid; | ||
14 | long result; | ||
15 | struct timeval start; | ||
16 | struct timeval end; | ||
17 | } syscall_record[1024]; | ||
18 | |||
19 | int record_syscall_start(int syscall) | ||
20 | { | ||
21 | int max, index; | ||
22 | |||
23 | max = sizeof(syscall_record)/sizeof(syscall_record[0]); | ||
24 | index = next_syscall_index(max); | ||
25 | |||
26 | syscall_record[index].syscall = syscall; | ||
27 | syscall_record[index].pid = current_pid(); | ||
28 | syscall_record[index].result = 0xdeadbeef; | ||
29 | gettimeofday(&syscall_record[index].start, NULL); | ||
30 | return(index); | ||
31 | } | ||
32 | |||
33 | void record_syscall_end(int index, long result) | ||
34 | { | ||
35 | syscall_record[index].result = result; | ||
36 | gettimeofday(&syscall_record[index].end, NULL); | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
41 | * Emacs will notice this stuff at the end of the file and automatically | ||
42 | * adjust the settings for this buffer only. This must remain at the end | ||
43 | * of the file. | ||
44 | * --------------------------------------------------------------------------- | ||
45 | * Local variables: | ||
46 | * c-file-style: "linux" | ||
47 | * End: | ||
48 | */ | ||
diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c new file mode 100644 index 000000000000..e630438f9e73 --- /dev/null +++ b/arch/um/kernel/sysrq.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/kernel.h" | ||
8 | #include "linux/module.h" | ||
9 | #include "linux/kallsyms.h" | ||
10 | #include "asm/page.h" | ||
11 | #include "asm/processor.h" | ||
12 | #include "sysrq.h" | ||
13 | #include "user_util.h" | ||
14 | |||
15 | void show_trace(unsigned long * stack) | ||
16 | { | ||
17 | /* XXX: Copy the CONFIG_FRAME_POINTER stack-walking backtrace from | ||
18 | * arch/i386/kernel/traps.c, and then move this to sys-i386/sysrq.c.*/ | ||
19 | unsigned long addr; | ||
20 | |||
21 | if (!stack) { | ||
22 | stack = (unsigned long*) &stack; | ||
23 | WARN_ON(1); | ||
24 | } | ||
25 | |||
26 | printk("Call Trace: \n"); | ||
27 | while (((long) stack & (THREAD_SIZE-1)) != 0) { | ||
28 | addr = *stack; | ||
29 | if (__kernel_text_address(addr)) { | ||
30 | printk("%08lx: [<%08lx>]", (unsigned long) stack, addr); | ||
31 | print_symbol(" %s", addr); | ||
32 | printk("\n"); | ||
33 | } | ||
34 | stack++; | ||
35 | } | ||
36 | printk("\n"); | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * stack dumps generator - this is used by arch-independent code. | ||
41 | * And this is identical to i386 currently. | ||
42 | */ | ||
43 | void dump_stack(void) | ||
44 | { | ||
45 | unsigned long stack; | ||
46 | |||
47 | show_trace(&stack); | ||
48 | } | ||
49 | EXPORT_SYMBOL(dump_stack); | ||
50 | |||
51 | /*Stolen from arch/i386/kernel/traps.c */ | ||
52 | static int kstack_depth_to_print = 24; | ||
53 | |||
54 | /* This recently started being used in arch-independent code too, as in | ||
55 | * kernel/sched.c.*/ | ||
56 | void show_stack(struct task_struct *task, unsigned long *esp) | ||
57 | { | ||
58 | unsigned long *stack; | ||
59 | int i; | ||
60 | |||
61 | if (esp == NULL) { | ||
62 | if (task != current) { | ||
63 | esp = (unsigned long *) KSTK_ESP(task); | ||
64 | /* Which one? No actual difference - just coding style.*/ | ||
65 | //esp = (unsigned long *) PT_REGS_IP(&task->thread.regs); | ||
66 | } else { | ||
67 | esp = (unsigned long *) &esp; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | stack = esp; | ||
72 | for(i = 0; i < kstack_depth_to_print; i++) { | ||
73 | if (kstack_end(stack)) | ||
74 | break; | ||
75 | if (i && ((i % 8) == 0)) | ||
76 | printk("\n "); | ||
77 | printk("%08lx ", *stack++); | ||
78 | } | ||
79 | |||
80 | show_trace(esp); | ||
81 | } | ||
diff --git a/arch/um/kernel/tempfile.c b/arch/um/kernel/tempfile.c new file mode 100644 index 000000000000..b1674bc1395d --- /dev/null +++ b/arch/um/kernel/tempfile.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | #include <sys/param.h> | ||
12 | #include "init.h" | ||
13 | |||
14 | /* Modified from create_mem_file and start_debugger */ | ||
15 | static char *tempdir = NULL; | ||
16 | |||
17 | static void __init find_tempdir(void) | ||
18 | { | ||
19 | char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; | ||
20 | int i; | ||
21 | char *dir = NULL; | ||
22 | |||
23 | if(tempdir != NULL) return; /* We've already been called */ | ||
24 | for(i = 0; dirs[i]; i++){ | ||
25 | dir = getenv(dirs[i]); | ||
26 | if((dir != NULL) && (*dir != '\0')) | ||
27 | break; | ||
28 | } | ||
29 | if((dir == NULL) || (*dir == '\0')) | ||
30 | dir = "/tmp"; | ||
31 | |||
32 | tempdir = malloc(strlen(dir) + 2); | ||
33 | if(tempdir == NULL){ | ||
34 | fprintf(stderr, "Failed to malloc tempdir, " | ||
35 | "errno = %d\n", errno); | ||
36 | return; | ||
37 | } | ||
38 | strcpy(tempdir, dir); | ||
39 | strcat(tempdir, "/"); | ||
40 | } | ||
41 | |||
42 | int make_tempfile(const char *template, char **out_tempname, int do_unlink) | ||
43 | { | ||
44 | char tempname[MAXPATHLEN]; | ||
45 | int fd; | ||
46 | |||
47 | find_tempdir(); | ||
48 | if (*template != '/') | ||
49 | strcpy(tempname, tempdir); | ||
50 | else | ||
51 | *tempname = 0; | ||
52 | strcat(tempname, template); | ||
53 | fd = mkstemp(tempname); | ||
54 | if(fd < 0){ | ||
55 | fprintf(stderr, "open - cannot create %s: %s\n", tempname, | ||
56 | strerror(errno)); | ||
57 | return -1; | ||
58 | } | ||
59 | if(do_unlink && (unlink(tempname) < 0)){ | ||
60 | perror("unlink"); | ||
61 | return -1; | ||
62 | } | ||
63 | if(out_tempname){ | ||
64 | *out_tempname = strdup(tempname); | ||
65 | if(*out_tempname == NULL){ | ||
66 | perror("strdup"); | ||
67 | return -1; | ||
68 | } | ||
69 | } | ||
70 | return(fd); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
75 | * Emacs will notice this stuff at the end of the file and automatically | ||
76 | * adjust the settings for this buffer only. This must remain at the end | ||
77 | * of the file. | ||
78 | * --------------------------------------------------------------------------- | ||
79 | * Local variables: | ||
80 | * c-file-style: "linux" | ||
81 | * End: | ||
82 | */ | ||
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c new file mode 100644 index 000000000000..c40c86a3f918 --- /dev/null +++ b/arch/um/kernel/time.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <time.h> | ||
10 | #include <sys/time.h> | ||
11 | #include <signal.h> | ||
12 | #include <errno.h> | ||
13 | #include "user_util.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "user.h" | ||
16 | #include "process.h" | ||
17 | #include "signal_user.h" | ||
18 | #include "time_user.h" | ||
19 | #include "kern_constants.h" | ||
20 | |||
21 | /* XXX This really needs to be declared and initialized in a kernel file since | ||
22 | * it's in <linux/time.h> | ||
23 | */ | ||
24 | extern struct timespec wall_to_monotonic; | ||
25 | |||
26 | extern struct timeval xtime; | ||
27 | |||
28 | struct timeval local_offset = { 0, 0 }; | ||
29 | |||
30 | void timer(void) | ||
31 | { | ||
32 | gettimeofday(&xtime, NULL); | ||
33 | timeradd(&xtime, &local_offset, &xtime); | ||
34 | } | ||
35 | |||
36 | void set_interval(int timer_type) | ||
37 | { | ||
38 | int usec = 1000000/hz(); | ||
39 | struct itimerval interval = ((struct itimerval) { { 0, usec }, | ||
40 | { 0, usec } }); | ||
41 | |||
42 | if(setitimer(timer_type, &interval, NULL) == -1) | ||
43 | panic("setitimer failed - errno = %d\n", errno); | ||
44 | } | ||
45 | |||
46 | void enable_timer(void) | ||
47 | { | ||
48 | int usec = 1000000/hz(); | ||
49 | struct itimerval enable = ((struct itimerval) { { 0, usec }, | ||
50 | { 0, usec }}); | ||
51 | if(setitimer(ITIMER_VIRTUAL, &enable, NULL)) | ||
52 | printk("enable_timer - setitimer failed, errno = %d\n", | ||
53 | errno); | ||
54 | } | ||
55 | |||
56 | void disable_timer(void) | ||
57 | { | ||
58 | struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); | ||
59 | if((setitimer(ITIMER_VIRTUAL, &disable, NULL) < 0) || | ||
60 | (setitimer(ITIMER_REAL, &disable, NULL) < 0)) | ||
61 | printk("disnable_timer - setitimer failed, errno = %d\n", | ||
62 | errno); | ||
63 | /* If there are signals already queued, after unblocking ignore them */ | ||
64 | set_handler(SIGALRM, SIG_IGN, 0, -1); | ||
65 | set_handler(SIGVTALRM, SIG_IGN, 0, -1); | ||
66 | } | ||
67 | |||
68 | void switch_timers(int to_real) | ||
69 | { | ||
70 | struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); | ||
71 | struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() }, | ||
72 | { 0, 1000000/hz() }}); | ||
73 | int old, new; | ||
74 | |||
75 | if(to_real){ | ||
76 | old = ITIMER_VIRTUAL; | ||
77 | new = ITIMER_REAL; | ||
78 | } | ||
79 | else { | ||
80 | old = ITIMER_REAL; | ||
81 | new = ITIMER_VIRTUAL; | ||
82 | } | ||
83 | |||
84 | if((setitimer(old, &disable, NULL) < 0) || | ||
85 | (setitimer(new, &enable, NULL))) | ||
86 | printk("switch_timers - setitimer failed, errno = %d\n", | ||
87 | errno); | ||
88 | } | ||
89 | |||
90 | void uml_idle_timer(void) | ||
91 | { | ||
92 | if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) | ||
93 | panic("Couldn't unset SIGVTALRM handler"); | ||
94 | |||
95 | set_handler(SIGALRM, (__sighandler_t) alarm_handler, | ||
96 | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1); | ||
97 | set_interval(ITIMER_REAL); | ||
98 | } | ||
99 | |||
100 | extern int do_posix_clock_monotonic_gettime(struct timespec *tp); | ||
101 | |||
102 | void time_init(void) | ||
103 | { | ||
104 | struct timespec now; | ||
105 | |||
106 | if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) | ||
107 | panic("Couldn't set SIGVTALRM handler"); | ||
108 | set_interval(ITIMER_VIRTUAL); | ||
109 | |||
110 | do_posix_clock_monotonic_gettime(&now); | ||
111 | wall_to_monotonic.tv_sec = -now.tv_sec; | ||
112 | wall_to_monotonic.tv_nsec = -now.tv_nsec; | ||
113 | } | ||
114 | |||
115 | /* Declared in linux/time.h, which can't be included here */ | ||
116 | extern void clock_was_set(void); | ||
117 | |||
118 | void do_gettimeofday(struct timeval *tv) | ||
119 | { | ||
120 | unsigned long flags; | ||
121 | |||
122 | flags = time_lock(); | ||
123 | gettimeofday(tv, NULL); | ||
124 | timeradd(tv, &local_offset, tv); | ||
125 | time_unlock(flags); | ||
126 | clock_was_set(); | ||
127 | } | ||
128 | |||
129 | int do_settimeofday(struct timespec *tv) | ||
130 | { | ||
131 | struct timeval now; | ||
132 | unsigned long flags; | ||
133 | struct timeval tv_in; | ||
134 | |||
135 | if ((unsigned long) tv->tv_nsec >= UM_NSEC_PER_SEC) | ||
136 | return -EINVAL; | ||
137 | |||
138 | tv_in.tv_sec = tv->tv_sec; | ||
139 | tv_in.tv_usec = tv->tv_nsec / 1000; | ||
140 | |||
141 | flags = time_lock(); | ||
142 | gettimeofday(&now, NULL); | ||
143 | timersub(&tv_in, &now, &local_offset); | ||
144 | time_unlock(flags); | ||
145 | |||
146 | return(0); | ||
147 | } | ||
148 | |||
149 | void idle_sleep(int secs) | ||
150 | { | ||
151 | struct timespec ts; | ||
152 | |||
153 | ts.tv_sec = secs; | ||
154 | ts.tv_nsec = 0; | ||
155 | nanosleep(&ts, NULL); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
160 | * Emacs will notice this stuff at the end of the file and automatically | ||
161 | * adjust the settings for this buffer only. This must remain at the end | ||
162 | * of the file. | ||
163 | * --------------------------------------------------------------------------- | ||
164 | * Local variables: | ||
165 | * c-file-style: "linux" | ||
166 | * End: | ||
167 | */ | ||
diff --git a/arch/um/kernel/time_kern.c b/arch/um/kernel/time_kern.c new file mode 100644 index 000000000000..2461cd73ca87 --- /dev/null +++ b/arch/um/kernel/time_kern.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/kernel.h" | ||
7 | #include "linux/module.h" | ||
8 | #include "linux/unistd.h" | ||
9 | #include "linux/stddef.h" | ||
10 | #include "linux/spinlock.h" | ||
11 | #include "linux/time.h" | ||
12 | #include "linux/sched.h" | ||
13 | #include "linux/interrupt.h" | ||
14 | #include "linux/init.h" | ||
15 | #include "linux/delay.h" | ||
16 | #include "asm/irq.h" | ||
17 | #include "asm/param.h" | ||
18 | #include "asm/current.h" | ||
19 | #include "kern_util.h" | ||
20 | #include "user_util.h" | ||
21 | #include "time_user.h" | ||
22 | #include "mode.h" | ||
23 | #include "os.h" | ||
24 | |||
25 | u64 jiffies_64 = INITIAL_JIFFIES; | ||
26 | |||
27 | EXPORT_SYMBOL(jiffies_64); | ||
28 | |||
29 | int hz(void) | ||
30 | { | ||
31 | return(HZ); | ||
32 | } | ||
33 | |||
34 | /* | ||
35 | * Scheduler clock - returns current time in nanosec units. | ||
36 | */ | ||
37 | unsigned long long sched_clock(void) | ||
38 | { | ||
39 | return (unsigned long long)jiffies_64 * (1000000000 / HZ); | ||
40 | } | ||
41 | |||
42 | /* Changed at early boot */ | ||
43 | int timer_irq_inited = 0; | ||
44 | |||
45 | static int first_tick; | ||
46 | static unsigned long long prev_usecs; | ||
47 | #ifdef CONFIG_UML_REAL_TIME_CLOCK | ||
48 | static long long delta; /* Deviation per interval */ | ||
49 | #endif | ||
50 | |||
51 | #define MILLION 1000000 | ||
52 | |||
53 | void timer_irq(union uml_pt_regs *regs) | ||
54 | { | ||
55 | unsigned long long ticks = 0; | ||
56 | |||
57 | if(!timer_irq_inited){ | ||
58 | /* This is to ensure that ticks don't pile up when | ||
59 | * the timer handler is suspended */ | ||
60 | first_tick = 0; | ||
61 | return; | ||
62 | } | ||
63 | |||
64 | if(first_tick){ | ||
65 | #ifdef CONFIG_UML_REAL_TIME_CLOCK | ||
66 | /* We've had 1 tick */ | ||
67 | unsigned long long usecs = os_usecs(); | ||
68 | |||
69 | delta += usecs - prev_usecs; | ||
70 | prev_usecs = usecs; | ||
71 | |||
72 | /* Protect against the host clock being set backwards */ | ||
73 | if(delta < 0) | ||
74 | delta = 0; | ||
75 | |||
76 | ticks += (delta * HZ) / MILLION; | ||
77 | delta -= (ticks * MILLION) / HZ; | ||
78 | #else | ||
79 | ticks = 1; | ||
80 | #endif | ||
81 | } | ||
82 | else { | ||
83 | prev_usecs = os_usecs(); | ||
84 | first_tick = 1; | ||
85 | } | ||
86 | |||
87 | while(ticks > 0){ | ||
88 | do_IRQ(TIMER_IRQ, regs); | ||
89 | ticks--; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | void boot_timer_handler(int sig) | ||
94 | { | ||
95 | struct pt_regs regs; | ||
96 | |||
97 | CHOOSE_MODE((void) | ||
98 | (UPT_SC(®s.regs) = (struct sigcontext *) (&sig + 1)), | ||
99 | (void) (regs.regs.skas.is_user = 0)); | ||
100 | do_timer(®s); | ||
101 | } | ||
102 | |||
103 | irqreturn_t um_timer(int irq, void *dev, struct pt_regs *regs) | ||
104 | { | ||
105 | unsigned long flags; | ||
106 | |||
107 | do_timer(regs); | ||
108 | write_seqlock_irqsave(&xtime_lock, flags); | ||
109 | timer(); | ||
110 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
111 | return(IRQ_HANDLED); | ||
112 | } | ||
113 | |||
114 | long um_time(int __user *tloc) | ||
115 | { | ||
116 | struct timeval now; | ||
117 | |||
118 | do_gettimeofday(&now); | ||
119 | if (tloc) { | ||
120 | if (put_user(now.tv_sec, tloc)) | ||
121 | now.tv_sec = -EFAULT; | ||
122 | } | ||
123 | return now.tv_sec; | ||
124 | } | ||
125 | |||
126 | long um_stime(int __user *tptr) | ||
127 | { | ||
128 | int value; | ||
129 | struct timespec new; | ||
130 | |||
131 | if (get_user(value, tptr)) | ||
132 | return -EFAULT; | ||
133 | new.tv_sec = value; | ||
134 | new.tv_nsec = 0; | ||
135 | do_settimeofday(&new); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | void __udelay(unsigned long usecs) | ||
140 | { | ||
141 | int i, n; | ||
142 | |||
143 | n = (loops_per_jiffy * HZ * usecs) / MILLION; | ||
144 | for(i=0;i<n;i++) ; | ||
145 | } | ||
146 | |||
147 | void __const_udelay(unsigned long usecs) | ||
148 | { | ||
149 | int i, n; | ||
150 | |||
151 | n = (loops_per_jiffy * HZ * usecs) / MILLION; | ||
152 | for(i=0;i<n;i++) ; | ||
153 | } | ||
154 | |||
155 | void timer_handler(int sig, union uml_pt_regs *regs) | ||
156 | { | ||
157 | local_irq_disable(); | ||
158 | update_process_times(CHOOSE_MODE(user_context(UPT_SP(regs)), (regs)->skas.is_user)); | ||
159 | local_irq_enable(); | ||
160 | if(current_thread->cpu == 0) | ||
161 | timer_irq(regs); | ||
162 | } | ||
163 | |||
164 | static DEFINE_SPINLOCK(timer_spinlock); | ||
165 | |||
166 | unsigned long time_lock(void) | ||
167 | { | ||
168 | unsigned long flags; | ||
169 | |||
170 | spin_lock_irqsave(&timer_spinlock, flags); | ||
171 | return(flags); | ||
172 | } | ||
173 | |||
174 | void time_unlock(unsigned long flags) | ||
175 | { | ||
176 | spin_unlock_irqrestore(&timer_spinlock, flags); | ||
177 | } | ||
178 | |||
179 | int __init timer_init(void) | ||
180 | { | ||
181 | int err; | ||
182 | |||
183 | CHOOSE_MODE(user_time_init_tt(), user_time_init_skas()); | ||
184 | err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", NULL); | ||
185 | if(err != 0) | ||
186 | printk(KERN_ERR "timer_init : request_irq failed - " | ||
187 | "errno = %d\n", -err); | ||
188 | timer_irq_inited = 1; | ||
189 | return(0); | ||
190 | } | ||
191 | |||
192 | __initcall(timer_init); | ||
193 | |||
194 | /* | ||
195 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
196 | * Emacs will notice this stuff at the end of the file and automatically | ||
197 | * adjust the settings for this buffer only. This must remain at the end | ||
198 | * of the file. | ||
199 | * --------------------------------------------------------------------------- | ||
200 | * Local variables: | ||
201 | * c-file-style: "linux" | ||
202 | * End: | ||
203 | */ | ||
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c new file mode 100644 index 000000000000..eda477edfdf5 --- /dev/null +++ b/arch/um/kernel/tlb.c | |||
@@ -0,0 +1,369 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/mm.h" | ||
7 | #include "asm/page.h" | ||
8 | #include "asm/pgalloc.h" | ||
9 | #include "asm/tlbflush.h" | ||
10 | #include "choose-mode.h" | ||
11 | #include "mode_kern.h" | ||
12 | #include "user_util.h" | ||
13 | #include "tlb.h" | ||
14 | #include "mem.h" | ||
15 | #include "mem_user.h" | ||
16 | #include "os.h" | ||
17 | |||
18 | #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1)) | ||
19 | |||
20 | void fix_range_common(struct mm_struct *mm, unsigned long start_addr, | ||
21 | unsigned long end_addr, int force, int data, | ||
22 | void (*do_ops)(int, struct host_vm_op *, int)) | ||
23 | { | ||
24 | pgd_t *npgd; | ||
25 | pud_t *npud; | ||
26 | pmd_t *npmd; | ||
27 | pte_t *npte; | ||
28 | unsigned long addr, end; | ||
29 | int r, w, x; | ||
30 | struct host_vm_op ops[16]; | ||
31 | int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1; | ||
32 | |||
33 | if(mm == NULL) return; | ||
34 | |||
35 | for(addr = start_addr; addr < end_addr;){ | ||
36 | npgd = pgd_offset(mm, addr); | ||
37 | if(!pgd_present(*npgd)){ | ||
38 | end = ADD_ROUND(addr, PGDIR_SIZE); | ||
39 | if(end > end_addr) | ||
40 | end = end_addr; | ||
41 | if(force || pgd_newpage(*npgd)){ | ||
42 | op_index = add_munmap(addr, end - addr, ops, | ||
43 | op_index, last_op, data, | ||
44 | do_ops); | ||
45 | pgd_mkuptodate(*npgd); | ||
46 | } | ||
47 | addr = end; | ||
48 | continue; | ||
49 | } | ||
50 | |||
51 | npud = pud_offset(npgd, addr); | ||
52 | if(!pud_present(*npud)){ | ||
53 | end = ADD_ROUND(addr, PUD_SIZE); | ||
54 | if(end > end_addr) | ||
55 | end = end_addr; | ||
56 | if(force || pud_newpage(*npud)){ | ||
57 | op_index = add_munmap(addr, end - addr, ops, | ||
58 | op_index, last_op, data, | ||
59 | do_ops); | ||
60 | pud_mkuptodate(*npud); | ||
61 | } | ||
62 | addr = end; | ||
63 | continue; | ||
64 | } | ||
65 | |||
66 | npmd = pmd_offset(npud, addr); | ||
67 | if(!pmd_present(*npmd)){ | ||
68 | end = ADD_ROUND(addr, PMD_SIZE); | ||
69 | if(end > end_addr) | ||
70 | end = end_addr; | ||
71 | if(force || pmd_newpage(*npmd)){ | ||
72 | op_index = add_munmap(addr, end - addr, ops, | ||
73 | op_index, last_op, data, | ||
74 | do_ops); | ||
75 | pmd_mkuptodate(*npmd); | ||
76 | } | ||
77 | addr = end; | ||
78 | continue; | ||
79 | } | ||
80 | |||
81 | npte = pte_offset_kernel(npmd, addr); | ||
82 | r = pte_read(*npte); | ||
83 | w = pte_write(*npte); | ||
84 | x = pte_exec(*npte); | ||
85 | if(!pte_dirty(*npte)) | ||
86 | w = 0; | ||
87 | if(!pte_young(*npte)){ | ||
88 | r = 0; | ||
89 | w = 0; | ||
90 | } | ||
91 | if(force || pte_newpage(*npte)){ | ||
92 | if(pte_present(*npte)) | ||
93 | op_index = add_mmap(addr, | ||
94 | pte_val(*npte) & PAGE_MASK, | ||
95 | PAGE_SIZE, r, w, x, ops, | ||
96 | op_index, last_op, data, | ||
97 | do_ops); | ||
98 | else op_index = add_munmap(addr, PAGE_SIZE, ops, | ||
99 | op_index, last_op, data, | ||
100 | do_ops); | ||
101 | } | ||
102 | else if(pte_newprot(*npte)) | ||
103 | op_index = add_mprotect(addr, PAGE_SIZE, r, w, x, ops, | ||
104 | op_index, last_op, data, | ||
105 | do_ops); | ||
106 | |||
107 | *npte = pte_mkuptodate(*npte); | ||
108 | addr += PAGE_SIZE; | ||
109 | } | ||
110 | (*do_ops)(data, ops, op_index); | ||
111 | } | ||
112 | |||
113 | int flush_tlb_kernel_range_common(unsigned long start, unsigned long end) | ||
114 | { | ||
115 | struct mm_struct *mm; | ||
116 | pgd_t *pgd; | ||
117 | pud_t *pud; | ||
118 | pmd_t *pmd; | ||
119 | pte_t *pte; | ||
120 | unsigned long addr, last; | ||
121 | int updated = 0, err; | ||
122 | |||
123 | mm = &init_mm; | ||
124 | for(addr = start; addr < end;){ | ||
125 | pgd = pgd_offset(mm, addr); | ||
126 | if(!pgd_present(*pgd)){ | ||
127 | last = ADD_ROUND(addr, PGDIR_SIZE); | ||
128 | if(last > end) | ||
129 | last = end; | ||
130 | if(pgd_newpage(*pgd)){ | ||
131 | updated = 1; | ||
132 | err = os_unmap_memory((void *) addr, | ||
133 | last - addr); | ||
134 | if(err < 0) | ||
135 | panic("munmap failed, errno = %d\n", | ||
136 | -err); | ||
137 | } | ||
138 | addr = last; | ||
139 | continue; | ||
140 | } | ||
141 | |||
142 | pud = pud_offset(pgd, addr); | ||
143 | if(!pud_present(*pud)){ | ||
144 | last = ADD_ROUND(addr, PUD_SIZE); | ||
145 | if(last > end) | ||
146 | last = end; | ||
147 | if(pud_newpage(*pud)){ | ||
148 | updated = 1; | ||
149 | err = os_unmap_memory((void *) addr, | ||
150 | last - addr); | ||
151 | if(err < 0) | ||
152 | panic("munmap failed, errno = %d\n", | ||
153 | -err); | ||
154 | } | ||
155 | addr = last; | ||
156 | continue; | ||
157 | } | ||
158 | |||
159 | pmd = pmd_offset(pud, addr); | ||
160 | if(!pmd_present(*pmd)){ | ||
161 | last = ADD_ROUND(addr, PMD_SIZE); | ||
162 | if(last > end) | ||
163 | last = end; | ||
164 | if(pmd_newpage(*pmd)){ | ||
165 | updated = 1; | ||
166 | err = os_unmap_memory((void *) addr, | ||
167 | last - addr); | ||
168 | if(err < 0) | ||
169 | panic("munmap failed, errno = %d\n", | ||
170 | -err); | ||
171 | } | ||
172 | addr = last; | ||
173 | continue; | ||
174 | } | ||
175 | |||
176 | pte = pte_offset_kernel(pmd, addr); | ||
177 | if(!pte_present(*pte) || pte_newpage(*pte)){ | ||
178 | updated = 1; | ||
179 | err = os_unmap_memory((void *) addr, | ||
180 | PAGE_SIZE); | ||
181 | if(err < 0) | ||
182 | panic("munmap failed, errno = %d\n", | ||
183 | -err); | ||
184 | if(pte_present(*pte)) | ||
185 | map_memory(addr, | ||
186 | pte_val(*pte) & PAGE_MASK, | ||
187 | PAGE_SIZE, 1, 1, 1); | ||
188 | } | ||
189 | else if(pte_newprot(*pte)){ | ||
190 | updated = 1; | ||
191 | protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1); | ||
192 | } | ||
193 | addr += PAGE_SIZE; | ||
194 | } | ||
195 | return(updated); | ||
196 | } | ||
197 | |||
198 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) | ||
199 | { | ||
200 | address &= PAGE_MASK; | ||
201 | flush_tlb_range(vma, address, address + PAGE_SIZE); | ||
202 | } | ||
203 | |||
204 | void flush_tlb_all(void) | ||
205 | { | ||
206 | flush_tlb_mm(current->mm); | ||
207 | } | ||
208 | |||
209 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
210 | { | ||
211 | CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt, | ||
212 | flush_tlb_kernel_range_common, start, end); | ||
213 | } | ||
214 | |||
215 | void flush_tlb_kernel_vm(void) | ||
216 | { | ||
217 | CHOOSE_MODE(flush_tlb_kernel_vm_tt(), | ||
218 | flush_tlb_kernel_range_common(start_vm, end_vm)); | ||
219 | } | ||
220 | |||
221 | void __flush_tlb_one(unsigned long addr) | ||
222 | { | ||
223 | CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr); | ||
224 | } | ||
225 | |||
226 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
227 | unsigned long end) | ||
228 | { | ||
229 | CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start, | ||
230 | end); | ||
231 | } | ||
232 | |||
233 | void flush_tlb_mm(struct mm_struct *mm) | ||
234 | { | ||
235 | CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm); | ||
236 | } | ||
237 | |||
238 | void force_flush_all(void) | ||
239 | { | ||
240 | CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas()); | ||
241 | } | ||
242 | |||
243 | pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) | ||
244 | { | ||
245 | return(pgd_offset(mm, address)); | ||
246 | } | ||
247 | |||
248 | pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address) | ||
249 | { | ||
250 | return(pud_offset(pgd, address)); | ||
251 | } | ||
252 | |||
253 | pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address) | ||
254 | { | ||
255 | return(pmd_offset(pud, address)); | ||
256 | } | ||
257 | |||
258 | pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) | ||
259 | { | ||
260 | return(pte_offset_kernel(pmd, address)); | ||
261 | } | ||
262 | |||
263 | pte_t *addr_pte(struct task_struct *task, unsigned long addr) | ||
264 | { | ||
265 | pgd_t *pgd = pgd_offset(task->mm, addr); | ||
266 | pud_t *pud = pud_offset(pgd, addr); | ||
267 | pmd_t *pmd = pmd_offset(pud, addr); | ||
268 | |||
269 | return(pte_offset_map(pmd, addr)); | ||
270 | } | ||
271 | |||
272 | int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, | ||
273 | int r, int w, int x, struct host_vm_op *ops, int index, | ||
274 | int last_filled, int data, | ||
275 | void (*do_ops)(int, struct host_vm_op *, int)) | ||
276 | { | ||
277 | __u64 offset; | ||
278 | struct host_vm_op *last; | ||
279 | int fd; | ||
280 | |||
281 | fd = phys_mapping(phys, &offset); | ||
282 | if(index != -1){ | ||
283 | last = &ops[index]; | ||
284 | if((last->type == MMAP) && | ||
285 | (last->u.mmap.addr + last->u.mmap.len == virt) && | ||
286 | (last->u.mmap.r == r) && (last->u.mmap.w == w) && | ||
287 | (last->u.mmap.x == x) && (last->u.mmap.fd == fd) && | ||
288 | (last->u.mmap.offset + last->u.mmap.len == offset)){ | ||
289 | last->u.mmap.len += len; | ||
290 | return(index); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | if(index == last_filled){ | ||
295 | (*do_ops)(data, ops, last_filled); | ||
296 | index = -1; | ||
297 | } | ||
298 | |||
299 | ops[++index] = ((struct host_vm_op) { .type = MMAP, | ||
300 | .u = { .mmap = { | ||
301 | .addr = virt, | ||
302 | .len = len, | ||
303 | .r = r, | ||
304 | .w = w, | ||
305 | .x = x, | ||
306 | .fd = fd, | ||
307 | .offset = offset } | ||
308 | } }); | ||
309 | return(index); | ||
310 | } | ||
311 | |||
312 | int add_munmap(unsigned long addr, unsigned long len, struct host_vm_op *ops, | ||
313 | int index, int last_filled, int data, | ||
314 | void (*do_ops)(int, struct host_vm_op *, int)) | ||
315 | { | ||
316 | struct host_vm_op *last; | ||
317 | |||
318 | if(index != -1){ | ||
319 | last = &ops[index]; | ||
320 | if((last->type == MUNMAP) && | ||
321 | (last->u.munmap.addr + last->u.mmap.len == addr)){ | ||
322 | last->u.munmap.len += len; | ||
323 | return(index); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | if(index == last_filled){ | ||
328 | (*do_ops)(data, ops, last_filled); | ||
329 | index = -1; | ||
330 | } | ||
331 | |||
332 | ops[++index] = ((struct host_vm_op) { .type = MUNMAP, | ||
333 | .u = { .munmap = { | ||
334 | .addr = addr, | ||
335 | .len = len } } }); | ||
336 | return(index); | ||
337 | } | ||
338 | |||
339 | int add_mprotect(unsigned long addr, unsigned long len, int r, int w, int x, | ||
340 | struct host_vm_op *ops, int index, int last_filled, int data, | ||
341 | void (*do_ops)(int, struct host_vm_op *, int)) | ||
342 | { | ||
343 | struct host_vm_op *last; | ||
344 | |||
345 | if(index != -1){ | ||
346 | last = &ops[index]; | ||
347 | if((last->type == MPROTECT) && | ||
348 | (last->u.mprotect.addr + last->u.mprotect.len == addr) && | ||
349 | (last->u.mprotect.r == r) && (last->u.mprotect.w == w) && | ||
350 | (last->u.mprotect.x == x)){ | ||
351 | last->u.mprotect.len += len; | ||
352 | return(index); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | if(index == last_filled){ | ||
357 | (*do_ops)(data, ops, last_filled); | ||
358 | index = -1; | ||
359 | } | ||
360 | |||
361 | ops[++index] = ((struct host_vm_op) { .type = MPROTECT, | ||
362 | .u = { .mprotect = { | ||
363 | .addr = addr, | ||
364 | .len = len, | ||
365 | .r = r, | ||
366 | .w = w, | ||
367 | .x = x } } }); | ||
368 | return(index); | ||
369 | } | ||
diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c new file mode 100644 index 000000000000..47e766e6ba10 --- /dev/null +++ b/arch/um/kernel/trap_kern.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/kernel.h" | ||
7 | #include "asm/errno.h" | ||
8 | #include "linux/sched.h" | ||
9 | #include "linux/mm.h" | ||
10 | #include "linux/spinlock.h" | ||
11 | #include "linux/config.h" | ||
12 | #include "linux/init.h" | ||
13 | #include "linux/ptrace.h" | ||
14 | #include "asm/semaphore.h" | ||
15 | #include "asm/pgtable.h" | ||
16 | #include "asm/pgalloc.h" | ||
17 | #include "asm/tlbflush.h" | ||
18 | #include "asm/a.out.h" | ||
19 | #include "asm/current.h" | ||
20 | #include "asm/irq.h" | ||
21 | #include "user_util.h" | ||
22 | #include "kern_util.h" | ||
23 | #include "kern.h" | ||
24 | #include "chan_kern.h" | ||
25 | #include "mconsole_kern.h" | ||
26 | #include "2_5compat.h" | ||
27 | #include "mem.h" | ||
28 | #include "mem_kern.h" | ||
29 | |||
30 | int handle_page_fault(unsigned long address, unsigned long ip, | ||
31 | int is_write, int is_user, int *code_out) | ||
32 | { | ||
33 | struct mm_struct *mm = current->mm; | ||
34 | struct vm_area_struct *vma; | ||
35 | pgd_t *pgd; | ||
36 | pud_t *pud; | ||
37 | pmd_t *pmd; | ||
38 | pte_t *pte; | ||
39 | unsigned long page; | ||
40 | int err = -EFAULT; | ||
41 | |||
42 | *code_out = SEGV_MAPERR; | ||
43 | down_read(&mm->mmap_sem); | ||
44 | vma = find_vma(mm, address); | ||
45 | if(!vma) | ||
46 | goto out; | ||
47 | else if(vma->vm_start <= address) | ||
48 | goto good_area; | ||
49 | else if(!(vma->vm_flags & VM_GROWSDOWN)) | ||
50 | goto out; | ||
51 | else if(!ARCH_IS_STACKGROW(address)) | ||
52 | goto out; | ||
53 | else if(expand_stack(vma, address)) | ||
54 | goto out; | ||
55 | |||
56 | good_area: | ||
57 | *code_out = SEGV_ACCERR; | ||
58 | if(is_write && !(vma->vm_flags & VM_WRITE)) | ||
59 | goto out; | ||
60 | page = address & PAGE_MASK; | ||
61 | pgd = pgd_offset(mm, page); | ||
62 | pud = pud_offset(pgd, page); | ||
63 | pmd = pmd_offset(pud, page); | ||
64 | do { | ||
65 | survive: | ||
66 | switch (handle_mm_fault(mm, vma, address, is_write)){ | ||
67 | case VM_FAULT_MINOR: | ||
68 | current->min_flt++; | ||
69 | break; | ||
70 | case VM_FAULT_MAJOR: | ||
71 | current->maj_flt++; | ||
72 | break; | ||
73 | case VM_FAULT_SIGBUS: | ||
74 | err = -EACCES; | ||
75 | goto out; | ||
76 | case VM_FAULT_OOM: | ||
77 | err = -ENOMEM; | ||
78 | goto out_of_memory; | ||
79 | default: | ||
80 | BUG(); | ||
81 | } | ||
82 | pgd = pgd_offset(mm, page); | ||
83 | pud = pud_offset(pgd, page); | ||
84 | pmd = pmd_offset(pud, page); | ||
85 | pte = pte_offset_kernel(pmd, page); | ||
86 | } while(!pte_present(*pte)); | ||
87 | err = 0; | ||
88 | *pte = pte_mkyoung(*pte); | ||
89 | if(pte_write(*pte)) *pte = pte_mkdirty(*pte); | ||
90 | flush_tlb_page(vma, page); | ||
91 | out: | ||
92 | up_read(&mm->mmap_sem); | ||
93 | return(err); | ||
94 | |||
95 | /* | ||
96 | * We ran out of memory, or some other thing happened to us that made | ||
97 | * us unable to handle the page fault gracefully. | ||
98 | */ | ||
99 | out_of_memory: | ||
100 | if (current->pid == 1) { | ||
101 | up_read(&mm->mmap_sem); | ||
102 | yield(); | ||
103 | down_read(&mm->mmap_sem); | ||
104 | goto survive; | ||
105 | } | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | LIST_HEAD(physmem_remappers); | ||
110 | |||
111 | void register_remapper(struct remapper *info) | ||
112 | { | ||
113 | list_add(&info->list, &physmem_remappers); | ||
114 | } | ||
115 | |||
116 | static int check_remapped_addr(unsigned long address, int is_write) | ||
117 | { | ||
118 | struct remapper *remapper; | ||
119 | struct list_head *ele; | ||
120 | __u64 offset; | ||
121 | int fd; | ||
122 | |||
123 | fd = phys_mapping(__pa(address), &offset); | ||
124 | if(fd == -1) | ||
125 | return(0); | ||
126 | |||
127 | list_for_each(ele, &physmem_remappers){ | ||
128 | remapper = list_entry(ele, struct remapper, list); | ||
129 | if((*remapper->proc)(fd, address, is_write, offset)) | ||
130 | return(1); | ||
131 | } | ||
132 | |||
133 | return(0); | ||
134 | } | ||
135 | |||
136 | unsigned long segv(unsigned long address, unsigned long ip, int is_write, | ||
137 | int is_user, void *sc) | ||
138 | { | ||
139 | struct siginfo si; | ||
140 | void *catcher; | ||
141 | int err; | ||
142 | |||
143 | if(!is_user && (address >= start_vm) && (address < end_vm)){ | ||
144 | flush_tlb_kernel_vm(); | ||
145 | return(0); | ||
146 | } | ||
147 | else if(check_remapped_addr(address & PAGE_MASK, is_write)) | ||
148 | return(0); | ||
149 | else if(current->mm == NULL) | ||
150 | panic("Segfault with no mm"); | ||
151 | err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); | ||
152 | |||
153 | catcher = current->thread.fault_catcher; | ||
154 | if(!err) | ||
155 | return(0); | ||
156 | else if(catcher != NULL){ | ||
157 | current->thread.fault_addr = (void *) address; | ||
158 | do_longjmp(catcher, 1); | ||
159 | } | ||
160 | else if(current->thread.fault_addr != NULL) | ||
161 | panic("fault_addr set but no fault catcher"); | ||
162 | else if(arch_fixup(ip, sc)) | ||
163 | return(0); | ||
164 | |||
165 | if(!is_user) | ||
166 | panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", | ||
167 | address, ip); | ||
168 | |||
169 | if(err == -EACCES){ | ||
170 | si.si_signo = SIGBUS; | ||
171 | si.si_errno = 0; | ||
172 | si.si_code = BUS_ADRERR; | ||
173 | si.si_addr = (void *)address; | ||
174 | force_sig_info(SIGBUS, &si, current); | ||
175 | } | ||
176 | else if(err == -ENOMEM){ | ||
177 | printk("VM: killing process %s\n", current->comm); | ||
178 | do_exit(SIGKILL); | ||
179 | } | ||
180 | else { | ||
181 | si.si_signo = SIGSEGV; | ||
182 | si.si_addr = (void *) address; | ||
183 | current->thread.cr2 = address; | ||
184 | current->thread.err = is_write; | ||
185 | force_sig_info(SIGSEGV, &si, current); | ||
186 | } | ||
187 | return(0); | ||
188 | } | ||
189 | |||
190 | void bad_segv(unsigned long address, unsigned long ip, int is_write) | ||
191 | { | ||
192 | struct siginfo si; | ||
193 | |||
194 | si.si_signo = SIGSEGV; | ||
195 | si.si_code = SEGV_ACCERR; | ||
196 | si.si_addr = (void *) address; | ||
197 | current->thread.cr2 = address; | ||
198 | current->thread.err = is_write; | ||
199 | force_sig_info(SIGSEGV, &si, current); | ||
200 | } | ||
201 | |||
202 | void relay_signal(int sig, union uml_pt_regs *regs) | ||
203 | { | ||
204 | if(arch_handle_signal(sig, regs)) return; | ||
205 | if(!UPT_IS_USER(regs)) | ||
206 | panic("Kernel mode signal %d", sig); | ||
207 | force_sig(sig, current); | ||
208 | } | ||
209 | |||
210 | void bus_handler(int sig, union uml_pt_regs *regs) | ||
211 | { | ||
212 | if(current->thread.fault_catcher != NULL) | ||
213 | do_longjmp(current->thread.fault_catcher, 1); | ||
214 | else relay_signal(sig, regs); | ||
215 | } | ||
216 | |||
217 | void winch(int sig, union uml_pt_regs *regs) | ||
218 | { | ||
219 | do_IRQ(WINCH_IRQ, regs); | ||
220 | } | ||
221 | |||
222 | void trap_init(void) | ||
223 | { | ||
224 | } | ||
225 | |||
226 | DEFINE_SPINLOCK(trap_lock); | ||
227 | |||
228 | static int trap_index = 0; | ||
229 | |||
230 | int next_trap_index(int limit) | ||
231 | { | ||
232 | int ret; | ||
233 | |||
234 | spin_lock(&trap_lock); | ||
235 | ret = trap_index; | ||
236 | if(++trap_index == limit) | ||
237 | trap_index = 0; | ||
238 | spin_unlock(&trap_lock); | ||
239 | return(ret); | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
244 | * Emacs will notice this stuff at the end of the file and automatically | ||
245 | * adjust the settings for this buffer only. This must remain at the end | ||
246 | * of the file. | ||
247 | * --------------------------------------------------------------------------- | ||
248 | * Local variables: | ||
249 | * c-file-style: "linux" | ||
250 | * End: | ||
251 | */ | ||
diff --git a/arch/um/kernel/trap_user.c b/arch/um/kernel/trap_user.c new file mode 100644 index 000000000000..50a4042a509f --- /dev/null +++ b/arch/um/kernel/trap_user.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <errno.h> | ||
8 | #include <setjmp.h> | ||
9 | #include <signal.h> | ||
10 | #include <sys/time.h> | ||
11 | #include <sys/wait.h> | ||
12 | #include <asm/page.h> | ||
13 | #include <asm/unistd.h> | ||
14 | #include <asm/ptrace.h> | ||
15 | #include "init.h" | ||
16 | #include "sysdep/ptrace.h" | ||
17 | #include "sigcontext.h" | ||
18 | #include "sysdep/sigcontext.h" | ||
19 | #include "irq_user.h" | ||
20 | #include "signal_user.h" | ||
21 | #include "time_user.h" | ||
22 | #include "task.h" | ||
23 | #include "mode.h" | ||
24 | #include "choose-mode.h" | ||
25 | #include "kern_util.h" | ||
26 | #include "user_util.h" | ||
27 | #include "os.h" | ||
28 | |||
29 | void kill_child_dead(int pid) | ||
30 | { | ||
31 | kill(pid, SIGKILL); | ||
32 | kill(pid, SIGCONT); | ||
33 | do { | ||
34 | int n; | ||
35 | CATCH_EINTR(n = waitpid(pid, NULL, 0)); | ||
36 | if (n > 0) | ||
37 | kill(pid, SIGCONT); | ||
38 | else | ||
39 | break; | ||
40 | } while(1); | ||
41 | } | ||
42 | |||
43 | /* Unlocked - don't care if this is a bit off */ | ||
44 | int nsegfaults = 0; | ||
45 | |||
46 | struct { | ||
47 | unsigned long address; | ||
48 | int is_write; | ||
49 | int pid; | ||
50 | unsigned long sp; | ||
51 | int is_user; | ||
52 | } segfault_record[1024]; | ||
53 | |||
54 | void segv_handler(int sig, union uml_pt_regs *regs) | ||
55 | { | ||
56 | int index, max; | ||
57 | |||
58 | if(UPT_IS_USER(regs) && !UPT_SEGV_IS_FIXABLE(regs)){ | ||
59 | bad_segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), | ||
60 | UPT_FAULT_WRITE(regs)); | ||
61 | return; | ||
62 | } | ||
63 | max = sizeof(segfault_record)/sizeof(segfault_record[0]); | ||
64 | index = next_trap_index(max); | ||
65 | |||
66 | nsegfaults++; | ||
67 | segfault_record[index].address = UPT_FAULT_ADDR(regs); | ||
68 | segfault_record[index].pid = os_getpid(); | ||
69 | segfault_record[index].is_write = UPT_FAULT_WRITE(regs); | ||
70 | segfault_record[index].sp = UPT_SP(regs); | ||
71 | segfault_record[index].is_user = UPT_IS_USER(regs); | ||
72 | segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), UPT_FAULT_WRITE(regs), | ||
73 | UPT_IS_USER(regs), regs); | ||
74 | } | ||
75 | |||
76 | void usr2_handler(int sig, union uml_pt_regs *regs) | ||
77 | { | ||
78 | CHOOSE_MODE(syscall_handler_tt(sig, regs), (void) 0); | ||
79 | } | ||
80 | |||
81 | struct signal_info sig_info[] = { | ||
82 | [ SIGTRAP ] { .handler = relay_signal, | ||
83 | .is_irq = 0 }, | ||
84 | [ SIGFPE ] { .handler = relay_signal, | ||
85 | .is_irq = 0 }, | ||
86 | [ SIGILL ] { .handler = relay_signal, | ||
87 | .is_irq = 0 }, | ||
88 | [ SIGWINCH ] { .handler = winch, | ||
89 | .is_irq = 1 }, | ||
90 | [ SIGBUS ] { .handler = bus_handler, | ||
91 | .is_irq = 0 }, | ||
92 | [ SIGSEGV] { .handler = segv_handler, | ||
93 | .is_irq = 0 }, | ||
94 | [ SIGIO ] { .handler = sigio_handler, | ||
95 | .is_irq = 1 }, | ||
96 | [ SIGVTALRM ] { .handler = timer_handler, | ||
97 | .is_irq = 1 }, | ||
98 | [ SIGALRM ] { .handler = timer_handler, | ||
99 | .is_irq = 1 }, | ||
100 | [ SIGUSR2 ] { .handler = usr2_handler, | ||
101 | .is_irq = 0 }, | ||
102 | }; | ||
103 | |||
104 | void do_longjmp(void *b, int val) | ||
105 | { | ||
106 | sigjmp_buf *buf = b; | ||
107 | |||
108 | siglongjmp(*buf, val); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
113 | * Emacs will notice this stuff at the end of the file and automatically | ||
114 | * adjust the settings for this buffer only. This must remain at the end | ||
115 | * of the file. | ||
116 | * --------------------------------------------------------------------------- | ||
117 | * Local variables: | ||
118 | * c-file-style: "linux" | ||
119 | * End: | ||
120 | */ | ||
diff --git a/arch/um/kernel/tt/Makefile b/arch/um/kernel/tt/Makefile new file mode 100644 index 000000000000..3d5177df3504 --- /dev/null +++ b/arch/um/kernel/tt/Makefile | |||
@@ -0,0 +1,28 @@ | |||
1 | # | ||
2 | # Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | extra-y := unmap_fin.o | ||
7 | clean-files := unmap_tmp.o | ||
8 | |||
9 | obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \ | ||
10 | syscall_kern.o syscall_user.o time.o tlb.o tracer.o trap_user.o \ | ||
11 | uaccess.o uaccess_user.o | ||
12 | |||
13 | obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/ | ||
14 | |||
15 | USER_OBJS := gdb.o time.o tracer.o | ||
16 | |||
17 | include arch/um/scripts/Makefile.rules | ||
18 | |||
19 | UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS)) | ||
20 | UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS)) | ||
21 | |||
22 | #XXX: partially copied from arch/um/scripts/Makefile.rules | ||
23 | $(obj)/unmap.o: c_flags = -Wp,-MD,$(depfile) $(UNMAP_CFLAGS) | ||
24 | |||
25 | $(obj)/unmap_fin.o : $(obj)/unmap.o | ||
26 | $(LD) -r -o $(obj)/unmap_tmp.o $< $(shell $(CC) -print-file-name=libc.a) | ||
27 | $(OBJCOPY) $(obj)/unmap_tmp.o $@ -G switcheroo | ||
28 | |||
diff --git a/arch/um/kernel/tt/exec_kern.c b/arch/um/kernel/tt/exec_kern.c new file mode 100644 index 000000000000..065b504a653b --- /dev/null +++ b/arch/um/kernel/tt/exec_kern.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/kernel.h" | ||
7 | #include "linux/mm.h" | ||
8 | #include "asm/signal.h" | ||
9 | #include "asm/ptrace.h" | ||
10 | #include "asm/uaccess.h" | ||
11 | #include "asm/pgalloc.h" | ||
12 | #include "asm/tlbflush.h" | ||
13 | #include "user_util.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "irq_user.h" | ||
16 | #include "time_user.h" | ||
17 | #include "signal_user.h" | ||
18 | #include "mem_user.h" | ||
19 | #include "os.h" | ||
20 | #include "tlb.h" | ||
21 | #include "mode.h" | ||
22 | |||
23 | static int exec_tramp(void *sig_stack) | ||
24 | { | ||
25 | init_new_thread_stack(sig_stack, NULL); | ||
26 | init_new_thread_signals(1); | ||
27 | os_stop_process(os_getpid()); | ||
28 | return(0); | ||
29 | } | ||
30 | |||
31 | void flush_thread_tt(void) | ||
32 | { | ||
33 | unsigned long stack; | ||
34 | int new_pid; | ||
35 | |||
36 | stack = alloc_stack(0, 0); | ||
37 | if(stack == 0){ | ||
38 | printk(KERN_ERR | ||
39 | "flush_thread : failed to allocate temporary stack\n"); | ||
40 | do_exit(SIGKILL); | ||
41 | } | ||
42 | |||
43 | new_pid = start_fork_tramp(current->thread_info, stack, 0, exec_tramp); | ||
44 | if(new_pid < 0){ | ||
45 | printk(KERN_ERR | ||
46 | "flush_thread : new thread failed, errno = %d\n", | ||
47 | -new_pid); | ||
48 | do_exit(SIGKILL); | ||
49 | } | ||
50 | |||
51 | if(current_thread->cpu == 0) | ||
52 | forward_interrupts(new_pid); | ||
53 | current->thread.request.op = OP_EXEC; | ||
54 | current->thread.request.u.exec.pid = new_pid; | ||
55 | unprotect_stack((unsigned long) current_thread); | ||
56 | os_usr1_process(os_getpid()); | ||
57 | change_sig(SIGUSR1, 1); | ||
58 | |||
59 | change_sig(SIGUSR1, 0); | ||
60 | enable_timer(); | ||
61 | free_page(stack); | ||
62 | protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); | ||
63 | task_protections((unsigned long) current_thread); | ||
64 | force_flush_all(); | ||
65 | unblock_signals(); | ||
66 | } | ||
67 | |||
68 | void start_thread_tt(struct pt_regs *regs, unsigned long eip, | ||
69 | unsigned long esp) | ||
70 | { | ||
71 | set_fs(USER_DS); | ||
72 | flush_tlb_mm(current->mm); | ||
73 | PT_REGS_IP(regs) = eip; | ||
74 | PT_REGS_SP(regs) = esp; | ||
75 | PT_FIX_EXEC_STACK(esp); | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
80 | * Emacs will notice this stuff at the end of the file and automatically | ||
81 | * adjust the settings for this buffer only. This must remain at the end | ||
82 | * of the file. | ||
83 | * --------------------------------------------------------------------------- | ||
84 | * Local variables: | ||
85 | * c-file-style: "linux" | ||
86 | * End: | ||
87 | */ | ||
diff --git a/arch/um/kernel/tt/exec_user.c b/arch/um/kernel/tt/exec_user.c new file mode 100644 index 000000000000..a92c02ff2ce3 --- /dev/null +++ b/arch/um/kernel/tt/exec_user.c | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <sched.h> | ||
10 | #include <errno.h> | ||
11 | #include <sys/wait.h> | ||
12 | #include <signal.h> | ||
13 | #include "user_util.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "user.h" | ||
16 | #include "ptrace_user.h" | ||
17 | #include "os.h" | ||
18 | |||
19 | void do_exec(int old_pid, int new_pid) | ||
20 | { | ||
21 | unsigned long regs[FRAME_SIZE]; | ||
22 | int err; | ||
23 | |||
24 | if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || | ||
25 | (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) | ||
26 | tracer_panic("do_exec failed to attach proc - errno = %d", | ||
27 | errno); | ||
28 | |||
29 | CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED)); | ||
30 | if (err < 0) | ||
31 | tracer_panic("do_exec failed to attach proc in waitpid - errno = %d", | ||
32 | errno); | ||
33 | |||
34 | if(ptrace_getregs(old_pid, regs) < 0) | ||
35 | tracer_panic("do_exec failed to get registers - errno = %d", | ||
36 | errno); | ||
37 | |||
38 | os_kill_ptraced_process(old_pid, 0); | ||
39 | |||
40 | if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) | ||
41 | tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno); | ||
42 | |||
43 | if(ptrace_setregs(new_pid, regs) < 0) | ||
44 | tracer_panic("do_exec failed to start new proc - errno = %d", | ||
45 | errno); | ||
46 | } | ||
47 | |||
48 | /* | ||
49 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
50 | * Emacs will notice this stuff at the end of the file and automatically | ||
51 | * adjust the settings for this buffer only. This must remain at the end | ||
52 | * of the file. | ||
53 | * --------------------------------------------------------------------------- | ||
54 | * Local variables: | ||
55 | * c-file-style: "linux" | ||
56 | * End: | ||
57 | */ | ||
diff --git a/arch/um/kernel/tt/gdb.c b/arch/um/kernel/tt/gdb.c new file mode 100644 index 000000000000..19a0ad7b35b3 --- /dev/null +++ b/arch/um/kernel/tt/gdb.c | |||
@@ -0,0 +1,278 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <errno.h> | ||
9 | #include <string.h> | ||
10 | #include <signal.h> | ||
11 | #include <sys/types.h> | ||
12 | #include "ptrace_user.h" | ||
13 | #include "uml-config.h" | ||
14 | #include "kern_constants.h" | ||
15 | #include "chan_user.h" | ||
16 | #include "init.h" | ||
17 | #include "user.h" | ||
18 | #include "debug.h" | ||
19 | #include "kern_util.h" | ||
20 | #include "user_util.h" | ||
21 | #include "tt.h" | ||
22 | #include "sysdep/thread.h" | ||
23 | |||
24 | extern int debugger_pid; | ||
25 | extern int debugger_fd; | ||
26 | extern int debugger_parent; | ||
27 | |||
28 | int detach(int pid, int sig) | ||
29 | { | ||
30 | return(ptrace(PTRACE_DETACH, pid, 0, sig)); | ||
31 | } | ||
32 | |||
33 | int attach(int pid) | ||
34 | { | ||
35 | int err; | ||
36 | |||
37 | err = ptrace(PTRACE_ATTACH, pid, 0, 0); | ||
38 | if(err < 0) return(-errno); | ||
39 | else return(err); | ||
40 | } | ||
41 | |||
42 | int cont(int pid) | ||
43 | { | ||
44 | return(ptrace(PTRACE_CONT, pid, 0, 0)); | ||
45 | } | ||
46 | |||
47 | #ifdef UML_CONFIG_PT_PROXY | ||
48 | |||
49 | int debugger_signal(int status, pid_t pid) | ||
50 | { | ||
51 | return(debugger_proxy(status, pid)); | ||
52 | } | ||
53 | |||
54 | void child_signal(pid_t pid, int status) | ||
55 | { | ||
56 | child_proxy(pid, status); | ||
57 | } | ||
58 | |||
59 | static void gdb_announce(char *dev_name, int dev) | ||
60 | { | ||
61 | printf("gdb assigned device '%s'\n", dev_name); | ||
62 | } | ||
63 | |||
64 | static struct chan_opts opts = { | ||
65 | .announce = gdb_announce, | ||
66 | .xterm_title = "UML kernel debugger", | ||
67 | .raw = 0, | ||
68 | .tramp_stack = 0, | ||
69 | .in_kernel = 0, | ||
70 | }; | ||
71 | |||
72 | /* Accessed by the tracing thread, which automatically serializes access */ | ||
73 | static void *xterm_data; | ||
74 | static int xterm_fd; | ||
75 | |||
76 | extern void *xterm_init(char *, int, struct chan_opts *); | ||
77 | extern int xterm_open(int, int, int, void *, char **); | ||
78 | extern void xterm_close(int, void *); | ||
79 | |||
80 | int open_gdb_chan(void) | ||
81 | { | ||
82 | char stack[UM_KERN_PAGE_SIZE], *dummy; | ||
83 | |||
84 | opts.tramp_stack = (unsigned long) stack; | ||
85 | xterm_data = xterm_init("", 0, &opts); | ||
86 | xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy); | ||
87 | return(xterm_fd); | ||
88 | } | ||
89 | |||
90 | static void exit_debugger_cb(void *unused) | ||
91 | { | ||
92 | if(debugger_pid != -1){ | ||
93 | if(gdb_pid != -1){ | ||
94 | fake_child_exit(); | ||
95 | gdb_pid = -1; | ||
96 | } | ||
97 | else kill_child_dead(debugger_pid); | ||
98 | debugger_pid = -1; | ||
99 | if(debugger_parent != -1) | ||
100 | detach(debugger_parent, SIGINT); | ||
101 | } | ||
102 | if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data); | ||
103 | } | ||
104 | |||
105 | static void exit_debugger(void) | ||
106 | { | ||
107 | initial_thread_cb(exit_debugger_cb, NULL); | ||
108 | } | ||
109 | |||
110 | __uml_exitcall(exit_debugger); | ||
111 | |||
112 | struct gdb_data { | ||
113 | char *str; | ||
114 | int err; | ||
115 | }; | ||
116 | |||
117 | static void config_gdb_cb(void *arg) | ||
118 | { | ||
119 | struct gdb_data *data = arg; | ||
120 | void *task; | ||
121 | int pid; | ||
122 | |||
123 | data->err = -1; | ||
124 | if(debugger_pid != -1) exit_debugger_cb(NULL); | ||
125 | if(!strncmp(data->str, "pid,", strlen("pid,"))){ | ||
126 | data->str += strlen("pid,"); | ||
127 | pid = strtoul(data->str, NULL, 0); | ||
128 | task = cpu_tasks[0].task; | ||
129 | debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0); | ||
130 | if(debugger_pid != -1){ | ||
131 | data->err = 0; | ||
132 | gdb_pid = pid; | ||
133 | } | ||
134 | return; | ||
135 | } | ||
136 | data->err = 0; | ||
137 | debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); | ||
138 | init_proxy(debugger_pid, 0, 0); | ||
139 | } | ||
140 | |||
141 | int gdb_config(char *str) | ||
142 | { | ||
143 | struct gdb_data data; | ||
144 | |||
145 | if(*str++ != '=') return(-1); | ||
146 | data.str = str; | ||
147 | initial_thread_cb(config_gdb_cb, &data); | ||
148 | return(data.err); | ||
149 | } | ||
150 | |||
151 | void remove_gdb_cb(void *unused) | ||
152 | { | ||
153 | exit_debugger_cb(NULL); | ||
154 | } | ||
155 | |||
156 | int gdb_remove(char *unused) | ||
157 | { | ||
158 | initial_thread_cb(remove_gdb_cb, NULL); | ||
159 | return(0); | ||
160 | } | ||
161 | |||
162 | void signal_usr1(int sig) | ||
163 | { | ||
164 | if(debugger_pid != -1){ | ||
165 | printf("The debugger is already running\n"); | ||
166 | return; | ||
167 | } | ||
168 | debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); | ||
169 | init_proxy(debugger_pid, 0, 0); | ||
170 | } | ||
171 | |||
172 | int init_ptrace_proxy(int idle_pid, int startup, int stop) | ||
173 | { | ||
174 | int pid, status; | ||
175 | |||
176 | pid = start_debugger(linux_prog, startup, stop, &debugger_fd); | ||
177 | status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); | ||
178 | if(pid < 0){ | ||
179 | cont(idle_pid); | ||
180 | return(-1); | ||
181 | } | ||
182 | init_proxy(pid, 1, status); | ||
183 | return(pid); | ||
184 | } | ||
185 | |||
186 | int attach_debugger(int idle_pid, int pid, int stop) | ||
187 | { | ||
188 | int status = 0, err; | ||
189 | |||
190 | err = attach(pid); | ||
191 | if(err < 0){ | ||
192 | printf("Failed to attach pid %d, errno = %d\n", pid, -err); | ||
193 | return(-1); | ||
194 | } | ||
195 | if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); | ||
196 | init_proxy(pid, 1, status); | ||
197 | return(pid); | ||
198 | } | ||
199 | |||
200 | #ifdef notdef /* Put this back in when it does something useful */ | ||
201 | static int __init uml_gdb_init_setup(char *line, int *add) | ||
202 | { | ||
203 | gdb_init = uml_strdup(line); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | __uml_setup("gdb=", uml_gdb_init_setup, | ||
208 | "gdb=<channel description>\n\n" | ||
209 | ); | ||
210 | #endif | ||
211 | |||
212 | static int __init uml_gdb_pid_setup(char *line, int *add) | ||
213 | { | ||
214 | gdb_pid = strtoul(line, NULL, 0); | ||
215 | *add = 0; | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | __uml_setup("gdb-pid=", uml_gdb_pid_setup, | ||
220 | "gdb-pid=<pid>\n" | ||
221 | " gdb-pid is used to attach an external debugger to UML. This may be\n" | ||
222 | " an already-running gdb or a debugger-like process like strace.\n\n" | ||
223 | ); | ||
224 | |||
225 | #else | ||
226 | |||
227 | int debugger_signal(int status, pid_t pid){ return(0); } | ||
228 | void child_signal(pid_t pid, int status){ } | ||
229 | int init_ptrace_proxy(int idle_pid, int startup, int stop) | ||
230 | { | ||
231 | printf("debug requested when CONFIG_PT_PROXY is off\n"); | ||
232 | kill_child_dead(idle_pid); | ||
233 | exit(1); | ||
234 | } | ||
235 | |||
236 | void signal_usr1(int sig) | ||
237 | { | ||
238 | printf("debug requested when CONFIG_PT_PROXY is off\n"); | ||
239 | } | ||
240 | |||
241 | int attach_debugger(int idle_pid, int pid, int stop) | ||
242 | { | ||
243 | printf("attach_debugger called when CONFIG_PT_PROXY " | ||
244 | "is off\n"); | ||
245 | return(-1); | ||
246 | } | ||
247 | |||
248 | int config_gdb(char *str) | ||
249 | { | ||
250 | return(-1); | ||
251 | } | ||
252 | |||
253 | int remove_gdb(void) | ||
254 | { | ||
255 | return(-1); | ||
256 | } | ||
257 | |||
258 | int init_parent_proxy(int pid) | ||
259 | { | ||
260 | return(-1); | ||
261 | } | ||
262 | |||
263 | void debugger_parent_signal(int status, int pid) | ||
264 | { | ||
265 | } | ||
266 | |||
267 | #endif | ||
268 | |||
269 | /* | ||
270 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
271 | * Emacs will notice this stuff at the end of the file and automatically | ||
272 | * adjust the settings for this buffer only. This must remain at the end | ||
273 | * of the file. | ||
274 | * --------------------------------------------------------------------------- | ||
275 | * Local variables: | ||
276 | * c-file-style: "linux" | ||
277 | * End: | ||
278 | */ | ||
diff --git a/arch/um/kernel/tt/gdb_kern.c b/arch/um/kernel/tt/gdb_kern.c new file mode 100644 index 000000000000..93fb121f86af --- /dev/null +++ b/arch/um/kernel/tt/gdb_kern.c | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/init.h" | ||
7 | #include "linux/config.h" | ||
8 | #include "mconsole_kern.h" | ||
9 | |||
10 | #ifdef CONFIG_MCONSOLE | ||
11 | |||
12 | extern int gdb_config(char *str); | ||
13 | extern int gdb_remove(char *unused); | ||
14 | |||
15 | static struct mc_device gdb_mc = { | ||
16 | .name = "gdb", | ||
17 | .config = gdb_config, | ||
18 | .remove = gdb_remove, | ||
19 | }; | ||
20 | |||
21 | int gdb_mc_init(void) | ||
22 | { | ||
23 | mconsole_register_dev(&gdb_mc); | ||
24 | return(0); | ||
25 | } | ||
26 | |||
27 | __initcall(gdb_mc_init); | ||
28 | |||
29 | #endif | ||
30 | |||
31 | /* | ||
32 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
33 | * Emacs will notice this stuff at the end of the file and automatically | ||
34 | * adjust the settings for this buffer only. This must remain at the end | ||
35 | * of the file. | ||
36 | * --------------------------------------------------------------------------- | ||
37 | * Local variables: | ||
38 | * c-file-style: "linux" | ||
39 | * End: | ||
40 | */ | ||
diff --git a/arch/um/kernel/tt/include/debug.h b/arch/um/kernel/tt/include/debug.h new file mode 100644 index 000000000000..8eff674107ca --- /dev/null +++ b/arch/um/kernel/tt/include/debug.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) and | ||
3 | * Lars Brinkhoff. | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #ifndef __DEBUG_H | ||
8 | #define __DEBUG_H | ||
9 | |||
10 | extern int debugger_proxy(int status, pid_t pid); | ||
11 | extern void child_proxy(pid_t pid, int status); | ||
12 | extern void init_proxy (pid_t pid, int waiting, int status); | ||
13 | extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd); | ||
14 | extern void fake_child_exit(void); | ||
15 | extern int gdb_config(char *str); | ||
16 | extern int gdb_remove(char *unused); | ||
17 | |||
18 | #endif | ||
19 | |||
20 | /* | ||
21 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
22 | * Emacs will notice this stuff at the end of the file and automatically | ||
23 | * adjust the settings for this buffer only. This must remain at the end | ||
24 | * of the file. | ||
25 | * --------------------------------------------------------------------------- | ||
26 | * Local variables: | ||
27 | * c-file-style: "linux" | ||
28 | * End: | ||
29 | */ | ||
diff --git a/arch/um/kernel/tt/include/mmu-tt.h b/arch/um/kernel/tt/include/mmu-tt.h new file mode 100644 index 000000000000..0440510ab3fe --- /dev/null +++ b/arch/um/kernel/tt/include/mmu-tt.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __TT_MMU_H | ||
7 | #define __TT_MMU_H | ||
8 | |||
9 | struct mmu_context_tt { | ||
10 | }; | ||
11 | |||
12 | #endif | ||
13 | |||
14 | /* | ||
15 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
16 | * Emacs will notice this stuff at the end of the file and automatically | ||
17 | * adjust the settings for this buffer only. This must remain at the end | ||
18 | * of the file. | ||
19 | * --------------------------------------------------------------------------- | ||
20 | * Local variables: | ||
21 | * c-file-style: "linux" | ||
22 | * End: | ||
23 | */ | ||
diff --git a/arch/um/kernel/tt/include/mode-tt.h b/arch/um/kernel/tt/include/mode-tt.h new file mode 100644 index 000000000000..efe462019069 --- /dev/null +++ b/arch/um/kernel/tt/include/mode-tt.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __MODE_TT_H__ | ||
7 | #define __MODE_TT_H__ | ||
8 | |||
9 | #include "sysdep/ptrace.h" | ||
10 | |||
11 | enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB }; | ||
12 | |||
13 | extern int tracing_pid; | ||
14 | |||
15 | extern int tracer(int (*init_proc)(void *), void *sp); | ||
16 | extern void user_time_init_tt(void); | ||
17 | extern void sig_handler_common_tt(int sig, void *sc); | ||
18 | extern void syscall_handler_tt(int sig, union uml_pt_regs *regs); | ||
19 | extern void reboot_tt(void); | ||
20 | extern void halt_tt(void); | ||
21 | extern int is_tracer_winch(int pid, int fd, void *data); | ||
22 | extern void kill_off_processes_tt(void); | ||
23 | |||
24 | #endif | ||
25 | |||
26 | /* | ||
27 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
28 | * Emacs will notice this stuff at the end of the file and automatically | ||
29 | * adjust the settings for this buffer only. This must remain at the end | ||
30 | * of the file. | ||
31 | * --------------------------------------------------------------------------- | ||
32 | * Local variables: | ||
33 | * c-file-style: "linux" | ||
34 | * End: | ||
35 | */ | ||
diff --git a/arch/um/kernel/tt/include/mode_kern-tt.h b/arch/um/kernel/tt/include/mode_kern-tt.h new file mode 100644 index 000000000000..28aaab3448fa --- /dev/null +++ b/arch/um/kernel/tt/include/mode_kern-tt.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __TT_MODE_KERN_H__ | ||
7 | #define __TT_MODE_KERN_H__ | ||
8 | |||
9 | #include "linux/sched.h" | ||
10 | #include "asm/page.h" | ||
11 | #include "asm/ptrace.h" | ||
12 | #include "asm/uaccess.h" | ||
13 | |||
14 | extern void *switch_to_tt(void *prev, void *next); | ||
15 | extern void flush_thread_tt(void); | ||
16 | extern void start_thread_tt(struct pt_regs *regs, unsigned long eip, | ||
17 | unsigned long esp); | ||
18 | extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp, | ||
19 | unsigned long stack_top, struct task_struct *p, | ||
20 | struct pt_regs *regs); | ||
21 | extern void release_thread_tt(struct task_struct *task); | ||
22 | extern void exit_thread_tt(void); | ||
23 | extern void initial_thread_cb_tt(void (*proc)(void *), void *arg); | ||
24 | extern void init_idle_tt(void); | ||
25 | extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end); | ||
26 | extern void flush_tlb_kernel_vm_tt(void); | ||
27 | extern void __flush_tlb_one_tt(unsigned long addr); | ||
28 | extern void flush_tlb_range_tt(struct vm_area_struct *vma, | ||
29 | unsigned long start, unsigned long end); | ||
30 | extern void flush_tlb_mm_tt(struct mm_struct *mm); | ||
31 | extern void force_flush_all_tt(void); | ||
32 | extern long execute_syscall_tt(void *r); | ||
33 | extern void before_mem_tt(unsigned long brk_start); | ||
34 | extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out, | ||
35 | unsigned long *task_size_out); | ||
36 | extern int start_uml_tt(void); | ||
37 | extern int external_pid_tt(struct task_struct *task); | ||
38 | extern int thread_pid_tt(struct task_struct *task); | ||
39 | |||
40 | #define kmem_end_tt (host_task_size - ABOVE_KMEM) | ||
41 | |||
42 | #endif | ||
43 | |||
44 | /* | ||
45 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
46 | * Emacs will notice this stuff at the end of the file and automatically | ||
47 | * adjust the settings for this buffer only. This must remain at the end | ||
48 | * of the file. | ||
49 | * --------------------------------------------------------------------------- | ||
50 | * Local variables: | ||
51 | * c-file-style: "linux" | ||
52 | * End: | ||
53 | */ | ||
diff --git a/arch/um/kernel/tt/include/tt.h b/arch/um/kernel/tt/include/tt.h new file mode 100644 index 000000000000..c667b67af405 --- /dev/null +++ b/arch/um/kernel/tt/include/tt.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __TT_H__ | ||
7 | #define __TT_H__ | ||
8 | |||
9 | #include "sysdep/ptrace.h" | ||
10 | |||
11 | extern int gdb_pid; | ||
12 | extern int debug; | ||
13 | extern int debug_stop; | ||
14 | extern int debug_trace; | ||
15 | |||
16 | extern int honeypot; | ||
17 | |||
18 | extern int fork_tramp(void *sig_stack); | ||
19 | extern int do_proc_op(void *t, int proc_id); | ||
20 | extern int tracer(int (*init_proc)(void *), void *sp); | ||
21 | extern void attach_process(int pid); | ||
22 | extern void tracer_panic(char *format, ...); | ||
23 | extern void set_init_pid(int pid); | ||
24 | extern int set_user_mode(void *task); | ||
25 | extern void set_tracing(void *t, int tracing); | ||
26 | extern int is_tracing(void *task); | ||
27 | extern void syscall_handler(int sig, union uml_pt_regs *regs); | ||
28 | extern void exit_kernel(int pid, void *task); | ||
29 | extern void do_syscall(void *task, int pid, int local_using_sysemu); | ||
30 | extern void do_sigtrap(void *task); | ||
31 | extern int is_valid_pid(int pid); | ||
32 | extern void remap_data(void *segment_start, void *segment_end, int w); | ||
33 | extern long execute_syscall_tt(void *r); | ||
34 | |||
35 | #endif | ||
36 | |||
37 | /* | ||
38 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
39 | * Emacs will notice this stuff at the end of the file and automatically | ||
40 | * adjust the settings for this buffer only. This must remain at the end | ||
41 | * of the file. | ||
42 | * --------------------------------------------------------------------------- | ||
43 | * Local variables: | ||
44 | * c-file-style: "linux" | ||
45 | * End: | ||
46 | */ | ||
diff --git a/arch/um/kernel/tt/include/uaccess-tt.h b/arch/um/kernel/tt/include/uaccess-tt.h new file mode 100644 index 000000000000..f0bad010cebd --- /dev/null +++ b/arch/um/kernel/tt/include/uaccess-tt.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __TT_UACCESS_H | ||
7 | #define __TT_UACCESS_H | ||
8 | |||
9 | #include "linux/string.h" | ||
10 | #include "linux/sched.h" | ||
11 | #include "asm/processor.h" | ||
12 | #include "asm/errno.h" | ||
13 | #include "asm/current.h" | ||
14 | #include "asm/a.out.h" | ||
15 | #include "uml_uaccess.h" | ||
16 | |||
17 | #define ABOVE_KMEM (16 * 1024 * 1024) | ||
18 | |||
19 | extern unsigned long end_vm; | ||
20 | extern unsigned long uml_physmem; | ||
21 | |||
22 | #define under_task_size(addr, size) \ | ||
23 | (((unsigned long) (addr) < TASK_SIZE) && \ | ||
24 | (((unsigned long) (addr) + (size)) < TASK_SIZE)) | ||
25 | |||
26 | #define is_stack(addr, size) \ | ||
27 | (((unsigned long) (addr) < STACK_TOP) && \ | ||
28 | ((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \ | ||
29 | (((unsigned long) (addr) + (size)) <= STACK_TOP)) | ||
30 | |||
31 | #define access_ok_tt(type, addr, size) \ | ||
32 | ((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \ | ||
33 | (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \ | ||
34 | (under_task_size(addr, size) || is_stack(addr, size)))) | ||
35 | |||
36 | static inline int verify_area_tt(int type, const void * addr, | ||
37 | unsigned long size) | ||
38 | { | ||
39 | return(access_ok_tt(type, addr, size) ? 0 : -EFAULT); | ||
40 | } | ||
41 | |||
42 | extern unsigned long get_fault_addr(void); | ||
43 | |||
44 | extern int __do_copy_from_user(void *to, const void *from, int n, | ||
45 | void **fault_addr, void **fault_catcher); | ||
46 | extern int __do_strncpy_from_user(char *dst, const char *src, size_t n, | ||
47 | void **fault_addr, void **fault_catcher); | ||
48 | extern int __do_clear_user(void *mem, size_t len, void **fault_addr, | ||
49 | void **fault_catcher); | ||
50 | extern int __do_strnlen_user(const char *str, unsigned long n, | ||
51 | void **fault_addr, void **fault_catcher); | ||
52 | |||
53 | extern int copy_from_user_tt(void *to, const void *from, int n); | ||
54 | extern int copy_to_user_tt(void *to, const void *from, int n); | ||
55 | extern int strncpy_from_user_tt(char *dst, const char *src, int count); | ||
56 | extern int __clear_user_tt(void *mem, int len); | ||
57 | extern int clear_user_tt(void *mem, int len); | ||
58 | extern int strnlen_user_tt(const void *str, int len); | ||
59 | |||
60 | #endif | ||
61 | |||
62 | /* | ||
63 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
64 | * Emacs will notice this stuff at the end of the file and automatically | ||
65 | * adjust the settings for this buffer only. This must remain at the end | ||
66 | * of the file. | ||
67 | * --------------------------------------------------------------------------- | ||
68 | * Local variables: | ||
69 | * c-file-style: "linux" | ||
70 | * End: | ||
71 | */ | ||
diff --git a/arch/um/kernel/tt/ksyms.c b/arch/um/kernel/tt/ksyms.c new file mode 100644 index 000000000000..92ec85d67c7c --- /dev/null +++ b/arch/um/kernel/tt/ksyms.c | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/module.h" | ||
7 | #include "asm/uaccess.h" | ||
8 | #include "mode.h" | ||
9 | |||
10 | EXPORT_SYMBOL(__do_copy_from_user); | ||
11 | EXPORT_SYMBOL(__do_copy_to_user); | ||
12 | EXPORT_SYMBOL(__do_strncpy_from_user); | ||
13 | EXPORT_SYMBOL(__do_strnlen_user); | ||
14 | EXPORT_SYMBOL(__do_clear_user); | ||
15 | |||
16 | EXPORT_SYMBOL(tracing_pid); | ||
17 | EXPORT_SYMBOL(honeypot); | ||
18 | |||
19 | /* | ||
20 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
21 | * Emacs will notice this stuff at the end of the file and automatically | ||
22 | * adjust the settings for this buffer only. This must remain at the end | ||
23 | * of the file. | ||
24 | * --------------------------------------------------------------------------- | ||
25 | * Local variables: | ||
26 | * c-file-style: "linux" | ||
27 | * End: | ||
28 | */ | ||
diff --git a/arch/um/kernel/tt/mem.c b/arch/um/kernel/tt/mem.c new file mode 100644 index 000000000000..74346a04a2b2 --- /dev/null +++ b/arch/um/kernel/tt/mem.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/stddef.h" | ||
7 | #include "linux/config.h" | ||
8 | #include "linux/mm.h" | ||
9 | #include "asm/uaccess.h" | ||
10 | #include "mem_user.h" | ||
11 | #include "kern_util.h" | ||
12 | #include "user_util.h" | ||
13 | #include "kern.h" | ||
14 | #include "tt.h" | ||
15 | |||
16 | void before_mem_tt(unsigned long brk_start) | ||
17 | { | ||
18 | if(debug) | ||
19 | remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1); | ||
20 | remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1); | ||
21 | remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1); | ||
22 | } | ||
23 | |||
24 | #ifdef CONFIG_HOST_2G_2G | ||
25 | #define TOP 0x80000000 | ||
26 | #else | ||
27 | #define TOP 0xc0000000 | ||
28 | #endif | ||
29 | |||
30 | #define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000) | ||
31 | #define START (TOP - SIZE) | ||
32 | |||
33 | unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out, | ||
34 | unsigned long *task_size_out) | ||
35 | { | ||
36 | /* Round up to the nearest 4M */ | ||
37 | *host_size_out = ROUND_4M((unsigned long) &arg); | ||
38 | *task_size_out = START; | ||
39 | return(START); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
44 | * Emacs will notice this stuff at the end of the file and automatically | ||
45 | * adjust the settings for this buffer only. This must remain at the end | ||
46 | * of the file. | ||
47 | * --------------------------------------------------------------------------- | ||
48 | * Local variables: | ||
49 | * c-file-style: "linux" | ||
50 | * End: | ||
51 | */ | ||
diff --git a/arch/um/kernel/tt/mem_user.c b/arch/um/kernel/tt/mem_user.c new file mode 100644 index 000000000000..3085267459b1 --- /dev/null +++ b/arch/um/kernel/tt/mem_user.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <stdio.h> | ||
8 | #include <unistd.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | #include <sys/mman.h> | ||
12 | #include "tt.h" | ||
13 | #include "mem_user.h" | ||
14 | #include "user_util.h" | ||
15 | |||
16 | void remap_data(void *segment_start, void *segment_end, int w) | ||
17 | { | ||
18 | void *addr; | ||
19 | unsigned long size; | ||
20 | int data, prot; | ||
21 | |||
22 | if(w) prot = PROT_WRITE; | ||
23 | else prot = 0; | ||
24 | prot |= PROT_READ | PROT_EXEC; | ||
25 | size = (unsigned long) segment_end - | ||
26 | (unsigned long) segment_start; | ||
27 | data = create_mem_file(size); | ||
28 | addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0); | ||
29 | if(addr == MAP_FAILED){ | ||
30 | perror("mapping new data segment"); | ||
31 | exit(1); | ||
32 | } | ||
33 | memcpy(addr, segment_start, size); | ||
34 | if(switcheroo(data, prot, addr, segment_start, size) < 0){ | ||
35 | printf("switcheroo failed\n"); | ||
36 | exit(1); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | /* | ||
41 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
42 | * Emacs will notice this stuff at the end of the file and automatically | ||
43 | * adjust the settings for this buffer only. This must remain at the end | ||
44 | * of the file. | ||
45 | * --------------------------------------------------------------------------- | ||
46 | * Local variables: | ||
47 | * c-file-style: "linux" | ||
48 | * End: | ||
49 | */ | ||
diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c new file mode 100644 index 000000000000..f19f7c18febe --- /dev/null +++ b/arch/um/kernel/tt/process_kern.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/signal.h" | ||
8 | #include "linux/kernel.h" | ||
9 | #include "linux/interrupt.h" | ||
10 | #include "linux/ptrace.h" | ||
11 | #include "asm/system.h" | ||
12 | #include "asm/pgalloc.h" | ||
13 | #include "asm/ptrace.h" | ||
14 | #include "asm/tlbflush.h" | ||
15 | #include "irq_user.h" | ||
16 | #include "signal_user.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "user_util.h" | ||
19 | #include "os.h" | ||
20 | #include "kern.h" | ||
21 | #include "sigcontext.h" | ||
22 | #include "time_user.h" | ||
23 | #include "mem_user.h" | ||
24 | #include "tlb.h" | ||
25 | #include "mode.h" | ||
26 | #include "init.h" | ||
27 | #include "tt.h" | ||
28 | |||
29 | void *switch_to_tt(void *prev, void *next, void *last) | ||
30 | { | ||
31 | struct task_struct *from, *to, *prev_sched; | ||
32 | unsigned long flags; | ||
33 | int err, vtalrm, alrm, prof, cpu; | ||
34 | char c; | ||
35 | /* jailing and SMP are incompatible, so this doesn't need to be | ||
36 | * made per-cpu | ||
37 | */ | ||
38 | static int reading; | ||
39 | |||
40 | from = prev; | ||
41 | to = next; | ||
42 | |||
43 | to->thread.prev_sched = from; | ||
44 | |||
45 | cpu = from->thread_info->cpu; | ||
46 | if(cpu == 0) | ||
47 | forward_interrupts(to->thread.mode.tt.extern_pid); | ||
48 | #ifdef CONFIG_SMP | ||
49 | forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid); | ||
50 | #endif | ||
51 | local_irq_save(flags); | ||
52 | |||
53 | vtalrm = change_sig(SIGVTALRM, 0); | ||
54 | alrm = change_sig(SIGALRM, 0); | ||
55 | prof = change_sig(SIGPROF, 0); | ||
56 | |||
57 | forward_pending_sigio(to->thread.mode.tt.extern_pid); | ||
58 | |||
59 | c = 0; | ||
60 | set_current(to); | ||
61 | |||
62 | reading = 0; | ||
63 | err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c)); | ||
64 | if(err != sizeof(c)) | ||
65 | panic("write of switch_pipe failed, err = %d", -err); | ||
66 | |||
67 | reading = 1; | ||
68 | if((from->exit_state == EXIT_ZOMBIE) || | ||
69 | (from->exit_state == EXIT_DEAD)) | ||
70 | os_kill_process(os_getpid(), 0); | ||
71 | |||
72 | err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c)); | ||
73 | if(err != sizeof(c)) | ||
74 | panic("read of switch_pipe failed, errno = %d", -err); | ||
75 | |||
76 | /* If the process that we have just scheduled away from has exited, | ||
77 | * then it needs to be killed here. The reason is that, even though | ||
78 | * it will kill itself when it next runs, that may be too late. Its | ||
79 | * stack will be freed, possibly before then, and if that happens, | ||
80 | * we have a use-after-free situation. So, it gets killed here | ||
81 | * in case it has not already killed itself. | ||
82 | */ | ||
83 | prev_sched = current->thread.prev_sched; | ||
84 | if((prev_sched->exit_state == EXIT_ZOMBIE) || | ||
85 | (prev_sched->exit_state == EXIT_DEAD)) | ||
86 | os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1); | ||
87 | |||
88 | change_sig(SIGVTALRM, vtalrm); | ||
89 | change_sig(SIGALRM, alrm); | ||
90 | change_sig(SIGPROF, prof); | ||
91 | |||
92 | arch_switch(); | ||
93 | |||
94 | flush_tlb_all(); | ||
95 | local_irq_restore(flags); | ||
96 | |||
97 | return(current->thread.prev_sched); | ||
98 | } | ||
99 | |||
100 | void release_thread_tt(struct task_struct *task) | ||
101 | { | ||
102 | int pid = task->thread.mode.tt.extern_pid; | ||
103 | |||
104 | if(os_getpid() != pid) | ||
105 | os_kill_process(pid, 0); | ||
106 | } | ||
107 | |||
108 | void exit_thread_tt(void) | ||
109 | { | ||
110 | os_close_file(current->thread.mode.tt.switch_pipe[0]); | ||
111 | os_close_file(current->thread.mode.tt.switch_pipe[1]); | ||
112 | } | ||
113 | |||
114 | void suspend_new_thread(int fd) | ||
115 | { | ||
116 | int err; | ||
117 | char c; | ||
118 | |||
119 | os_stop_process(os_getpid()); | ||
120 | err = os_read_file(fd, &c, sizeof(c)); | ||
121 | if(err != sizeof(c)) | ||
122 | panic("read failed in suspend_new_thread, err = %d", -err); | ||
123 | } | ||
124 | |||
125 | void schedule_tail(task_t *prev); | ||
126 | |||
127 | static void new_thread_handler(int sig) | ||
128 | { | ||
129 | unsigned long disable; | ||
130 | int (*fn)(void *); | ||
131 | void *arg; | ||
132 | |||
133 | fn = current->thread.request.u.thread.proc; | ||
134 | arg = current->thread.request.u.thread.arg; | ||
135 | |||
136 | UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); | ||
137 | disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) | | ||
138 | (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1)); | ||
139 | SC_SIGMASK(UPT_SC(¤t->thread.regs.regs)) &= ~disable; | ||
140 | |||
141 | suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); | ||
142 | |||
143 | force_flush_all(); | ||
144 | if(current->thread.prev_sched != NULL) | ||
145 | schedule_tail(current->thread.prev_sched); | ||
146 | current->thread.prev_sched = NULL; | ||
147 | |||
148 | init_new_thread_signals(1); | ||
149 | enable_timer(); | ||
150 | free_page(current->thread.temp_stack); | ||
151 | set_cmdline("(kernel thread)"); | ||
152 | |||
153 | change_sig(SIGUSR1, 1); | ||
154 | change_sig(SIGVTALRM, 1); | ||
155 | change_sig(SIGPROF, 1); | ||
156 | local_irq_enable(); | ||
157 | if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf)) | ||
158 | do_exit(0); | ||
159 | |||
160 | /* XXX No set_user_mode here because a newly execed process will | ||
161 | * immediately segfault on its non-existent IP, coming straight back | ||
162 | * to the signal handler, which will call set_user_mode on its way | ||
163 | * out. This should probably change since it's confusing. | ||
164 | */ | ||
165 | } | ||
166 | |||
167 | static int new_thread_proc(void *stack) | ||
168 | { | ||
169 | /* local_irq_disable is needed to block out signals until this thread is | ||
170 | * properly scheduled. Otherwise, the tracing thread will get mighty | ||
171 | * upset about any signals that arrive before that. | ||
172 | * This has the complication that it sets the saved signal mask in | ||
173 | * the sigcontext to block signals. This gets restored when this | ||
174 | * thread (or a descendant, since they get a copy of this sigcontext) | ||
175 | * returns to userspace. | ||
176 | * So, this is compensated for elsewhere. | ||
177 | * XXX There is still a small window until local_irq_disable() actually | ||
178 | * finishes where signals are possible - shouldn't be a problem in | ||
179 | * practice since SIGIO hasn't been forwarded here yet, and the | ||
180 | * local_irq_disable should finish before a SIGVTALRM has time to be | ||
181 | * delivered. | ||
182 | */ | ||
183 | |||
184 | local_irq_disable(); | ||
185 | init_new_thread_stack(stack, new_thread_handler); | ||
186 | os_usr1_process(os_getpid()); | ||
187 | change_sig(SIGUSR1, 1); | ||
188 | return(0); | ||
189 | } | ||
190 | |||
191 | /* Signal masking - signals are blocked at the start of fork_tramp. They | ||
192 | * are re-enabled when finish_fork_handler is entered by fork_tramp hitting | ||
193 | * itself with a SIGUSR1. set_user_mode has to be run with SIGUSR1 off, | ||
194 | * so it is blocked before it's called. They are re-enabled on sigreturn | ||
195 | * despite the fact that they were blocked when the SIGUSR1 was issued because | ||
196 | * copy_thread copies the parent's sigcontext, including the signal mask | ||
197 | * onto the signal frame. | ||
198 | */ | ||
199 | |||
200 | void finish_fork_handler(int sig) | ||
201 | { | ||
202 | UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); | ||
203 | suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); | ||
204 | |||
205 | force_flush_all(); | ||
206 | if(current->thread.prev_sched != NULL) | ||
207 | schedule_tail(current->thread.prev_sched); | ||
208 | current->thread.prev_sched = NULL; | ||
209 | |||
210 | enable_timer(); | ||
211 | change_sig(SIGVTALRM, 1); | ||
212 | local_irq_enable(); | ||
213 | if(current->mm != current->parent->mm) | ||
214 | protect_memory(uml_reserved, high_physmem - uml_reserved, 1, | ||
215 | 1, 0, 1); | ||
216 | task_protections((unsigned long) current_thread); | ||
217 | |||
218 | free_page(current->thread.temp_stack); | ||
219 | local_irq_disable(); | ||
220 | change_sig(SIGUSR1, 0); | ||
221 | set_user_mode(current); | ||
222 | } | ||
223 | |||
224 | int fork_tramp(void *stack) | ||
225 | { | ||
226 | local_irq_disable(); | ||
227 | arch_init_thread(); | ||
228 | init_new_thread_stack(stack, finish_fork_handler); | ||
229 | |||
230 | os_usr1_process(os_getpid()); | ||
231 | change_sig(SIGUSR1, 1); | ||
232 | return(0); | ||
233 | } | ||
234 | |||
235 | int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp, | ||
236 | unsigned long stack_top, struct task_struct * p, | ||
237 | struct pt_regs *regs) | ||
238 | { | ||
239 | int (*tramp)(void *); | ||
240 | int new_pid, err; | ||
241 | unsigned long stack; | ||
242 | |||
243 | if(current->thread.forking) | ||
244 | tramp = fork_tramp; | ||
245 | else { | ||
246 | tramp = new_thread_proc; | ||
247 | p->thread.request.u.thread = current->thread.request.u.thread; | ||
248 | } | ||
249 | |||
250 | err = os_pipe(p->thread.mode.tt.switch_pipe, 1, 1); | ||
251 | if(err < 0){ | ||
252 | printk("copy_thread : pipe failed, err = %d\n", -err); | ||
253 | return(err); | ||
254 | } | ||
255 | |||
256 | stack = alloc_stack(0, 0); | ||
257 | if(stack == 0){ | ||
258 | printk(KERN_ERR "copy_thread : failed to allocate " | ||
259 | "temporary stack\n"); | ||
260 | return(-ENOMEM); | ||
261 | } | ||
262 | |||
263 | clone_flags &= CLONE_VM; | ||
264 | p->thread.temp_stack = stack; | ||
265 | new_pid = start_fork_tramp(p->thread_info, stack, clone_flags, tramp); | ||
266 | if(new_pid < 0){ | ||
267 | printk(KERN_ERR "copy_thread : clone failed - errno = %d\n", | ||
268 | -new_pid); | ||
269 | return(new_pid); | ||
270 | } | ||
271 | |||
272 | if(current->thread.forking){ | ||
273 | sc_to_sc(UPT_SC(&p->thread.regs.regs), | ||
274 | UPT_SC(¤t->thread.regs.regs)); | ||
275 | SC_SET_SYSCALL_RETURN(UPT_SC(&p->thread.regs.regs), 0); | ||
276 | if(sp != 0) SC_SP(UPT_SC(&p->thread.regs.regs)) = sp; | ||
277 | } | ||
278 | p->thread.mode.tt.extern_pid = new_pid; | ||
279 | |||
280 | current->thread.request.op = OP_FORK; | ||
281 | current->thread.request.u.fork.pid = new_pid; | ||
282 | os_usr1_process(os_getpid()); | ||
283 | |||
284 | /* Enable the signal and then disable it to ensure that it is handled | ||
285 | * here, and nowhere else. | ||
286 | */ | ||
287 | change_sig(SIGUSR1, 1); | ||
288 | |||
289 | change_sig(SIGUSR1, 0); | ||
290 | err = 0; | ||
291 | return(err); | ||
292 | } | ||
293 | |||
294 | void reboot_tt(void) | ||
295 | { | ||
296 | current->thread.request.op = OP_REBOOT; | ||
297 | os_usr1_process(os_getpid()); | ||
298 | change_sig(SIGUSR1, 1); | ||
299 | } | ||
300 | |||
301 | void halt_tt(void) | ||
302 | { | ||
303 | current->thread.request.op = OP_HALT; | ||
304 | os_usr1_process(os_getpid()); | ||
305 | change_sig(SIGUSR1, 1); | ||
306 | } | ||
307 | |||
308 | void kill_off_processes_tt(void) | ||
309 | { | ||
310 | struct task_struct *p; | ||
311 | int me; | ||
312 | |||
313 | me = os_getpid(); | ||
314 | for_each_process(p){ | ||
315 | if(p->thread.mode.tt.extern_pid != me) | ||
316 | os_kill_process(p->thread.mode.tt.extern_pid, 0); | ||
317 | } | ||
318 | if(init_task.thread.mode.tt.extern_pid != me) | ||
319 | os_kill_process(init_task.thread.mode.tt.extern_pid, 0); | ||
320 | } | ||
321 | |||
322 | void initial_thread_cb_tt(void (*proc)(void *), void *arg) | ||
323 | { | ||
324 | if(os_getpid() == tracing_pid){ | ||
325 | (*proc)(arg); | ||
326 | } | ||
327 | else { | ||
328 | current->thread.request.op = OP_CB; | ||
329 | current->thread.request.u.cb.proc = proc; | ||
330 | current->thread.request.u.cb.arg = arg; | ||
331 | os_usr1_process(os_getpid()); | ||
332 | change_sig(SIGUSR1, 1); | ||
333 | |||
334 | change_sig(SIGUSR1, 0); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | int do_proc_op(void *t, int proc_id) | ||
339 | { | ||
340 | struct task_struct *task; | ||
341 | struct thread_struct *thread; | ||
342 | int op, pid; | ||
343 | |||
344 | task = t; | ||
345 | thread = &task->thread; | ||
346 | op = thread->request.op; | ||
347 | switch(op){ | ||
348 | case OP_NONE: | ||
349 | case OP_TRACE_ON: | ||
350 | break; | ||
351 | case OP_EXEC: | ||
352 | pid = thread->request.u.exec.pid; | ||
353 | do_exec(thread->mode.tt.extern_pid, pid); | ||
354 | thread->mode.tt.extern_pid = pid; | ||
355 | cpu_tasks[task->thread_info->cpu].pid = pid; | ||
356 | break; | ||
357 | case OP_FORK: | ||
358 | attach_process(thread->request.u.fork.pid); | ||
359 | break; | ||
360 | case OP_CB: | ||
361 | (*thread->request.u.cb.proc)(thread->request.u.cb.arg); | ||
362 | break; | ||
363 | case OP_REBOOT: | ||
364 | case OP_HALT: | ||
365 | break; | ||
366 | default: | ||
367 | tracer_panic("Bad op in do_proc_op"); | ||
368 | break; | ||
369 | } | ||
370 | thread->request.op = OP_NONE; | ||
371 | return(op); | ||
372 | } | ||
373 | |||
374 | void init_idle_tt(void) | ||
375 | { | ||
376 | default_idle(); | ||
377 | } | ||
378 | |||
379 | extern void start_kernel(void); | ||
380 | |||
381 | static int start_kernel_proc(void *unused) | ||
382 | { | ||
383 | int pid; | ||
384 | |||
385 | block_signals(); | ||
386 | pid = os_getpid(); | ||
387 | |||
388 | cpu_tasks[0].pid = pid; | ||
389 | cpu_tasks[0].task = current; | ||
390 | #ifdef CONFIG_SMP | ||
391 | cpu_online_map = cpumask_of_cpu(0); | ||
392 | #endif | ||
393 | if(debug) os_stop_process(pid); | ||
394 | start_kernel(); | ||
395 | return(0); | ||
396 | } | ||
397 | |||
398 | void set_tracing(void *task, int tracing) | ||
399 | { | ||
400 | ((struct task_struct *) task)->thread.mode.tt.tracing = tracing; | ||
401 | } | ||
402 | |||
403 | int is_tracing(void *t) | ||
404 | { | ||
405 | return (((struct task_struct *) t)->thread.mode.tt.tracing); | ||
406 | } | ||
407 | |||
408 | int set_user_mode(void *t) | ||
409 | { | ||
410 | struct task_struct *task; | ||
411 | |||
412 | task = t ? t : current; | ||
413 | if(task->thread.mode.tt.tracing) | ||
414 | return(1); | ||
415 | task->thread.request.op = OP_TRACE_ON; | ||
416 | os_usr1_process(os_getpid()); | ||
417 | return(0); | ||
418 | } | ||
419 | |||
420 | void set_init_pid(int pid) | ||
421 | { | ||
422 | int err; | ||
423 | |||
424 | init_task.thread.mode.tt.extern_pid = pid; | ||
425 | err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1); | ||
426 | if(err) | ||
427 | panic("Can't create switch pipe for init_task, errno = %d", | ||
428 | -err); | ||
429 | } | ||
430 | |||
431 | int start_uml_tt(void) | ||
432 | { | ||
433 | void *sp; | ||
434 | int pages; | ||
435 | |||
436 | pages = (1 << CONFIG_KERNEL_STACK_ORDER); | ||
437 | sp = (void *) ((unsigned long) init_task.thread_info) + | ||
438 | pages * PAGE_SIZE - sizeof(unsigned long); | ||
439 | return(tracer(start_kernel_proc, sp)); | ||
440 | } | ||
441 | |||
442 | int external_pid_tt(struct task_struct *task) | ||
443 | { | ||
444 | return(task->thread.mode.tt.extern_pid); | ||
445 | } | ||
446 | |||
447 | int thread_pid_tt(struct task_struct *task) | ||
448 | { | ||
449 | return(task->thread.mode.tt.extern_pid); | ||
450 | } | ||
451 | |||
452 | int is_valid_pid(int pid) | ||
453 | { | ||
454 | struct task_struct *task; | ||
455 | |||
456 | read_lock(&tasklist_lock); | ||
457 | for_each_process(task){ | ||
458 | if(task->thread.mode.tt.extern_pid == pid){ | ||
459 | read_unlock(&tasklist_lock); | ||
460 | return(1); | ||
461 | } | ||
462 | } | ||
463 | read_unlock(&tasklist_lock); | ||
464 | return(0); | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
469 | * Emacs will notice this stuff at the end of the file and automatically | ||
470 | * adjust the settings for this buffer only. This must remain at the end | ||
471 | * of the file. | ||
472 | * --------------------------------------------------------------------------- | ||
473 | * Local variables: | ||
474 | * c-file-style: "linux" | ||
475 | * End: | ||
476 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/Makefile b/arch/um/kernel/tt/ptproxy/Makefile new file mode 100644 index 000000000000..3ad5b774de59 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | obj-y = proxy.o ptrace.o sysdep.o wait.o | ||
7 | |||
8 | USER_OBJS := $(obj-y) | ||
9 | |||
10 | include arch/um/scripts/Makefile.rules | ||
diff --git a/arch/um/kernel/tt/ptproxy/proxy.c b/arch/um/kernel/tt/ptproxy/proxy.c new file mode 100644 index 000000000000..58800c50b10e --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/proxy.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /********************************************************************** | ||
2 | proxy.c | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | |||
7 | Jeff Dike (jdike@karaya.com) : Modified for integration into uml | ||
8 | **********************************************************************/ | ||
9 | |||
10 | /* XXX This file shouldn't refer to CONFIG_* */ | ||
11 | |||
12 | #include <errno.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <unistd.h> | ||
16 | #include <signal.h> | ||
17 | #include <string.h> | ||
18 | #include <termios.h> | ||
19 | #include <sys/wait.h> | ||
20 | #include <sys/types.h> | ||
21 | #include <sys/ioctl.h> | ||
22 | #include <asm/unistd.h> | ||
23 | #include "ptrace_user.h" | ||
24 | |||
25 | #include "ptproxy.h" | ||
26 | #include "sysdep.h" | ||
27 | #include "wait.h" | ||
28 | |||
29 | #include "user_util.h" | ||
30 | #include "user.h" | ||
31 | #include "os.h" | ||
32 | #include "tempfile.h" | ||
33 | |||
34 | static int debugger_wait(debugger_state *debugger, int *status, int options, | ||
35 | int (*syscall)(debugger_state *debugger, pid_t child), | ||
36 | int (*normal_return)(debugger_state *debugger, | ||
37 | pid_t unused), | ||
38 | int (*wait_return)(debugger_state *debugger, | ||
39 | pid_t unused)) | ||
40 | { | ||
41 | if(debugger->real_wait){ | ||
42 | debugger->handle_trace = normal_return; | ||
43 | syscall_continue(debugger->pid); | ||
44 | debugger->real_wait = 0; | ||
45 | return(1); | ||
46 | } | ||
47 | debugger->wait_status_ptr = status; | ||
48 | debugger->wait_options = options; | ||
49 | if((debugger->debugee != NULL) && debugger->debugee->event){ | ||
50 | syscall_continue(debugger->pid); | ||
51 | wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL, | ||
52 | NULL); | ||
53 | (*wait_return)(debugger, -1); | ||
54 | return(0); | ||
55 | } | ||
56 | else if(debugger->wait_options & WNOHANG){ | ||
57 | syscall_cancel(debugger->pid, 0); | ||
58 | debugger->handle_trace = syscall; | ||
59 | return(0); | ||
60 | } | ||
61 | else { | ||
62 | syscall_pause(debugger->pid); | ||
63 | debugger->handle_trace = wait_return; | ||
64 | debugger->waiting = 1; | ||
65 | } | ||
66 | return(1); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Handle debugger trap, i.e. syscall. | ||
71 | */ | ||
72 | |||
73 | int debugger_syscall(debugger_state *debugger, pid_t child) | ||
74 | { | ||
75 | long arg1, arg2, arg3, arg4, arg5, result; | ||
76 | int syscall, ret = 0; | ||
77 | |||
78 | syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, | ||
79 | &arg5); | ||
80 | |||
81 | switch(syscall){ | ||
82 | case __NR_execve: | ||
83 | /* execve never returns */ | ||
84 | debugger->handle_trace = debugger_syscall; | ||
85 | break; | ||
86 | |||
87 | case __NR_ptrace: | ||
88 | if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; | ||
89 | if(!debugger->debugee->in_context) | ||
90 | child = debugger->debugee->pid; | ||
91 | result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child, | ||
92 | &ret); | ||
93 | syscall_cancel(debugger->pid, result); | ||
94 | debugger->handle_trace = debugger_syscall; | ||
95 | return(ret); | ||
96 | |||
97 | #ifdef __NR_waitpid | ||
98 | case __NR_waitpid: | ||
99 | #endif | ||
100 | case __NR_wait4: | ||
101 | if(!debugger_wait(debugger, (int *) arg2, arg3, | ||
102 | debugger_syscall, debugger_normal_return, | ||
103 | proxy_wait_return)) | ||
104 | return(0); | ||
105 | break; | ||
106 | |||
107 | case __NR_kill: | ||
108 | if(!debugger->debugee->in_context) | ||
109 | child = debugger->debugee->pid; | ||
110 | if(arg1 == debugger->debugee->pid){ | ||
111 | result = kill(child, arg2); | ||
112 | syscall_cancel(debugger->pid, result); | ||
113 | debugger->handle_trace = debugger_syscall; | ||
114 | return(0); | ||
115 | } | ||
116 | else debugger->handle_trace = debugger_normal_return; | ||
117 | break; | ||
118 | |||
119 | default: | ||
120 | debugger->handle_trace = debugger_normal_return; | ||
121 | } | ||
122 | |||
123 | syscall_continue(debugger->pid); | ||
124 | return(0); | ||
125 | } | ||
126 | |||
127 | /* Used by the tracing thread */ | ||
128 | static debugger_state parent; | ||
129 | static int parent_syscall(debugger_state *debugger, int pid); | ||
130 | |||
131 | int init_parent_proxy(int pid) | ||
132 | { | ||
133 | parent = ((debugger_state) { .pid = pid, | ||
134 | .wait_options = 0, | ||
135 | .wait_status_ptr = NULL, | ||
136 | .waiting = 0, | ||
137 | .real_wait = 0, | ||
138 | .expecting_child = 0, | ||
139 | .handle_trace = parent_syscall, | ||
140 | .debugee = NULL } ); | ||
141 | return(0); | ||
142 | } | ||
143 | |||
144 | int parent_normal_return(debugger_state *debugger, pid_t unused) | ||
145 | { | ||
146 | debugger->handle_trace = parent_syscall; | ||
147 | syscall_continue(debugger->pid); | ||
148 | return(0); | ||
149 | } | ||
150 | |||
151 | static int parent_syscall(debugger_state *debugger, int pid) | ||
152 | { | ||
153 | long arg1, arg2, arg3, arg4, arg5; | ||
154 | int syscall; | ||
155 | |||
156 | syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5); | ||
157 | |||
158 | if((syscall == __NR_wait4) | ||
159 | #ifdef __NR_waitpid | ||
160 | || (syscall == __NR_waitpid) | ||
161 | #endif | ||
162 | ){ | ||
163 | debugger_wait(&parent, (int *) arg2, arg3, parent_syscall, | ||
164 | parent_normal_return, parent_wait_return); | ||
165 | } | ||
166 | else ptrace(PTRACE_SYSCALL, pid, 0, 0); | ||
167 | return(0); | ||
168 | } | ||
169 | |||
170 | int debugger_normal_return(debugger_state *debugger, pid_t unused) | ||
171 | { | ||
172 | debugger->handle_trace = debugger_syscall; | ||
173 | syscall_continue(debugger->pid); | ||
174 | return(0); | ||
175 | } | ||
176 | |||
177 | void debugger_cancelled_return(debugger_state *debugger, int result) | ||
178 | { | ||
179 | debugger->handle_trace = debugger_syscall; | ||
180 | syscall_set_result(debugger->pid, result); | ||
181 | syscall_continue(debugger->pid); | ||
182 | } | ||
183 | |||
184 | /* Used by the tracing thread */ | ||
185 | static debugger_state debugger; | ||
186 | static debugee_state debugee; | ||
187 | |||
188 | void init_proxy (pid_t debugger_pid, int stopped, int status) | ||
189 | { | ||
190 | debugger.pid = debugger_pid; | ||
191 | debugger.handle_trace = debugger_syscall; | ||
192 | debugger.debugee = &debugee; | ||
193 | debugger.waiting = 0; | ||
194 | debugger.real_wait = 0; | ||
195 | debugger.expecting_child = 0; | ||
196 | |||
197 | debugee.pid = 0; | ||
198 | debugee.traced = 0; | ||
199 | debugee.stopped = stopped; | ||
200 | debugee.event = 0; | ||
201 | debugee.zombie = 0; | ||
202 | debugee.died = 0; | ||
203 | debugee.wait_status = status; | ||
204 | debugee.in_context = 1; | ||
205 | } | ||
206 | |||
207 | int debugger_proxy(int status, int pid) | ||
208 | { | ||
209 | int ret = 0, sig; | ||
210 | |||
211 | if(WIFSTOPPED(status)){ | ||
212 | sig = WSTOPSIG(status); | ||
213 | if (sig == SIGTRAP) | ||
214 | ret = (*debugger.handle_trace)(&debugger, pid); | ||
215 | |||
216 | else if(sig == SIGCHLD){ | ||
217 | if(debugger.expecting_child){ | ||
218 | ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | ||
219 | debugger.expecting_child = 0; | ||
220 | } | ||
221 | else if(debugger.waiting) | ||
222 | real_wait_return(&debugger); | ||
223 | else { | ||
224 | ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | ||
225 | debugger.real_wait = 1; | ||
226 | } | ||
227 | } | ||
228 | else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | ||
229 | } | ||
230 | else if(WIFEXITED(status)){ | ||
231 | tracer_panic("debugger (pid %d) exited with status %d", | ||
232 | debugger.pid, WEXITSTATUS(status)); | ||
233 | } | ||
234 | else if(WIFSIGNALED(status)){ | ||
235 | tracer_panic("debugger (pid %d) exited with signal %d", | ||
236 | debugger.pid, WTERMSIG(status)); | ||
237 | } | ||
238 | else { | ||
239 | tracer_panic("proxy got unknown status (0x%x) on debugger " | ||
240 | "(pid %d)", status, debugger.pid); | ||
241 | } | ||
242 | return(ret); | ||
243 | } | ||
244 | |||
245 | void child_proxy(pid_t pid, int status) | ||
246 | { | ||
247 | debugee.event = 1; | ||
248 | debugee.wait_status = status; | ||
249 | |||
250 | if(WIFSTOPPED(status)){ | ||
251 | debugee.stopped = 1; | ||
252 | debugger.expecting_child = 1; | ||
253 | kill(debugger.pid, SIGCHLD); | ||
254 | } | ||
255 | else if(WIFEXITED(status) || WIFSIGNALED(status)){ | ||
256 | debugee.zombie = 1; | ||
257 | debugger.expecting_child = 1; | ||
258 | kill(debugger.pid, SIGCHLD); | ||
259 | } | ||
260 | else panic("proxy got unknown status (0x%x) on child (pid %d)", | ||
261 | status, pid); | ||
262 | } | ||
263 | |||
264 | void debugger_parent_signal(int status, int pid) | ||
265 | { | ||
266 | int sig; | ||
267 | |||
268 | if(WIFSTOPPED(status)){ | ||
269 | sig = WSTOPSIG(status); | ||
270 | if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid); | ||
271 | else ptrace(PTRACE_SYSCALL, pid, 0, sig); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | void fake_child_exit(void) | ||
276 | { | ||
277 | int status, pid; | ||
278 | |||
279 | child_proxy(1, W_EXITCODE(0, 0)); | ||
280 | while(debugger.waiting == 1){ | ||
281 | CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); | ||
282 | if(pid != debugger.pid){ | ||
283 | printk("fake_child_exit - waitpid failed, " | ||
284 | "errno = %d\n", errno); | ||
285 | return; | ||
286 | } | ||
287 | debugger_proxy(status, debugger.pid); | ||
288 | } | ||
289 | CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); | ||
290 | if(pid != debugger.pid){ | ||
291 | printk("fake_child_exit - waitpid failed, " | ||
292 | "errno = %d\n", errno); | ||
293 | return; | ||
294 | } | ||
295 | if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0) | ||
296 | printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n", | ||
297 | errno); | ||
298 | } | ||
299 | |||
300 | char gdb_init_string[] = | ||
301 | "att 1 \n\ | ||
302 | b panic \n\ | ||
303 | b stop \n\ | ||
304 | handle SIGWINCH nostop noprint pass \n\ | ||
305 | "; | ||
306 | |||
307 | int start_debugger(char *prog, int startup, int stop, int *fd_out) | ||
308 | { | ||
309 | int slave, child; | ||
310 | |||
311 | slave = open_gdb_chan(); | ||
312 | child = fork(); | ||
313 | if(child == 0){ | ||
314 | char *tempname = NULL; | ||
315 | int fd; | ||
316 | |||
317 | if(setsid() < 0) perror("setsid"); | ||
318 | if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || | ||
319 | (dup2(slave, 2) < 0)){ | ||
320 | printk("start_debugger : dup2 failed, errno = %d\n", | ||
321 | errno); | ||
322 | exit(1); | ||
323 | } | ||
324 | if(ioctl(0, TIOCSCTTY, 0) < 0){ | ||
325 | printk("start_debugger : TIOCSCTTY failed, " | ||
326 | "errno = %d\n", errno); | ||
327 | exit(1); | ||
328 | } | ||
329 | if(tcsetpgrp (1, os_getpid()) < 0){ | ||
330 | printk("start_debugger : tcsetpgrp failed, " | ||
331 | "errno = %d\n", errno); | ||
332 | #ifdef notdef | ||
333 | exit(1); | ||
334 | #endif | ||
335 | } | ||
336 | fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0); | ||
337 | if(fd < 0){ | ||
338 | printk("start_debugger : make_tempfile failed," | ||
339 | "err = %d\n", -fd); | ||
340 | exit(1); | ||
341 | } | ||
342 | os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1); | ||
343 | if(startup){ | ||
344 | if(stop){ | ||
345 | os_write_file(fd, "b start_kernel\n", | ||
346 | strlen("b start_kernel\n")); | ||
347 | } | ||
348 | os_write_file(fd, "c\n", strlen("c\n")); | ||
349 | } | ||
350 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ | ||
351 | printk("start_debugger : PTRACE_TRACEME failed, " | ||
352 | "errno = %d\n", errno); | ||
353 | exit(1); | ||
354 | } | ||
355 | execlp("gdb", "gdb", "--command", tempname, prog, NULL); | ||
356 | printk("start_debugger : exec of gdb failed, errno = %d\n", | ||
357 | errno); | ||
358 | } | ||
359 | if(child < 0){ | ||
360 | printk("start_debugger : fork for gdb failed, errno = %d\n", | ||
361 | errno); | ||
362 | return(-1); | ||
363 | } | ||
364 | *fd_out = slave; | ||
365 | return(child); | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
370 | * Emacs will notice this stuff at the end of the file and automatically | ||
371 | * adjust the settings for this buffer only. This must remain at the end | ||
372 | * of the file. | ||
373 | * --------------------------------------------------------------------------- | ||
374 | * Local variables: | ||
375 | * c-file-style: "linux" | ||
376 | * End: | ||
377 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/ptproxy.h b/arch/um/kernel/tt/ptproxy/ptproxy.h new file mode 100644 index 000000000000..5eb0285b1968 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/ptproxy.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /********************************************************************** | ||
2 | ptproxy.h | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | **********************************************************************/ | ||
7 | |||
8 | #ifndef __PTPROXY_H | ||
9 | #define __PTPROXY_H | ||
10 | |||
11 | #include <sys/types.h> | ||
12 | |||
13 | typedef struct debugger debugger_state; | ||
14 | typedef struct debugee debugee_state; | ||
15 | |||
16 | struct debugger | ||
17 | { | ||
18 | pid_t pid; | ||
19 | int wait_options; | ||
20 | int *wait_status_ptr; | ||
21 | unsigned int waiting : 1; | ||
22 | unsigned int real_wait : 1; | ||
23 | unsigned int expecting_child : 1; | ||
24 | int (*handle_trace) (debugger_state *, pid_t); | ||
25 | |||
26 | debugee_state *debugee; | ||
27 | }; | ||
28 | |||
29 | struct debugee | ||
30 | { | ||
31 | pid_t pid; | ||
32 | int wait_status; | ||
33 | unsigned int died : 1; | ||
34 | unsigned int event : 1; | ||
35 | unsigned int stopped : 1; | ||
36 | unsigned int trace_singlestep : 1; | ||
37 | unsigned int trace_syscall : 1; | ||
38 | unsigned int traced : 1; | ||
39 | unsigned int zombie : 1; | ||
40 | unsigned int in_context : 1; | ||
41 | }; | ||
42 | |||
43 | extern int debugger_syscall(debugger_state *debugger, pid_t pid); | ||
44 | extern int debugger_normal_return (debugger_state *debugger, pid_t unused); | ||
45 | |||
46 | extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t, | ||
47 | int *strace_out); | ||
48 | extern void debugger_cancelled_return(debugger_state *debugger, int result); | ||
49 | |||
50 | #endif | ||
51 | |||
52 | /* | ||
53 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
54 | * Emacs will notice this stuff at the end of the file and automatically | ||
55 | * adjust the settings for this buffer only. This must remain at the end | ||
56 | * of the file. | ||
57 | * --------------------------------------------------------------------------- | ||
58 | * Local variables: | ||
59 | * c-file-style: "linux" | ||
60 | * End: | ||
61 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/ptrace.c b/arch/um/kernel/tt/ptproxy/ptrace.c new file mode 100644 index 000000000000..528a5fc8d887 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/ptrace.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /********************************************************************** | ||
2 | ptrace.c | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | |||
7 | Jeff Dike (jdike@karaya.com) : Modified for integration into uml | ||
8 | **********************************************************************/ | ||
9 | |||
10 | #include <errno.h> | ||
11 | #include <unistd.h> | ||
12 | #include <signal.h> | ||
13 | #include <sys/types.h> | ||
14 | #include <sys/time.h> | ||
15 | #include <sys/wait.h> | ||
16 | |||
17 | #include "ptproxy.h" | ||
18 | #include "debug.h" | ||
19 | #include "user_util.h" | ||
20 | #include "kern_util.h" | ||
21 | #include "ptrace_user.h" | ||
22 | #include "tt.h" | ||
23 | |||
24 | long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2, | ||
25 | long arg3, long arg4, pid_t child, int *ret) | ||
26 | { | ||
27 | sigset_t relay; | ||
28 | long result; | ||
29 | int status; | ||
30 | |||
31 | *ret = 0; | ||
32 | if(debugger->debugee->died) return(-ESRCH); | ||
33 | |||
34 | switch(arg1){ | ||
35 | case PTRACE_ATTACH: | ||
36 | if(debugger->debugee->traced) return(-EPERM); | ||
37 | |||
38 | debugger->debugee->pid = arg2; | ||
39 | debugger->debugee->traced = 1; | ||
40 | |||
41 | if(is_valid_pid(arg2) && (arg2 != child)){ | ||
42 | debugger->debugee->in_context = 0; | ||
43 | kill(arg2, SIGSTOP); | ||
44 | debugger->debugee->event = 1; | ||
45 | debugger->debugee->wait_status = W_STOPCODE(SIGSTOP); | ||
46 | } | ||
47 | else { | ||
48 | debugger->debugee->in_context = 1; | ||
49 | if(debugger->debugee->stopped) | ||
50 | child_proxy(child, W_STOPCODE(SIGSTOP)); | ||
51 | else kill(child, SIGSTOP); | ||
52 | } | ||
53 | |||
54 | return(0); | ||
55 | |||
56 | case PTRACE_DETACH: | ||
57 | if(!debugger->debugee->traced) return(-EPERM); | ||
58 | |||
59 | debugger->debugee->traced = 0; | ||
60 | debugger->debugee->pid = 0; | ||
61 | if(!debugger->debugee->in_context) | ||
62 | kill(child, SIGCONT); | ||
63 | |||
64 | return(0); | ||
65 | |||
66 | case PTRACE_CONT: | ||
67 | if(!debugger->debugee->in_context) return(-EPERM); | ||
68 | *ret = PTRACE_CONT; | ||
69 | return(ptrace(PTRACE_CONT, child, arg3, arg4)); | ||
70 | |||
71 | #ifdef UM_HAVE_GETFPREGS | ||
72 | case PTRACE_GETFPREGS: | ||
73 | { | ||
74 | long regs[FP_FRAME_SIZE]; | ||
75 | int i, result; | ||
76 | |||
77 | result = ptrace(PTRACE_GETFPREGS, child, 0, regs); | ||
78 | if(result == -1) return(-errno); | ||
79 | |||
80 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
81 | ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, | ||
82 | regs[i]); | ||
83 | return(result); | ||
84 | } | ||
85 | #endif | ||
86 | |||
87 | #ifdef UM_HAVE_GETFPXREGS | ||
88 | case PTRACE_GETFPXREGS: | ||
89 | { | ||
90 | long regs[FPX_FRAME_SIZE]; | ||
91 | int i, result; | ||
92 | |||
93 | result = ptrace(PTRACE_GETFPXREGS, child, 0, regs); | ||
94 | if(result == -1) return(-errno); | ||
95 | |||
96 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
97 | ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, | ||
98 | regs[i]); | ||
99 | return(result); | ||
100 | } | ||
101 | #endif | ||
102 | |||
103 | #ifdef UM_HAVE_GETREGS | ||
104 | case PTRACE_GETREGS: | ||
105 | { | ||
106 | long regs[FRAME_SIZE]; | ||
107 | int i, result; | ||
108 | |||
109 | result = ptrace(PTRACE_GETREGS, child, 0, regs); | ||
110 | if(result == -1) return(-errno); | ||
111 | |||
112 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
113 | ptrace (PTRACE_POKEDATA, debugger->pid, | ||
114 | arg4 + 4 * i, regs[i]); | ||
115 | return(result); | ||
116 | } | ||
117 | break; | ||
118 | #endif | ||
119 | |||
120 | case PTRACE_KILL: | ||
121 | result = ptrace(PTRACE_KILL, child, arg3, arg4); | ||
122 | if(result == -1) return(-errno); | ||
123 | |||
124 | return(result); | ||
125 | |||
126 | case PTRACE_PEEKDATA: | ||
127 | case PTRACE_PEEKTEXT: | ||
128 | case PTRACE_PEEKUSR: | ||
129 | /* The value being read out could be -1, so we have to | ||
130 | * check errno to see if there's an error, and zero it | ||
131 | * beforehand so we're not faked out by an old error | ||
132 | */ | ||
133 | |||
134 | errno = 0; | ||
135 | result = ptrace(arg1, child, arg3, 0); | ||
136 | if((result == -1) && (errno != 0)) return(-errno); | ||
137 | |||
138 | result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result); | ||
139 | if(result == -1) return(-errno); | ||
140 | |||
141 | return(result); | ||
142 | |||
143 | case PTRACE_POKEDATA: | ||
144 | case PTRACE_POKETEXT: | ||
145 | case PTRACE_POKEUSR: | ||
146 | result = ptrace(arg1, child, arg3, arg4); | ||
147 | if(result == -1) return(-errno); | ||
148 | |||
149 | if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4); | ||
150 | return(result); | ||
151 | |||
152 | #ifdef UM_HAVE_SETFPREGS | ||
153 | case PTRACE_SETFPREGS: | ||
154 | { | ||
155 | long regs[FP_FRAME_SIZE]; | ||
156 | int i; | ||
157 | |||
158 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
159 | regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, | ||
160 | arg4 + 4 * i, 0); | ||
161 | result = ptrace(PTRACE_SETFPREGS, child, 0, regs); | ||
162 | if(result == -1) return(-errno); | ||
163 | |||
164 | return(result); | ||
165 | } | ||
166 | #endif | ||
167 | |||
168 | #ifdef UM_HAVE_SETFPXREGS | ||
169 | case PTRACE_SETFPXREGS: | ||
170 | { | ||
171 | long regs[FPX_FRAME_SIZE]; | ||
172 | int i; | ||
173 | |||
174 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
175 | regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, | ||
176 | arg4 + 4 * i, 0); | ||
177 | result = ptrace(PTRACE_SETFPXREGS, child, 0, regs); | ||
178 | if(result == -1) return(-errno); | ||
179 | |||
180 | return(result); | ||
181 | } | ||
182 | #endif | ||
183 | |||
184 | #ifdef UM_HAVE_SETREGS | ||
185 | case PTRACE_SETREGS: | ||
186 | { | ||
187 | long regs[FRAME_SIZE]; | ||
188 | int i; | ||
189 | |||
190 | for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | ||
191 | regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid, | ||
192 | arg4 + 4 * i, 0); | ||
193 | result = ptrace(PTRACE_SETREGS, child, 0, regs); | ||
194 | if(result == -1) return(-errno); | ||
195 | |||
196 | return(result); | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | case PTRACE_SINGLESTEP: | ||
201 | if(!debugger->debugee->in_context) return(-EPERM); | ||
202 | sigemptyset(&relay); | ||
203 | sigaddset(&relay, SIGSEGV); | ||
204 | sigaddset(&relay, SIGILL); | ||
205 | sigaddset(&relay, SIGBUS); | ||
206 | result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4); | ||
207 | if(result == -1) return(-errno); | ||
208 | |||
209 | status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP, | ||
210 | &relay); | ||
211 | child_proxy(child, status); | ||
212 | return(result); | ||
213 | |||
214 | case PTRACE_SYSCALL: | ||
215 | if(!debugger->debugee->in_context) return(-EPERM); | ||
216 | result = ptrace(PTRACE_SYSCALL, child, arg3, arg4); | ||
217 | if(result == -1) return(-errno); | ||
218 | |||
219 | *ret = PTRACE_SYSCALL; | ||
220 | return(result); | ||
221 | |||
222 | case PTRACE_TRACEME: | ||
223 | default: | ||
224 | return(-EINVAL); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
230 | * Emacs will notice this stuff at the end of the file and automatically | ||
231 | * adjust the settings for this buffer only. This must remain at the end | ||
232 | * of the file. | ||
233 | * --------------------------------------------------------------------------- | ||
234 | * Local variables: | ||
235 | * c-file-style: "linux" | ||
236 | * End: | ||
237 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/sysdep.c b/arch/um/kernel/tt/ptproxy/sysdep.c new file mode 100644 index 000000000000..a5f0e01e214e --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/sysdep.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /********************************************************************** | ||
2 | sysdep.c | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | **********************************************************************/ | ||
7 | |||
8 | #include <stdio.h> | ||
9 | #include <string.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <signal.h> | ||
12 | #include <errno.h> | ||
13 | #include <sys/types.h> | ||
14 | #include <linux/unistd.h> | ||
15 | #include "ptrace_user.h" | ||
16 | #include "user_util.h" | ||
17 | #include "user.h" | ||
18 | |||
19 | int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4, | ||
20 | long *arg5) | ||
21 | { | ||
22 | *arg1 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG1_OFFSET, 0); | ||
23 | *arg2 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG2_OFFSET, 0); | ||
24 | *arg3 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG3_OFFSET, 0); | ||
25 | *arg4 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG4_OFFSET, 0); | ||
26 | *arg5 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG5_OFFSET, 0); | ||
27 | return(ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 0)); | ||
28 | } | ||
29 | |||
30 | void syscall_cancel(pid_t pid, int result) | ||
31 | { | ||
32 | if((ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, | ||
33 | __NR_getpid) < 0) || | ||
34 | (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) || | ||
35 | (wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) || | ||
36 | (ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result) < 0) || | ||
37 | (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)) | ||
38 | printk("ptproxy: couldn't cancel syscall: errno = %d\n", | ||
39 | errno); | ||
40 | } | ||
41 | |||
42 | void syscall_set_result(pid_t pid, long result) | ||
43 | { | ||
44 | ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result); | ||
45 | } | ||
46 | |||
47 | void syscall_continue(pid_t pid) | ||
48 | { | ||
49 | ptrace(PTRACE_SYSCALL, pid, 0, 0); | ||
50 | } | ||
51 | |||
52 | int syscall_pause(pid_t pid) | ||
53 | { | ||
54 | if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){ | ||
55 | printk("syscall_change - ptrace failed, errno = %d\n", errno); | ||
56 | return(-1); | ||
57 | } | ||
58 | return(0); | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
63 | * Emacs will notice this stuff at the end of the file and automatically | ||
64 | * adjust the settings for this buffer only. This must remain at the end | ||
65 | * of the file. | ||
66 | * --------------------------------------------------------------------------- | ||
67 | * Local variables: | ||
68 | * c-file-style: "linux" | ||
69 | * End: | ||
70 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/sysdep.h b/arch/um/kernel/tt/ptproxy/sysdep.h new file mode 100644 index 000000000000..735f488049aa --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/sysdep.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /********************************************************************** | ||
2 | sysdep.h | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. | ||
5 | Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
6 | See the file COPYING for licensing terms and conditions. | ||
7 | **********************************************************************/ | ||
8 | |||
9 | extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, | ||
10 | long *arg4, long *arg5); | ||
11 | extern void syscall_cancel (pid_t pid, long result); | ||
12 | extern void syscall_set_result (pid_t pid, long result); | ||
13 | extern void syscall_continue (pid_t pid); | ||
14 | extern int syscall_pause(pid_t pid); | ||
15 | |||
16 | /* | ||
17 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
18 | * Emacs will notice this stuff at the end of the file and automatically | ||
19 | * adjust the settings for this buffer only. This must remain at the end | ||
20 | * of the file. | ||
21 | * --------------------------------------------------------------------------- | ||
22 | * Local variables: | ||
23 | * c-file-style: "linux" | ||
24 | * End: | ||
25 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/wait.c b/arch/um/kernel/tt/ptproxy/wait.c new file mode 100644 index 000000000000..12f6319d8d76 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/wait.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /********************************************************************** | ||
2 | wait.c | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | |||
7 | **********************************************************************/ | ||
8 | |||
9 | #include <errno.h> | ||
10 | #include <signal.h> | ||
11 | #include <sys/wait.h> | ||
12 | |||
13 | #include "ptproxy.h" | ||
14 | #include "sysdep.h" | ||
15 | #include "wait.h" | ||
16 | #include "user_util.h" | ||
17 | #include "ptrace_user.h" | ||
18 | #include "sysdep/ptrace.h" | ||
19 | #include "sysdep/sigcontext.h" | ||
20 | |||
21 | int proxy_wait_return(struct debugger *debugger, pid_t unused) | ||
22 | { | ||
23 | debugger->waiting = 0; | ||
24 | |||
25 | if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){ | ||
26 | debugger_cancelled_return(debugger, -ECHILD); | ||
27 | return(0); | ||
28 | } | ||
29 | |||
30 | if(debugger->debugee->zombie && debugger->debugee->event) | ||
31 | debugger->debugee->died = 1; | ||
32 | |||
33 | if(debugger->debugee->event){ | ||
34 | debugger->debugee->event = 0; | ||
35 | ptrace(PTRACE_POKEDATA, debugger->pid, | ||
36 | debugger->wait_status_ptr, | ||
37 | debugger->debugee->wait_status); | ||
38 | /* if (wait4) | ||
39 | ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ | ||
40 | debugger_cancelled_return(debugger, debugger->debugee->pid); | ||
41 | return(0); | ||
42 | } | ||
43 | |||
44 | /* pause will return -EINTR, which happens to be right for wait */ | ||
45 | debugger_normal_return(debugger, -1); | ||
46 | return(0); | ||
47 | } | ||
48 | |||
49 | int parent_wait_return(struct debugger *debugger, pid_t unused) | ||
50 | { | ||
51 | return(debugger_normal_return(debugger, -1)); | ||
52 | } | ||
53 | |||
54 | int real_wait_return(struct debugger *debugger) | ||
55 | { | ||
56 | unsigned long ip; | ||
57 | int pid; | ||
58 | |||
59 | pid = debugger->pid; | ||
60 | |||
61 | ip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0); | ||
62 | IP_RESTART_SYSCALL(ip); | ||
63 | |||
64 | if(ptrace(PTRACE_POKEUSR, pid, PT_IP_OFFSET, ip) < 0) | ||
65 | tracer_panic("real_wait_return : Failed to restart system " | ||
66 | "call, errno = %d\n", errno); | ||
67 | |||
68 | if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) || | ||
69 | (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || | ||
70 | (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || | ||
71 | debugger_normal_return(debugger, -1)) | ||
72 | tracer_panic("real_wait_return : gdb failed to wait, " | ||
73 | "errno = %d\n", errno); | ||
74 | return(0); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
79 | * Emacs will notice this stuff at the end of the file and automatically | ||
80 | * adjust the settings for this buffer only. This must remain at the end | ||
81 | * of the file. | ||
82 | * --------------------------------------------------------------------------- | ||
83 | * Local variables: | ||
84 | * c-file-style: "linux" | ||
85 | * End: | ||
86 | */ | ||
diff --git a/arch/um/kernel/tt/ptproxy/wait.h b/arch/um/kernel/tt/ptproxy/wait.h new file mode 100644 index 000000000000..542e73ee2cee --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/wait.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /********************************************************************** | ||
2 | wait.h | ||
3 | |||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | ||
5 | terms and conditions. | ||
6 | **********************************************************************/ | ||
7 | |||
8 | #ifndef __PTPROXY_WAIT_H | ||
9 | #define __PTPROXY_WAIT_H | ||
10 | |||
11 | extern int proxy_wait_return(struct debugger *debugger, pid_t unused); | ||
12 | extern int real_wait_return(struct debugger *debugger); | ||
13 | extern int parent_wait_return(struct debugger *debugger, pid_t unused); | ||
14 | |||
15 | #endif | ||
diff --git a/arch/um/kernel/tt/syscall_kern.c b/arch/um/kernel/tt/syscall_kern.c new file mode 100644 index 000000000000..2650a628719e --- /dev/null +++ b/arch/um/kernel/tt/syscall_kern.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/types.h" | ||
7 | #include "linux/utime.h" | ||
8 | #include "linux/sys.h" | ||
9 | #include "linux/ptrace.h" | ||
10 | #include "asm/unistd.h" | ||
11 | #include "asm/ptrace.h" | ||
12 | #include "asm/uaccess.h" | ||
13 | #include "asm/stat.h" | ||
14 | #include "sysdep/syscalls.h" | ||
15 | #include "kern_util.h" | ||
16 | |||
17 | extern syscall_handler_t *sys_call_table[]; | ||
18 | |||
19 | long execute_syscall_tt(void *r) | ||
20 | { | ||
21 | struct pt_regs *regs = r; | ||
22 | long res; | ||
23 | int syscall; | ||
24 | |||
25 | #ifdef CONFIG_SYSCALL_DEBUG | ||
26 | current->thread.nsyscalls++; | ||
27 | nsyscalls++; | ||
28 | #endif | ||
29 | syscall = UPT_SYSCALL_NR(®s->regs); | ||
30 | |||
31 | if((syscall >= NR_syscalls) || (syscall < 0)) | ||
32 | res = -ENOSYS; | ||
33 | else res = EXECUTE_SYSCALL(syscall, regs); | ||
34 | |||
35 | return(res); | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
40 | * Emacs will notice this stuff at the end of the file and automatically | ||
41 | * adjust the settings for this buffer only. This must remain at the end | ||
42 | * of the file. | ||
43 | * --------------------------------------------------------------------------- | ||
44 | * Local variables: | ||
45 | * c-file-style: "linux" | ||
46 | * End: | ||
47 | */ | ||
diff --git a/arch/um/kernel/tt/syscall_user.c b/arch/um/kernel/tt/syscall_user.c new file mode 100644 index 000000000000..e4e7e9c2224c --- /dev/null +++ b/arch/um/kernel/tt/syscall_user.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <signal.h> | ||
8 | #include <errno.h> | ||
9 | #include <asm/unistd.h> | ||
10 | #include "sysdep/ptrace.h" | ||
11 | #include "sigcontext.h" | ||
12 | #include "ptrace_user.h" | ||
13 | #include "task.h" | ||
14 | #include "user_util.h" | ||
15 | #include "kern_util.h" | ||
16 | #include "syscall_user.h" | ||
17 | #include "tt.h" | ||
18 | |||
19 | |||
20 | void syscall_handler_tt(int sig, union uml_pt_regs *regs) | ||
21 | { | ||
22 | void *sc; | ||
23 | long result; | ||
24 | int syscall; | ||
25 | #ifdef UML_CONFIG_DEBUG_SYSCALL | ||
26 | int index; | ||
27 | #endif | ||
28 | |||
29 | syscall = UPT_SYSCALL_NR(regs); | ||
30 | sc = UPT_SC(regs); | ||
31 | SC_START_SYSCALL(sc); | ||
32 | |||
33 | #ifdef UML_CONFIG_DEBUG_SYSCALL | ||
34 | index = record_syscall_start(syscall); | ||
35 | #endif | ||
36 | syscall_trace(regs, 0); | ||
37 | result = execute_syscall_tt(regs); | ||
38 | |||
39 | /* regs->sc may have changed while the system call ran (there may | ||
40 | * have been an interrupt or segfault), so it needs to be refreshed. | ||
41 | */ | ||
42 | UPT_SC(regs) = sc; | ||
43 | |||
44 | SC_SET_SYSCALL_RETURN(sc, result); | ||
45 | |||
46 | syscall_trace(regs, 1); | ||
47 | #ifdef UML_CONFIG_DEBUG_SYSCALL | ||
48 | record_syscall_end(index, result); | ||
49 | #endif | ||
50 | } | ||
51 | |||
52 | void do_sigtrap(void *task) | ||
53 | { | ||
54 | UPT_SYSCALL_NR(TASK_REGS(task)) = -1; | ||
55 | } | ||
56 | |||
57 | void do_syscall(void *task, int pid, int local_using_sysemu) | ||
58 | { | ||
59 | unsigned long proc_regs[FRAME_SIZE]; | ||
60 | |||
61 | if(ptrace_getregs(pid, proc_regs) < 0) | ||
62 | tracer_panic("Couldn't read registers"); | ||
63 | |||
64 | UPT_SYSCALL_NR(TASK_REGS(task)) = PT_SYSCALL_NR(proc_regs); | ||
65 | |||
66 | if(((unsigned long *) PT_IP(proc_regs) >= &_stext) && | ||
67 | ((unsigned long *) PT_IP(proc_regs) <= &_etext)) | ||
68 | tracer_panic("I'm tracing myself and I can't get out"); | ||
69 | |||
70 | /* advanced sysemu mode set syscall number to -1 automatically */ | ||
71 | if (local_using_sysemu==2) | ||
72 | return; | ||
73 | |||
74 | /* syscall number -1 in sysemu skips syscall restarting in host */ | ||
75 | if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, | ||
76 | local_using_sysemu ? -1 : __NR_getpid) < 0) | ||
77 | tracer_panic("do_syscall : Nullifying syscall failed, " | ||
78 | "errno = %d", errno); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
83 | * Emacs will notice this stuff at the end of the file and automatically | ||
84 | * adjust the settings for this buffer only. This must remain at the end | ||
85 | * of the file. | ||
86 | * --------------------------------------------------------------------------- | ||
87 | * Local variables: | ||
88 | * c-file-style: "linux" | ||
89 | * End: | ||
90 | */ | ||
diff --git a/arch/um/kernel/tt/time.c b/arch/um/kernel/tt/time.c new file mode 100644 index 000000000000..8565b71b07cd --- /dev/null +++ b/arch/um/kernel/tt/time.c | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <signal.h> | ||
7 | #include <sys/time.h> | ||
8 | #include <time_user.h> | ||
9 | #include "process.h" | ||
10 | #include "user.h" | ||
11 | |||
12 | void user_time_init_tt(void) | ||
13 | { | ||
14 | if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR) | ||
15 | panic("Couldn't set SIGVTALRM handler"); | ||
16 | set_interval(ITIMER_VIRTUAL); | ||
17 | } | ||
18 | |||
19 | /* | ||
20 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
21 | * Emacs will notice this stuff at the end of the file and automatically | ||
22 | * adjust the settings for this buffer only. This must remain at the end | ||
23 | * of the file. | ||
24 | * --------------------------------------------------------------------------- | ||
25 | * Local variables: | ||
26 | * c-file-style: "linux" | ||
27 | * End: | ||
28 | */ | ||
diff --git a/arch/um/kernel/tt/tlb.c b/arch/um/kernel/tt/tlb.c new file mode 100644 index 000000000000..203216ad86f1 --- /dev/null +++ b/arch/um/kernel/tt/tlb.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Copyright 2003 PathScale, Inc. | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include "linux/stddef.h" | ||
8 | #include "linux/kernel.h" | ||
9 | #include "linux/sched.h" | ||
10 | #include "linux/mm.h" | ||
11 | #include "asm/page.h" | ||
12 | #include "asm/pgtable.h" | ||
13 | #include "asm/uaccess.h" | ||
14 | #include "asm/tlbflush.h" | ||
15 | #include "user_util.h" | ||
16 | #include "mem_user.h" | ||
17 | #include "os.h" | ||
18 | #include "tlb.h" | ||
19 | |||
20 | static void do_ops(int unused, struct host_vm_op *ops, int last) | ||
21 | { | ||
22 | struct host_vm_op *op; | ||
23 | int i; | ||
24 | |||
25 | for(i = 0; i <= last; i++){ | ||
26 | op = &ops[i]; | ||
27 | switch(op->type){ | ||
28 | case MMAP: | ||
29 | os_map_memory((void *) op->u.mmap.addr, op->u.mmap.fd, | ||
30 | op->u.mmap.offset, op->u.mmap.len, | ||
31 | op->u.mmap.r, op->u.mmap.w, | ||
32 | op->u.mmap.x); | ||
33 | break; | ||
34 | case MUNMAP: | ||
35 | os_unmap_memory((void *) op->u.munmap.addr, | ||
36 | op->u.munmap.len); | ||
37 | break; | ||
38 | case MPROTECT: | ||
39 | protect_memory(op->u.mprotect.addr, op->u.munmap.len, | ||
40 | op->u.mprotect.r, op->u.mprotect.w, | ||
41 | op->u.mprotect.x, 1); | ||
42 | break; | ||
43 | default: | ||
44 | printk("Unknown op type %d in do_ops\n", op->type); | ||
45 | break; | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | static void fix_range(struct mm_struct *mm, unsigned long start_addr, | ||
51 | unsigned long end_addr, int force) | ||
52 | { | ||
53 | if((current->thread.mode.tt.extern_pid != -1) && | ||
54 | (current->thread.mode.tt.extern_pid != os_getpid())) | ||
55 | panic("fix_range fixing wrong address space, current = 0x%p", | ||
56 | current); | ||
57 | |||
58 | fix_range_common(mm, start_addr, end_addr, force, 0, do_ops); | ||
59 | } | ||
60 | |||
61 | atomic_t vmchange_seq = ATOMIC_INIT(1); | ||
62 | |||
63 | void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end) | ||
64 | { | ||
65 | if(flush_tlb_kernel_range_common(start, end)) | ||
66 | atomic_inc(&vmchange_seq); | ||
67 | } | ||
68 | |||
69 | static void protect_vm_page(unsigned long addr, int w, int must_succeed) | ||
70 | { | ||
71 | int err; | ||
72 | |||
73 | err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed); | ||
74 | if(err == 0) return; | ||
75 | else if((err == -EFAULT) || (err == -ENOMEM)){ | ||
76 | flush_tlb_kernel_range(addr, addr + PAGE_SIZE); | ||
77 | protect_vm_page(addr, w, 1); | ||
78 | } | ||
79 | else panic("protect_vm_page : protect failed, errno = %d\n", err); | ||
80 | } | ||
81 | |||
82 | void mprotect_kernel_vm(int w) | ||
83 | { | ||
84 | struct mm_struct *mm; | ||
85 | pgd_t *pgd; | ||
86 | pud_t *pud; | ||
87 | pmd_t *pmd; | ||
88 | pte_t *pte; | ||
89 | unsigned long addr; | ||
90 | |||
91 | mm = &init_mm; | ||
92 | for(addr = start_vm; addr < end_vm;){ | ||
93 | pgd = pgd_offset(mm, addr); | ||
94 | pud = pud_offset(pgd, addr); | ||
95 | pmd = pmd_offset(pud, addr); | ||
96 | if(pmd_present(*pmd)){ | ||
97 | pte = pte_offset_kernel(pmd, addr); | ||
98 | if(pte_present(*pte)) protect_vm_page(addr, w, 0); | ||
99 | addr += PAGE_SIZE; | ||
100 | } | ||
101 | else addr += PMD_SIZE; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void flush_tlb_kernel_vm_tt(void) | ||
106 | { | ||
107 | flush_tlb_kernel_range(start_vm, end_vm); | ||
108 | } | ||
109 | |||
110 | void __flush_tlb_one_tt(unsigned long addr) | ||
111 | { | ||
112 | flush_tlb_kernel_range(addr, addr + PAGE_SIZE); | ||
113 | } | ||
114 | |||
115 | void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start, | ||
116 | unsigned long end) | ||
117 | { | ||
118 | if(vma->vm_mm != current->mm) return; | ||
119 | |||
120 | /* Assumes that the range start ... end is entirely within | ||
121 | * either process memory or kernel vm | ||
122 | */ | ||
123 | if((start >= start_vm) && (start < end_vm)){ | ||
124 | if(flush_tlb_kernel_range_common(start, end)) | ||
125 | atomic_inc(&vmchange_seq); | ||
126 | } | ||
127 | else fix_range(vma->vm_mm, start, end, 0); | ||
128 | } | ||
129 | |||
130 | void flush_tlb_mm_tt(struct mm_struct *mm) | ||
131 | { | ||
132 | unsigned long seq; | ||
133 | |||
134 | if(mm != current->mm) return; | ||
135 | |||
136 | fix_range(mm, 0, STACK_TOP, 0); | ||
137 | |||
138 | seq = atomic_read(&vmchange_seq); | ||
139 | if(current->thread.mode.tt.vm_seq == seq) | ||
140 | return; | ||
141 | current->thread.mode.tt.vm_seq = seq; | ||
142 | flush_tlb_kernel_range_common(start_vm, end_vm); | ||
143 | } | ||
144 | |||
145 | void force_flush_all_tt(void) | ||
146 | { | ||
147 | fix_range(current->mm, 0, STACK_TOP, 1); | ||
148 | flush_tlb_kernel_range_common(start_vm, end_vm); | ||
149 | } | ||
diff --git a/arch/um/kernel/tt/tracer.c b/arch/um/kernel/tt/tracer.c new file mode 100644 index 000000000000..7b5d937e5955 --- /dev/null +++ b/arch/um/kernel/tt/tracer.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <stdarg.h> | ||
9 | #include <unistd.h> | ||
10 | #include <signal.h> | ||
11 | #include <errno.h> | ||
12 | #include <sched.h> | ||
13 | #include <string.h> | ||
14 | #include <sys/mman.h> | ||
15 | #include <sys/time.h> | ||
16 | #include <sys/wait.h> | ||
17 | #include "user.h" | ||
18 | #include "sysdep/ptrace.h" | ||
19 | #include "sigcontext.h" | ||
20 | #include "sysdep/sigcontext.h" | ||
21 | #include "os.h" | ||
22 | #include "signal_user.h" | ||
23 | #include "user_util.h" | ||
24 | #include "mem_user.h" | ||
25 | #include "process.h" | ||
26 | #include "kern_util.h" | ||
27 | #include "chan_user.h" | ||
28 | #include "ptrace_user.h" | ||
29 | #include "mode.h" | ||
30 | #include "tt.h" | ||
31 | |||
32 | static int tracer_winch[2]; | ||
33 | |||
34 | int is_tracer_winch(int pid, int fd, void *data) | ||
35 | { | ||
36 | if(pid != tracing_pid) | ||
37 | return(0); | ||
38 | |||
39 | register_winch_irq(tracer_winch[0], fd, -1, data); | ||
40 | return(1); | ||
41 | } | ||
42 | |||
43 | static void tracer_winch_handler(int sig) | ||
44 | { | ||
45 | int n; | ||
46 | char c = 1; | ||
47 | |||
48 | n = os_write_file(tracer_winch[1], &c, sizeof(c)); | ||
49 | if(n != sizeof(c)) | ||
50 | printk("tracer_winch_handler - write failed, err = %d\n", -n); | ||
51 | } | ||
52 | |||
53 | /* Called only by the tracing thread during initialization */ | ||
54 | |||
55 | static void setup_tracer_winch(void) | ||
56 | { | ||
57 | int err; | ||
58 | |||
59 | err = os_pipe(tracer_winch, 1, 1); | ||
60 | if(err < 0){ | ||
61 | printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err); | ||
62 | return; | ||
63 | } | ||
64 | signal(SIGWINCH, tracer_winch_handler); | ||
65 | } | ||
66 | |||
67 | void attach_process(int pid) | ||
68 | { | ||
69 | if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) || | ||
70 | (ptrace(PTRACE_CONT, pid, 0, 0) < 0)) | ||
71 | tracer_panic("OP_FORK failed to attach pid"); | ||
72 | wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL); | ||
73 | if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) | ||
74 | tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno); | ||
75 | if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) | ||
76 | tracer_panic("OP_FORK failed to continue process"); | ||
77 | } | ||
78 | |||
79 | void tracer_panic(char *format, ...) | ||
80 | { | ||
81 | va_list ap; | ||
82 | |||
83 | va_start(ap, format); | ||
84 | vprintf(format, ap); | ||
85 | va_end(ap); | ||
86 | printf("\n"); | ||
87 | while(1) pause(); | ||
88 | } | ||
89 | |||
90 | static void tracer_segv(int sig, struct sigcontext sc) | ||
91 | { | ||
92 | printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n", | ||
93 | SC_FAULT_ADDR(&sc), SC_IP(&sc)); | ||
94 | while(1) | ||
95 | pause(); | ||
96 | } | ||
97 | |||
98 | /* Changed early in boot, and then only read */ | ||
99 | int debug = 0; | ||
100 | int debug_stop = 1; | ||
101 | int debug_parent = 0; | ||
102 | int honeypot = 0; | ||
103 | |||
104 | static int signal_tramp(void *arg) | ||
105 | { | ||
106 | int (*proc)(void *); | ||
107 | |||
108 | if(honeypot && munmap((void *) (host_task_size - 0x10000000), | ||
109 | 0x10000000)) | ||
110 | panic("Unmapping stack failed"); | ||
111 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) | ||
112 | panic("ptrace PTRACE_TRACEME failed"); | ||
113 | os_stop_process(os_getpid()); | ||
114 | change_sig(SIGWINCH, 0); | ||
115 | signal(SIGUSR1, SIG_IGN); | ||
116 | change_sig(SIGCHLD, 0); | ||
117 | signal(SIGSEGV, (__sighandler_t) sig_handler); | ||
118 | set_cmdline("(idle thread)"); | ||
119 | set_init_pid(os_getpid()); | ||
120 | proc = arg; | ||
121 | return((*proc)(NULL)); | ||
122 | } | ||
123 | |||
124 | static void sleeping_process_signal(int pid, int sig) | ||
125 | { | ||
126 | switch(sig){ | ||
127 | /* These two result from UML being ^Z-ed and bg-ed. PTRACE_CONT is | ||
128 | * right because the process must be in the kernel already. | ||
129 | */ | ||
130 | case SIGCONT: | ||
131 | case SIGTSTP: | ||
132 | if(ptrace(PTRACE_CONT, pid, 0, sig) < 0) | ||
133 | tracer_panic("sleeping_process_signal : Failed to " | ||
134 | "continue pid %d, signal = %d, " | ||
135 | "errno = %d\n", pid, sig, errno); | ||
136 | break; | ||
137 | |||
138 | /* This happens when the debugger (e.g. strace) is doing system call | ||
139 | * tracing on the kernel. During a context switch, the current task | ||
140 | * will be set to the incoming process and the outgoing process will | ||
141 | * hop into write and then read. Since it's not the current process | ||
142 | * any more, the trace of those will land here. So, we need to just | ||
143 | * PTRACE_SYSCALL it. | ||
144 | */ | ||
145 | case (SIGTRAP + 0x80): | ||
146 | if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) | ||
147 | tracer_panic("sleeping_process_signal : Failed to " | ||
148 | "PTRACE_SYSCALL pid %d, errno = %d\n", | ||
149 | pid, errno); | ||
150 | break; | ||
151 | case SIGSTOP: | ||
152 | break; | ||
153 | default: | ||
154 | tracer_panic("sleeping process %d got unexpected " | ||
155 | "signal : %d\n", pid, sig); | ||
156 | break; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /* Accessed only by the tracing thread */ | ||
161 | int debugger_pid = -1; | ||
162 | int debugger_parent = -1; | ||
163 | int debugger_fd = -1; | ||
164 | int gdb_pid = -1; | ||
165 | |||
166 | struct { | ||
167 | int pid; | ||
168 | int signal; | ||
169 | unsigned long addr; | ||
170 | struct timeval time; | ||
171 | } signal_record[1024][32]; | ||
172 | |||
173 | int signal_index[32]; | ||
174 | int nsignals = 0; | ||
175 | int debug_trace = 0; | ||
176 | extern int io_nsignals, io_count, intr_count; | ||
177 | |||
178 | extern void signal_usr1(int sig); | ||
179 | |||
180 | int tracing_pid = -1; | ||
181 | |||
182 | int tracer(int (*init_proc)(void *), void *sp) | ||
183 | { | ||
184 | void *task = NULL; | ||
185 | int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0; | ||
186 | int proc_id = 0, n, err, old_tracing = 0, strace = 0; | ||
187 | int local_using_sysemu = 0; | ||
188 | #ifdef UML_CONFIG_SYSCALL_DEBUG | ||
189 | unsigned long eip = 0; | ||
190 | int last_index; | ||
191 | #endif | ||
192 | signal(SIGPIPE, SIG_IGN); | ||
193 | setup_tracer_winch(); | ||
194 | tracing_pid = os_getpid(); | ||
195 | printf("tracing thread pid = %d\n", tracing_pid); | ||
196 | |||
197 | pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); | ||
198 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | ||
199 | if(n < 0){ | ||
200 | printf("waitpid on idle thread failed, errno = %d\n", errno); | ||
201 | exit(1); | ||
202 | } | ||
203 | if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) { | ||
204 | printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno); | ||
205 | exit(1); | ||
206 | } | ||
207 | if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){ | ||
208 | printf("Failed to continue idle thread, errno = %d\n", errno); | ||
209 | exit(1); | ||
210 | } | ||
211 | |||
212 | signal(SIGSEGV, (sighandler_t) tracer_segv); | ||
213 | signal(SIGUSR1, signal_usr1); | ||
214 | if(debug_trace){ | ||
215 | printf("Tracing thread pausing to be attached\n"); | ||
216 | stop(); | ||
217 | } | ||
218 | if(debug){ | ||
219 | if(gdb_pid != -1) | ||
220 | debugger_pid = attach_debugger(pid, gdb_pid, 1); | ||
221 | else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop); | ||
222 | if(debug_parent){ | ||
223 | debugger_parent = os_process_parent(debugger_pid); | ||
224 | init_parent_proxy(debugger_parent); | ||
225 | err = attach(debugger_parent); | ||
226 | if(err){ | ||
227 | printf("Failed to attach debugger parent %d, " | ||
228 | "errno = %d\n", debugger_parent, -err); | ||
229 | debugger_parent = -1; | ||
230 | } | ||
231 | else { | ||
232 | if(ptrace(PTRACE_SYSCALL, debugger_parent, | ||
233 | 0, 0) < 0){ | ||
234 | printf("Failed to continue debugger " | ||
235 | "parent, errno = %d\n", errno); | ||
236 | debugger_parent = -1; | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | set_cmdline("(tracing thread)"); | ||
242 | while(1){ | ||
243 | CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED)); | ||
244 | if(pid <= 0){ | ||
245 | if(errno != ECHILD){ | ||
246 | printf("wait failed - errno = %d\n", errno); | ||
247 | } | ||
248 | continue; | ||
249 | } | ||
250 | if(pid == debugger_pid){ | ||
251 | int cont = 0; | ||
252 | |||
253 | if(WIFEXITED(status) || WIFSIGNALED(status)) | ||
254 | debugger_pid = -1; | ||
255 | /* XXX Figure out how to deal with gdb and SMP */ | ||
256 | else cont = debugger_signal(status, cpu_tasks[0].pid); | ||
257 | if(cont == PTRACE_SYSCALL) strace = 1; | ||
258 | continue; | ||
259 | } | ||
260 | else if(pid == debugger_parent){ | ||
261 | debugger_parent_signal(status, pid); | ||
262 | continue; | ||
263 | } | ||
264 | nsignals++; | ||
265 | if(WIFEXITED(status)) ; | ||
266 | #ifdef notdef | ||
267 | { | ||
268 | printf("Child %d exited with status %d\n", pid, | ||
269 | WEXITSTATUS(status)); | ||
270 | } | ||
271 | #endif | ||
272 | else if(WIFSIGNALED(status)){ | ||
273 | sig = WTERMSIG(status); | ||
274 | if(sig != 9){ | ||
275 | printf("Child %d exited with signal %d\n", pid, | ||
276 | sig); | ||
277 | } | ||
278 | } | ||
279 | else if(WIFSTOPPED(status)){ | ||
280 | proc_id = pid_to_processor_id(pid); | ||
281 | sig = WSTOPSIG(status); | ||
282 | #ifdef UML_CONFIG_SYSCALL_DEBUG | ||
283 | if(signal_index[proc_id] == 1024){ | ||
284 | signal_index[proc_id] = 0; | ||
285 | last_index = 1023; | ||
286 | } | ||
287 | else last_index = signal_index[proc_id] - 1; | ||
288 | if(((sig == SIGPROF) || (sig == SIGVTALRM) || | ||
289 | (sig == SIGALRM)) && | ||
290 | (signal_record[proc_id][last_index].signal == sig)&& | ||
291 | (signal_record[proc_id][last_index].pid == pid)) | ||
292 | signal_index[proc_id] = last_index; | ||
293 | signal_record[proc_id][signal_index[proc_id]].pid = pid; | ||
294 | gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL); | ||
295 | eip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0); | ||
296 | signal_record[proc_id][signal_index[proc_id]].addr = eip; | ||
297 | signal_record[proc_id][signal_index[proc_id]++].signal = sig; | ||
298 | #endif | ||
299 | if(proc_id == -1){ | ||
300 | sleeping_process_signal(pid, sig); | ||
301 | continue; | ||
302 | } | ||
303 | |||
304 | task = cpu_tasks[proc_id].task; | ||
305 | tracing = is_tracing(task); | ||
306 | old_tracing = tracing; | ||
307 | |||
308 | /* Assume: no syscall, when coming from user */ | ||
309 | if ( tracing ) | ||
310 | do_sigtrap(task); | ||
311 | |||
312 | switch(sig){ | ||
313 | case SIGUSR1: | ||
314 | sig = 0; | ||
315 | op = do_proc_op(task, proc_id); | ||
316 | switch(op){ | ||
317 | /* | ||
318 | * This is called when entering user mode; after | ||
319 | * this, we start intercepting syscalls. | ||
320 | * | ||
321 | * In fact, a process is started in kernel mode, | ||
322 | * so with is_tracing() == 0 (and that is reset | ||
323 | * when executing syscalls, since UML kernel has | ||
324 | * the right to do syscalls); | ||
325 | */ | ||
326 | case OP_TRACE_ON: | ||
327 | arch_leave_kernel(task, pid); | ||
328 | tracing = 1; | ||
329 | break; | ||
330 | case OP_REBOOT: | ||
331 | case OP_HALT: | ||
332 | unmap_physmem(); | ||
333 | kmalloc_ok = 0; | ||
334 | os_kill_ptraced_process(pid, 0); | ||
335 | /* Now let's reap remaining zombies */ | ||
336 | errno = 0; | ||
337 | do { | ||
338 | waitpid(-1, &status, | ||
339 | WUNTRACED); | ||
340 | } while (errno != ECHILD); | ||
341 | return(op == OP_REBOOT); | ||
342 | case OP_NONE: | ||
343 | printf("Detaching pid %d\n", pid); | ||
344 | detach(pid, SIGSTOP); | ||
345 | continue; | ||
346 | default: | ||
347 | break; | ||
348 | } | ||
349 | /* OP_EXEC switches host processes on us, | ||
350 | * we want to continue the new one. | ||
351 | */ | ||
352 | pid = cpu_tasks[proc_id].pid; | ||
353 | break; | ||
354 | case (SIGTRAP + 0x80): | ||
355 | if(!tracing && (debugger_pid != -1)){ | ||
356 | child_signal(pid, status & 0x7fff); | ||
357 | continue; | ||
358 | } | ||
359 | tracing = 0; | ||
360 | /* local_using_sysemu has been already set | ||
361 | * below, since if we are here, is_tracing() on | ||
362 | * the traced task was 1, i.e. the process had | ||
363 | * already run through one iteration of the | ||
364 | * loop which executed a OP_TRACE_ON request.*/ | ||
365 | do_syscall(task, pid, local_using_sysemu); | ||
366 | sig = SIGUSR2; | ||
367 | break; | ||
368 | case SIGTRAP: | ||
369 | if(!tracing && (debugger_pid != -1)){ | ||
370 | child_signal(pid, status); | ||
371 | continue; | ||
372 | } | ||
373 | tracing = 0; | ||
374 | break; | ||
375 | case SIGPROF: | ||
376 | if(tracing) sig = 0; | ||
377 | break; | ||
378 | case SIGCHLD: | ||
379 | case SIGHUP: | ||
380 | sig = 0; | ||
381 | break; | ||
382 | case SIGSEGV: | ||
383 | case SIGIO: | ||
384 | case SIGALRM: | ||
385 | case SIGVTALRM: | ||
386 | case SIGFPE: | ||
387 | case SIGBUS: | ||
388 | case SIGILL: | ||
389 | case SIGWINCH: | ||
390 | |||
391 | default: | ||
392 | tracing = 0; | ||
393 | break; | ||
394 | } | ||
395 | set_tracing(task, tracing); | ||
396 | |||
397 | if(!tracing && old_tracing) | ||
398 | arch_enter_kernel(task, pid); | ||
399 | |||
400 | if(!tracing && (debugger_pid != -1) && (sig != 0) && | ||
401 | (sig != SIGALRM) && (sig != SIGVTALRM) && | ||
402 | (sig != SIGSEGV) && (sig != SIGTRAP) && | ||
403 | (sig != SIGUSR2) && (sig != SIGIO) && | ||
404 | (sig != SIGFPE)){ | ||
405 | child_signal(pid, status); | ||
406 | continue; | ||
407 | } | ||
408 | |||
409 | local_using_sysemu = get_using_sysemu(); | ||
410 | |||
411 | if(tracing) | ||
412 | cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu, | ||
413 | singlestepping(task)); | ||
414 | else if((debugger_pid != -1) && strace) | ||
415 | cont_type = PTRACE_SYSCALL; | ||
416 | else | ||
417 | cont_type = PTRACE_CONT; | ||
418 | |||
419 | if(ptrace(cont_type, pid, 0, sig) != 0){ | ||
420 | tracer_panic("ptrace failed to continue " | ||
421 | "process - errno = %d\n", | ||
422 | errno); | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | return(0); | ||
427 | } | ||
428 | |||
429 | static int __init uml_debug_setup(char *line, int *add) | ||
430 | { | ||
431 | char *next; | ||
432 | |||
433 | debug = 1; | ||
434 | *add = 0; | ||
435 | if(*line != '=') return(0); | ||
436 | line++; | ||
437 | |||
438 | while(line != NULL){ | ||
439 | next = strchr(line, ','); | ||
440 | if(next) *next++ = '\0'; | ||
441 | |||
442 | if(!strcmp(line, "go")) debug_stop = 0; | ||
443 | else if(!strcmp(line, "parent")) debug_parent = 1; | ||
444 | else printf("Unknown debug option : '%s'\n", line); | ||
445 | |||
446 | line = next; | ||
447 | } | ||
448 | return(0); | ||
449 | } | ||
450 | |||
451 | __uml_setup("debug", uml_debug_setup, | ||
452 | "debug\n" | ||
453 | " Starts up the kernel under the control of gdb. See the \n" | ||
454 | " kernel debugging tutorial and the debugging session pages\n" | ||
455 | " at http://user-mode-linux.sourceforge.net/ for more information.\n\n" | ||
456 | ); | ||
457 | |||
458 | static int __init uml_debugtrace_setup(char *line, int *add) | ||
459 | { | ||
460 | debug_trace = 1; | ||
461 | return 0; | ||
462 | } | ||
463 | __uml_setup("debugtrace", uml_debugtrace_setup, | ||
464 | "debugtrace\n" | ||
465 | " Causes the tracing thread to pause until it is attached by a\n" | ||
466 | " debugger and continued. This is mostly for debugging crashes\n" | ||
467 | " early during boot, and should be pretty much obsoleted by\n" | ||
468 | " the debug switch.\n\n" | ||
469 | ); | ||
470 | |||
471 | /* | ||
472 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
473 | * Emacs will notice this stuff at the end of the file and automatically | ||
474 | * adjust the settings for this buffer only. This must remain at the end | ||
475 | * of the file. | ||
476 | * --------------------------------------------------------------------------- | ||
477 | * Local variables: | ||
478 | * c-file-style: "linux" | ||
479 | * End: | ||
480 | */ | ||
diff --git a/arch/um/kernel/tt/trap_user.c b/arch/um/kernel/tt/trap_user.c new file mode 100644 index 000000000000..92a3820ca543 --- /dev/null +++ b/arch/um/kernel/tt/trap_user.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <errno.h> | ||
8 | #include <signal.h> | ||
9 | #include "sysdep/ptrace.h" | ||
10 | #include "signal_user.h" | ||
11 | #include "user_util.h" | ||
12 | #include "kern_util.h" | ||
13 | #include "task.h" | ||
14 | #include "tt.h" | ||
15 | |||
16 | void sig_handler_common_tt(int sig, void *sc_ptr) | ||
17 | { | ||
18 | struct sigcontext *sc = sc_ptr; | ||
19 | struct tt_regs save_regs, *r; | ||
20 | struct signal_info *info; | ||
21 | int save_errno = errno, is_user; | ||
22 | |||
23 | /* This is done because to allow SIGSEGV to be delivered inside a SEGV | ||
24 | * handler. This can happen in copy_user, and if SEGV is disabled, | ||
25 | * the process will die. | ||
26 | */ | ||
27 | if(sig == SIGSEGV) | ||
28 | change_sig(SIGSEGV, 1); | ||
29 | |||
30 | r = &TASK_REGS(get_current())->tt; | ||
31 | save_regs = *r; | ||
32 | is_user = user_context(SC_SP(sc)); | ||
33 | r->sc = sc; | ||
34 | if(sig != SIGUSR2) | ||
35 | r->syscall = -1; | ||
36 | |||
37 | info = &sig_info[sig]; | ||
38 | if(!info->is_irq) unblock_signals(); | ||
39 | |||
40 | (*info->handler)(sig, (union uml_pt_regs *) r); | ||
41 | |||
42 | if(is_user){ | ||
43 | interrupt_end(); | ||
44 | block_signals(); | ||
45 | set_user_mode(NULL); | ||
46 | } | ||
47 | *r = save_regs; | ||
48 | errno = save_errno; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
53 | * Emacs will notice this stuff at the end of the file and automatically | ||
54 | * adjust the settings for this buffer only. This must remain at the end | ||
55 | * of the file. | ||
56 | * --------------------------------------------------------------------------- | ||
57 | * Local variables: | ||
58 | * c-file-style: "linux" | ||
59 | * End: | ||
60 | */ | ||
diff --git a/arch/um/kernel/tt/uaccess.c b/arch/um/kernel/tt/uaccess.c new file mode 100644 index 000000000000..a72aa632972f --- /dev/null +++ b/arch/um/kernel/tt/uaccess.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "asm/uaccess.h" | ||
8 | |||
9 | int copy_from_user_tt(void *to, const void __user *from, int n) | ||
10 | { | ||
11 | if(!access_ok_tt(VERIFY_READ, from, n)) | ||
12 | return(n); | ||
13 | |||
14 | return(__do_copy_from_user(to, from, n, ¤t->thread.fault_addr, | ||
15 | ¤t->thread.fault_catcher)); | ||
16 | } | ||
17 | |||
18 | int copy_to_user_tt(void __user *to, const void *from, int n) | ||
19 | { | ||
20 | if(!access_ok_tt(VERIFY_WRITE, to, n)) | ||
21 | return(n); | ||
22 | |||
23 | return(__do_copy_to_user(to, from, n, ¤t->thread.fault_addr, | ||
24 | ¤t->thread.fault_catcher)); | ||
25 | } | ||
26 | |||
27 | int strncpy_from_user_tt(char *dst, const char __user *src, int count) | ||
28 | { | ||
29 | int n; | ||
30 | |||
31 | if(!access_ok_tt(VERIFY_READ, src, 1)) | ||
32 | return(-EFAULT); | ||
33 | |||
34 | n = __do_strncpy_from_user(dst, src, count, | ||
35 | ¤t->thread.fault_addr, | ||
36 | ¤t->thread.fault_catcher); | ||
37 | if(n < 0) return(-EFAULT); | ||
38 | return(n); | ||
39 | } | ||
40 | |||
41 | int __clear_user_tt(void __user *mem, int len) | ||
42 | { | ||
43 | return(__do_clear_user(mem, len, | ||
44 | ¤t->thread.fault_addr, | ||
45 | ¤t->thread.fault_catcher)); | ||
46 | } | ||
47 | |||
48 | int clear_user_tt(void __user *mem, int len) | ||
49 | { | ||
50 | if(!access_ok_tt(VERIFY_WRITE, mem, len)) | ||
51 | return(len); | ||
52 | |||
53 | return(__do_clear_user(mem, len, ¤t->thread.fault_addr, | ||
54 | ¤t->thread.fault_catcher)); | ||
55 | } | ||
56 | |||
57 | int strnlen_user_tt(const void __user *str, int len) | ||
58 | { | ||
59 | return(__do_strnlen_user(str, len, | ||
60 | ¤t->thread.fault_addr, | ||
61 | ¤t->thread.fault_catcher)); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
66 | * Emacs will notice this stuff at the end of the file and automatically | ||
67 | * adjust the settings for this buffer only. This must remain at the end | ||
68 | * of the file. | ||
69 | * --------------------------------------------------------------------------- | ||
70 | * Local variables: | ||
71 | * c-file-style: "linux" | ||
72 | * End: | ||
73 | */ | ||
diff --git a/arch/um/kernel/tt/uaccess_user.c b/arch/um/kernel/tt/uaccess_user.c new file mode 100644 index 000000000000..f01475512ecb --- /dev/null +++ b/arch/um/kernel/tt/uaccess_user.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) | ||
3 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <setjmp.h> | ||
8 | #include <string.h> | ||
9 | #include "user_util.h" | ||
10 | #include "uml_uaccess.h" | ||
11 | #include "task.h" | ||
12 | #include "kern_util.h" | ||
13 | |||
14 | int __do_copy_from_user(void *to, const void *from, int n, | ||
15 | void **fault_addr, void **fault_catcher) | ||
16 | { | ||
17 | struct tt_regs save = TASK_REGS(get_current())->tt; | ||
18 | unsigned long fault; | ||
19 | int faulted; | ||
20 | |||
21 | fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, | ||
22 | __do_copy, &faulted); | ||
23 | TASK_REGS(get_current())->tt = save; | ||
24 | |||
25 | if(!faulted) return(0); | ||
26 | else return(n - (fault - (unsigned long) from)); | ||
27 | } | ||
28 | |||
29 | static void __do_strncpy(void *dst, const void *src, int count) | ||
30 | { | ||
31 | strncpy(dst, src, count); | ||
32 | } | ||
33 | |||
34 | int __do_strncpy_from_user(char *dst, const char *src, unsigned long count, | ||
35 | void **fault_addr, void **fault_catcher) | ||
36 | { | ||
37 | struct tt_regs save = TASK_REGS(get_current())->tt; | ||
38 | unsigned long fault; | ||
39 | int faulted; | ||
40 | |||
41 | fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher, | ||
42 | __do_strncpy, &faulted); | ||
43 | TASK_REGS(get_current())->tt = save; | ||
44 | |||
45 | if(!faulted) return(strlen(dst)); | ||
46 | else return(-1); | ||
47 | } | ||
48 | |||
49 | static void __do_clear(void *to, const void *from, int n) | ||
50 | { | ||
51 | memset(to, 0, n); | ||
52 | } | ||
53 | |||
54 | int __do_clear_user(void *mem, unsigned long len, | ||
55 | void **fault_addr, void **fault_catcher) | ||
56 | { | ||
57 | struct tt_regs save = TASK_REGS(get_current())->tt; | ||
58 | unsigned long fault; | ||
59 | int faulted; | ||
60 | |||
61 | fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher, | ||
62 | __do_clear, &faulted); | ||
63 | TASK_REGS(get_current())->tt = save; | ||
64 | |||
65 | if(!faulted) return(0); | ||
66 | else return(len - (fault - (unsigned long) mem)); | ||
67 | } | ||
68 | |||
69 | int __do_strnlen_user(const char *str, unsigned long n, | ||
70 | void **fault_addr, void **fault_catcher) | ||
71 | { | ||
72 | struct tt_regs save = TASK_REGS(get_current())->tt; | ||
73 | int ret; | ||
74 | unsigned long *faddrp = (unsigned long *)fault_addr; | ||
75 | sigjmp_buf jbuf; | ||
76 | |||
77 | *fault_catcher = &jbuf; | ||
78 | if(sigsetjmp(jbuf, 1) == 0) | ||
79 | ret = strlen(str) + 1; | ||
80 | else ret = *faddrp - (unsigned long) str; | ||
81 | |||
82 | *fault_addr = NULL; | ||
83 | *fault_catcher = NULL; | ||
84 | |||
85 | TASK_REGS(get_current())->tt = save; | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
91 | * Emacs will notice this stuff at the end of the file and automatically | ||
92 | * adjust the settings for this buffer only. This must remain at the end | ||
93 | * of the file. | ||
94 | * --------------------------------------------------------------------------- | ||
95 | * Local variables: | ||
96 | * c-file-style: "linux" | ||
97 | * End: | ||
98 | */ | ||
diff --git a/arch/um/kernel/tt/unmap.c b/arch/um/kernel/tt/unmap.c new file mode 100644 index 000000000000..3f7aecdbe532 --- /dev/null +++ b/arch/um/kernel/tt/unmap.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <sys/mman.h> | ||
7 | |||
8 | int switcheroo(int fd, int prot, void *from, void *to, int size) | ||
9 | { | ||
10 | if(munmap(to, size) < 0){ | ||
11 | return(-1); | ||
12 | } | ||
13 | if(mmap(to, size, prot, MAP_SHARED | MAP_FIXED, fd, 0) != to){ | ||
14 | return(-1); | ||
15 | } | ||
16 | if(munmap(from, size) < 0){ | ||
17 | return(-1); | ||
18 | } | ||
19 | return(0); | ||
20 | } | ||
21 | |||
22 | /* | ||
23 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
24 | * Emacs will notice this stuff at the end of the file and automatically | ||
25 | * adjust the settings for this buffer only. This must remain at the end | ||
26 | * of the file. | ||
27 | * --------------------------------------------------------------------------- | ||
28 | * Local variables: | ||
29 | * c-file-style: "linux" | ||
30 | * End: | ||
31 | */ | ||
diff --git a/arch/um/kernel/tty_log.c b/arch/um/kernel/tty_log.c new file mode 100644 index 000000000000..9ada656f68ce --- /dev/null +++ b/arch/um/kernel/tty_log.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and | ||
3 | * geoffrey hing <ghing@net.ohio-state.edu> | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <errno.h> | ||
8 | #include <string.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <unistd.h> | ||
12 | #include <sys/time.h> | ||
13 | #include "init.h" | ||
14 | #include "user.h" | ||
15 | #include "kern_util.h" | ||
16 | #include "os.h" | ||
17 | |||
18 | #define TTY_LOG_DIR "./" | ||
19 | |||
20 | /* Set early in boot and then unchanged */ | ||
21 | static char *tty_log_dir = TTY_LOG_DIR; | ||
22 | static int tty_log_fd = -1; | ||
23 | |||
24 | #define TTY_LOG_OPEN 1 | ||
25 | #define TTY_LOG_CLOSE 2 | ||
26 | #define TTY_LOG_WRITE 3 | ||
27 | #define TTY_LOG_EXEC 4 | ||
28 | |||
29 | #define TTY_READ 1 | ||
30 | #define TTY_WRITE 2 | ||
31 | |||
32 | struct tty_log_buf { | ||
33 | int what; | ||
34 | unsigned long tty; | ||
35 | int len; | ||
36 | int direction; | ||
37 | unsigned long sec; | ||
38 | unsigned long usec; | ||
39 | }; | ||
40 | |||
41 | int open_tty_log(void *tty, void *current_tty) | ||
42 | { | ||
43 | struct timeval tv; | ||
44 | struct tty_log_buf data; | ||
45 | char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")]; | ||
46 | int fd; | ||
47 | |||
48 | gettimeofday(&tv, NULL); | ||
49 | if(tty_log_fd != -1){ | ||
50 | data = ((struct tty_log_buf) { .what = TTY_LOG_OPEN, | ||
51 | .tty = (unsigned long) tty, | ||
52 | .len = sizeof(current_tty), | ||
53 | .direction = 0, | ||
54 | .sec = tv.tv_sec, | ||
55 | .usec = tv.tv_usec } ); | ||
56 | os_write_file(tty_log_fd, &data, sizeof(data)); | ||
57 | os_write_file(tty_log_fd, ¤t_tty, data.len); | ||
58 | return(tty_log_fd); | ||
59 | } | ||
60 | |||
61 | sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec, | ||
62 | (unsigned int) tv.tv_usec); | ||
63 | |||
64 | fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))), | ||
65 | 0644); | ||
66 | if(fd < 0){ | ||
67 | printk("open_tty_log : couldn't open '%s', errno = %d\n", | ||
68 | buf, -fd); | ||
69 | } | ||
70 | return(fd); | ||
71 | } | ||
72 | |||
73 | void close_tty_log(int fd, void *tty) | ||
74 | { | ||
75 | struct tty_log_buf data; | ||
76 | struct timeval tv; | ||
77 | |||
78 | if(tty_log_fd != -1){ | ||
79 | gettimeofday(&tv, NULL); | ||
80 | data = ((struct tty_log_buf) { .what = TTY_LOG_CLOSE, | ||
81 | .tty = (unsigned long) tty, | ||
82 | .len = 0, | ||
83 | .direction = 0, | ||
84 | .sec = tv.tv_sec, | ||
85 | .usec = tv.tv_usec } ); | ||
86 | os_write_file(tty_log_fd, &data, sizeof(data)); | ||
87 | return; | ||
88 | } | ||
89 | os_close_file(fd); | ||
90 | } | ||
91 | |||
92 | static int log_chunk(int fd, const char *buf, int len) | ||
93 | { | ||
94 | int total = 0, try, missed, n; | ||
95 | char chunk[64]; | ||
96 | |||
97 | while(len > 0){ | ||
98 | try = (len > sizeof(chunk)) ? sizeof(chunk) : len; | ||
99 | missed = copy_from_user_proc(chunk, (char *) buf, try); | ||
100 | try -= missed; | ||
101 | n = os_write_file(fd, chunk, try); | ||
102 | if(n != try) { | ||
103 | if(n < 0) | ||
104 | return(n); | ||
105 | return(-EIO); | ||
106 | } | ||
107 | if(missed != 0) | ||
108 | return(-EFAULT); | ||
109 | |||
110 | len -= try; | ||
111 | total += try; | ||
112 | buf += try; | ||
113 | } | ||
114 | |||
115 | return(total); | ||
116 | } | ||
117 | |||
118 | int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read) | ||
119 | { | ||
120 | struct timeval tv; | ||
121 | struct tty_log_buf data; | ||
122 | int direction; | ||
123 | |||
124 | if(fd == tty_log_fd){ | ||
125 | gettimeofday(&tv, NULL); | ||
126 | direction = is_read ? TTY_READ : TTY_WRITE; | ||
127 | data = ((struct tty_log_buf) { .what = TTY_LOG_WRITE, | ||
128 | .tty = (unsigned long) tty, | ||
129 | .len = len, | ||
130 | .direction = direction, | ||
131 | .sec = tv.tv_sec, | ||
132 | .usec = tv.tv_usec } ); | ||
133 | os_write_file(tty_log_fd, &data, sizeof(data)); | ||
134 | } | ||
135 | |||
136 | return(log_chunk(fd, buf, len)); | ||
137 | } | ||
138 | |||
139 | void log_exec(char **argv, void *tty) | ||
140 | { | ||
141 | struct timeval tv; | ||
142 | struct tty_log_buf data; | ||
143 | char **ptr,*arg; | ||
144 | int len; | ||
145 | |||
146 | if(tty_log_fd == -1) return; | ||
147 | |||
148 | gettimeofday(&tv, NULL); | ||
149 | |||
150 | len = 0; | ||
151 | for(ptr = argv; ; ptr++){ | ||
152 | if(copy_from_user_proc(&arg, ptr, sizeof(arg))) | ||
153 | return; | ||
154 | if(arg == NULL) break; | ||
155 | len += strlen_user_proc(arg); | ||
156 | } | ||
157 | |||
158 | data = ((struct tty_log_buf) { .what = TTY_LOG_EXEC, | ||
159 | .tty = (unsigned long) tty, | ||
160 | .len = len, | ||
161 | .direction = 0, | ||
162 | .sec = tv.tv_sec, | ||
163 | .usec = tv.tv_usec } ); | ||
164 | os_write_file(tty_log_fd, &data, sizeof(data)); | ||
165 | |||
166 | for(ptr = argv; ; ptr++){ | ||
167 | if(copy_from_user_proc(&arg, ptr, sizeof(arg))) | ||
168 | return; | ||
169 | if(arg == NULL) break; | ||
170 | log_chunk(tty_log_fd, arg, strlen_user_proc(arg)); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | extern void register_tty_logger(int (*opener)(void *, void *), | ||
175 | int (*writer)(int, const char *, int, | ||
176 | void *, int), | ||
177 | void (*closer)(int, void *)); | ||
178 | |||
179 | static int register_logger(void) | ||
180 | { | ||
181 | register_tty_logger(open_tty_log, write_tty_log, close_tty_log); | ||
182 | return(0); | ||
183 | } | ||
184 | |||
185 | __uml_initcall(register_logger); | ||
186 | |||
187 | static int __init set_tty_log_dir(char *name, int *add) | ||
188 | { | ||
189 | tty_log_dir = name; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | __uml_setup("tty_log_dir=", set_tty_log_dir, | ||
194 | "tty_log_dir=<directory>\n" | ||
195 | " This is used to specify the directory where the logs of all pty\n" | ||
196 | " data from this UML machine will be written.\n\n" | ||
197 | ); | ||
198 | |||
199 | static int __init set_tty_log_fd(char *name, int *add) | ||
200 | { | ||
201 | char *end; | ||
202 | |||
203 | tty_log_fd = strtoul(name, &end, 0); | ||
204 | if((*end != '\0') || (end == name)){ | ||
205 | printf("set_tty_log_fd - strtoul failed on '%s'\n", name); | ||
206 | tty_log_fd = -1; | ||
207 | } | ||
208 | |||
209 | *add = 0; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | __uml_setup("tty_log_fd=", set_tty_log_fd, | ||
214 | "tty_log_fd=<fd>\n" | ||
215 | " This is used to specify a preconfigured file descriptor to which all\n" | ||
216 | " tty data will be written. Preconfigure the descriptor with something\n" | ||
217 | " like '10>tty_log tty_log_fd=10'.\n\n" | ||
218 | ); | ||
219 | |||
220 | |||
221 | /* | ||
222 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
223 | * Emacs will notice this stuff at the end of the file and automatically | ||
224 | * adjust the settings for this buffer only. This must remain at the end | ||
225 | * of the file. | ||
226 | * --------------------------------------------------------------------------- | ||
227 | * Local variables: | ||
228 | * c-file-style: "linux" | ||
229 | * End: | ||
230 | */ | ||
diff --git a/arch/um/kernel/uaccess_user.c b/arch/um/kernel/uaccess_user.c new file mode 100644 index 000000000000..d035257ed0af --- /dev/null +++ b/arch/um/kernel/uaccess_user.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) | ||
3 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <setjmp.h> | ||
8 | #include <string.h> | ||
9 | |||
10 | /* These are here rather than tt/uaccess.c because skas mode needs them in | ||
11 | * order to do SIGBUS recovery when a tmpfs mount runs out of room. | ||
12 | */ | ||
13 | |||
14 | unsigned long __do_user_copy(void *to, const void *from, int n, | ||
15 | void **fault_addr, void **fault_catcher, | ||
16 | void (*op)(void *to, const void *from, | ||
17 | int n), int *faulted_out) | ||
18 | { | ||
19 | unsigned long *faddrp = (unsigned long *) fault_addr, ret; | ||
20 | |||
21 | sigjmp_buf jbuf; | ||
22 | *fault_catcher = &jbuf; | ||
23 | if(sigsetjmp(jbuf, 1) == 0){ | ||
24 | (*op)(to, from, n); | ||
25 | ret = 0; | ||
26 | *faulted_out = 0; | ||
27 | } | ||
28 | else { | ||
29 | ret = *faddrp; | ||
30 | *faulted_out = 1; | ||
31 | } | ||
32 | *fault_addr = NULL; | ||
33 | *fault_catcher = NULL; | ||
34 | return ret; | ||
35 | } | ||
36 | |||
37 | void __do_copy(void *to, const void *from, int n) | ||
38 | { | ||
39 | memcpy(to, from, n); | ||
40 | } | ||
41 | |||
42 | |||
43 | int __do_copy_to_user(void *to, const void *from, int n, | ||
44 | void **fault_addr, void **fault_catcher) | ||
45 | { | ||
46 | unsigned long fault; | ||
47 | int faulted; | ||
48 | |||
49 | fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, | ||
50 | __do_copy, &faulted); | ||
51 | if(!faulted) return(0); | ||
52 | else return(n - (fault - (unsigned long) to)); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
57 | * Emacs will notice this stuff at the end of the file and automatically | ||
58 | * adjust the settings for this buffer only. This must remain at the end | ||
59 | * of the file. | ||
60 | * --------------------------------------------------------------------------- | ||
61 | * Local variables: | ||
62 | * c-file-style: "linux" | ||
63 | * End: | ||
64 | */ | ||
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c new file mode 100644 index 000000000000..5c49d88eed3d --- /dev/null +++ b/arch/um/kernel/um_arch.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/kernel.h" | ||
8 | #include "linux/sched.h" | ||
9 | #include "linux/notifier.h" | ||
10 | #include "linux/mm.h" | ||
11 | #include "linux/types.h" | ||
12 | #include "linux/tty.h" | ||
13 | #include "linux/init.h" | ||
14 | #include "linux/bootmem.h" | ||
15 | #include "linux/spinlock.h" | ||
16 | #include "linux/utsname.h" | ||
17 | #include "linux/sysrq.h" | ||
18 | #include "linux/seq_file.h" | ||
19 | #include "linux/delay.h" | ||
20 | #include "linux/module.h" | ||
21 | #include "asm/page.h" | ||
22 | #include "asm/pgtable.h" | ||
23 | #include "asm/ptrace.h" | ||
24 | #include "asm/elf.h" | ||
25 | #include "asm/user.h" | ||
26 | #include "ubd_user.h" | ||
27 | #include "asm/current.h" | ||
28 | #include "asm/setup.h" | ||
29 | #include "user_util.h" | ||
30 | #include "kern_util.h" | ||
31 | #include "kern.h" | ||
32 | #include "mem_user.h" | ||
33 | #include "mem.h" | ||
34 | #include "umid.h" | ||
35 | #include "initrd.h" | ||
36 | #include "init.h" | ||
37 | #include "os.h" | ||
38 | #include "choose-mode.h" | ||
39 | #include "mode_kern.h" | ||
40 | #include "mode.h" | ||
41 | |||
42 | #define DEFAULT_COMMAND_LINE "root=98:0" | ||
43 | |||
44 | /* Changed in linux_main and setup_arch, which run before SMP is started */ | ||
45 | char command_line[COMMAND_LINE_SIZE] = { 0 }; | ||
46 | |||
47 | void add_arg(char *arg) | ||
48 | { | ||
49 | if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { | ||
50 | printf("add_arg: Too many command line arguments!\n"); | ||
51 | exit(1); | ||
52 | } | ||
53 | if(strlen(command_line) > 0) | ||
54 | strcat(command_line, " "); | ||
55 | strcat(command_line, arg); | ||
56 | } | ||
57 | |||
58 | struct cpuinfo_um boot_cpu_data = { | ||
59 | .loops_per_jiffy = 0, | ||
60 | .ipi_pipe = { -1, -1 } | ||
61 | }; | ||
62 | |||
63 | unsigned long thread_saved_pc(struct task_struct *task) | ||
64 | { | ||
65 | return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas, | ||
66 | task))); | ||
67 | } | ||
68 | |||
69 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
70 | { | ||
71 | int index = 0; | ||
72 | |||
73 | #ifdef CONFIG_SMP | ||
74 | index = (struct cpuinfo_um *) v - cpu_data; | ||
75 | if (!cpu_online(index)) | ||
76 | return 0; | ||
77 | #endif | ||
78 | |||
79 | seq_printf(m, "processor\t: %d\n", index); | ||
80 | seq_printf(m, "vendor_id\t: User Mode Linux\n"); | ||
81 | seq_printf(m, "model name\t: UML\n"); | ||
82 | seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas")); | ||
83 | seq_printf(m, "host\t\t: %s\n", host_info); | ||
84 | seq_printf(m, "bogomips\t: %lu.%02lu\n\n", | ||
85 | loops_per_jiffy/(500000/HZ), | ||
86 | (loops_per_jiffy/(5000/HZ)) % 100); | ||
87 | |||
88 | return(0); | ||
89 | } | ||
90 | |||
91 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
92 | { | ||
93 | return *pos < NR_CPUS ? cpu_data + *pos : NULL; | ||
94 | } | ||
95 | |||
96 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
97 | { | ||
98 | ++*pos; | ||
99 | return c_start(m, pos); | ||
100 | } | ||
101 | |||
102 | static void c_stop(struct seq_file *m, void *v) | ||
103 | { | ||
104 | } | ||
105 | |||
106 | struct seq_operations cpuinfo_op = { | ||
107 | .start = c_start, | ||
108 | .next = c_next, | ||
109 | .stop = c_stop, | ||
110 | .show = show_cpuinfo, | ||
111 | }; | ||
112 | |||
113 | pte_t * __bad_pagetable(void) | ||
114 | { | ||
115 | panic("Someone should implement __bad_pagetable"); | ||
116 | return(NULL); | ||
117 | } | ||
118 | |||
119 | /* Set in linux_main */ | ||
120 | unsigned long host_task_size; | ||
121 | unsigned long task_size; | ||
122 | |||
123 | unsigned long uml_start; | ||
124 | |||
125 | /* Set in early boot */ | ||
126 | unsigned long uml_physmem; | ||
127 | unsigned long uml_reserved; | ||
128 | unsigned long start_vm; | ||
129 | unsigned long end_vm; | ||
130 | int ncpus = 1; | ||
131 | |||
132 | #ifdef CONFIG_MODE_TT | ||
133 | /* Pointer set in linux_main, the array itself is private to each thread, | ||
134 | * and changed at address space creation time so this poses no concurrency | ||
135 | * problems. | ||
136 | */ | ||
137 | static char *argv1_begin = NULL; | ||
138 | static char *argv1_end = NULL; | ||
139 | #endif | ||
140 | |||
141 | /* Set in early boot */ | ||
142 | static int have_root __initdata = 0; | ||
143 | long physmem_size = 32 * 1024 * 1024; | ||
144 | |||
145 | void set_cmdline(char *cmd) | ||
146 | { | ||
147 | #ifdef CONFIG_MODE_TT | ||
148 | char *umid, *ptr; | ||
149 | |||
150 | if(CHOOSE_MODE(honeypot, 0)) return; | ||
151 | |||
152 | umid = get_umid(1); | ||
153 | if(umid != NULL){ | ||
154 | snprintf(argv1_begin, | ||
155 | (argv1_end - argv1_begin) * sizeof(*ptr), | ||
156 | "(%s) ", umid); | ||
157 | ptr = &argv1_begin[strlen(argv1_begin)]; | ||
158 | } | ||
159 | else ptr = argv1_begin; | ||
160 | |||
161 | snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd); | ||
162 | memset(argv1_begin + strlen(argv1_begin), '\0', | ||
163 | argv1_end - argv1_begin - strlen(argv1_begin)); | ||
164 | #endif | ||
165 | } | ||
166 | |||
167 | static char *usage_string = | ||
168 | "User Mode Linux v%s\n" | ||
169 | " available at http://user-mode-linux.sourceforge.net/\n\n"; | ||
170 | |||
171 | static int __init uml_version_setup(char *line, int *add) | ||
172 | { | ||
173 | printf("%s\n", system_utsname.release); | ||
174 | exit(0); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | __uml_setup("--version", uml_version_setup, | ||
180 | "--version\n" | ||
181 | " Prints the version number of the kernel.\n\n" | ||
182 | ); | ||
183 | |||
184 | static int __init uml_root_setup(char *line, int *add) | ||
185 | { | ||
186 | have_root = 1; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | __uml_setup("root=", uml_root_setup, | ||
191 | "root=<file containing the root fs>\n" | ||
192 | " This is actually used by the generic kernel in exactly the same\n" | ||
193 | " way as in any other kernel. If you configure a number of block\n" | ||
194 | " devices and want to boot off something other than ubd0, you \n" | ||
195 | " would use something like:\n" | ||
196 | " root=/dev/ubd5\n\n" | ||
197 | ); | ||
198 | |||
199 | #ifdef CONFIG_SMP | ||
200 | static int __init uml_ncpus_setup(char *line, int *add) | ||
201 | { | ||
202 | if (!sscanf(line, "%d", &ncpus)) { | ||
203 | printf("Couldn't parse [%s]\n", line); | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | __uml_setup("ncpus=", uml_ncpus_setup, | ||
211 | "ncpus=<# of desired CPUs>\n" | ||
212 | " This tells an SMP kernel how many virtual processors to start.\n\n" | ||
213 | ); | ||
214 | #endif | ||
215 | |||
216 | static int force_tt = 0; | ||
217 | |||
218 | #if defined(CONFIG_MODE_TT) && defined(CONFIG_MODE_SKAS) | ||
219 | #define DEFAULT_TT 0 | ||
220 | |||
221 | static int __init mode_tt_setup(char *line, int *add) | ||
222 | { | ||
223 | force_tt = 1; | ||
224 | return(0); | ||
225 | } | ||
226 | |||
227 | #else | ||
228 | #ifdef CONFIG_MODE_SKAS | ||
229 | |||
230 | #define DEFAULT_TT 0 | ||
231 | |||
232 | static int __init mode_tt_setup(char *line, int *add) | ||
233 | { | ||
234 | printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n"); | ||
235 | return(0); | ||
236 | } | ||
237 | |||
238 | #else | ||
239 | #ifdef CONFIG_MODE_TT | ||
240 | |||
241 | #define DEFAULT_TT 1 | ||
242 | |||
243 | static int __init mode_tt_setup(char *line, int *add) | ||
244 | { | ||
245 | printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n"); | ||
246 | return(0); | ||
247 | } | ||
248 | |||
249 | #else | ||
250 | |||
251 | #error Either CONFIG_MODE_TT or CONFIG_MODE_SKAS must be enabled | ||
252 | |||
253 | #endif | ||
254 | #endif | ||
255 | #endif | ||
256 | |||
257 | __uml_setup("mode=tt", mode_tt_setup, | ||
258 | "mode=tt\n" | ||
259 | " When both CONFIG_MODE_TT and CONFIG_MODE_SKAS are enabled, this option\n" | ||
260 | " forces UML to run in tt (tracing thread) mode. It is not the default\n" | ||
261 | " because it's slower and less secure than skas mode.\n\n" | ||
262 | ); | ||
263 | |||
264 | int mode_tt = DEFAULT_TT; | ||
265 | |||
266 | static int __init Usage(char *line, int *add) | ||
267 | { | ||
268 | const char **p; | ||
269 | |||
270 | printf(usage_string, system_utsname.release); | ||
271 | p = &__uml_help_start; | ||
272 | while (p < &__uml_help_end) { | ||
273 | printf("%s", *p); | ||
274 | p++; | ||
275 | } | ||
276 | exit(0); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | __uml_setup("--help", Usage, | ||
282 | "--help\n" | ||
283 | " Prints this message.\n\n" | ||
284 | ); | ||
285 | |||
286 | static int __init uml_checksetup(char *line, int *add) | ||
287 | { | ||
288 | struct uml_param *p; | ||
289 | |||
290 | p = &__uml_setup_start; | ||
291 | while(p < &__uml_setup_end) { | ||
292 | int n; | ||
293 | |||
294 | n = strlen(p->str); | ||
295 | if(!strncmp(line, p->str, n)){ | ||
296 | if (p->setup_func(line + n, add)) return 1; | ||
297 | } | ||
298 | p++; | ||
299 | } | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static void __init uml_postsetup(void) | ||
304 | { | ||
305 | initcall_t *p; | ||
306 | |||
307 | p = &__uml_postsetup_start; | ||
308 | while(p < &__uml_postsetup_end){ | ||
309 | (*p)(); | ||
310 | p++; | ||
311 | } | ||
312 | return; | ||
313 | } | ||
314 | |||
315 | /* Set during early boot */ | ||
316 | unsigned long brk_start; | ||
317 | unsigned long end_iomem; | ||
318 | EXPORT_SYMBOL(end_iomem); | ||
319 | |||
320 | #define MIN_VMALLOC (32 * 1024 * 1024) | ||
321 | |||
322 | int linux_main(int argc, char **argv) | ||
323 | { | ||
324 | unsigned long avail, diff; | ||
325 | unsigned long virtmem_size, max_physmem; | ||
326 | unsigned int i, add; | ||
327 | |||
328 | for (i = 1; i < argc; i++){ | ||
329 | if((i == 1) && (argv[i][0] == ' ')) continue; | ||
330 | add = 1; | ||
331 | uml_checksetup(argv[i], &add); | ||
332 | if (add) | ||
333 | add_arg(argv[i]); | ||
334 | } | ||
335 | if(have_root == 0) | ||
336 | add_arg(DEFAULT_COMMAND_LINE); | ||
337 | |||
338 | mode_tt = force_tt ? 1 : !can_do_skas(); | ||
339 | #ifndef CONFIG_MODE_TT | ||
340 | if (mode_tt) { | ||
341 | /*Since CONFIG_MODE_TT is #undef'ed, force_tt cannot be 1. So, | ||
342 | * can_do_skas() returned 0, and the message is correct. */ | ||
343 | printf("Support for TT mode is disabled, and no SKAS support is present on the host.\n"); | ||
344 | exit(1); | ||
345 | } | ||
346 | #endif | ||
347 | uml_start = CHOOSE_MODE_PROC(set_task_sizes_tt, set_task_sizes_skas, 0, | ||
348 | &host_task_size, &task_size); | ||
349 | |||
350 | /* Need to check this early because mmapping happens before the | ||
351 | * kernel is running. | ||
352 | */ | ||
353 | check_tmpexec(); | ||
354 | |||
355 | brk_start = (unsigned long) sbrk(0); | ||
356 | CHOOSE_MODE_PROC(before_mem_tt, before_mem_skas, brk_start); | ||
357 | /* Increase physical memory size for exec-shield users | ||
358 | so they actually get what they asked for. This should | ||
359 | add zero for non-exec shield users */ | ||
360 | |||
361 | diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); | ||
362 | if(diff > 1024 * 1024){ | ||
363 | printf("Adding %ld bytes to physical memory to account for " | ||
364 | "exec-shield gap\n", diff); | ||
365 | physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); | ||
366 | } | ||
367 | |||
368 | uml_physmem = uml_start; | ||
369 | |||
370 | /* Reserve up to 4M after the current brk */ | ||
371 | uml_reserved = ROUND_4M(brk_start) + (1 << 22); | ||
372 | |||
373 | setup_machinename(system_utsname.machine); | ||
374 | |||
375 | #ifdef CONFIG_MODE_TT | ||
376 | argv1_begin = argv[1]; | ||
377 | argv1_end = &argv[1][strlen(argv[1])]; | ||
378 | #endif | ||
379 | |||
380 | highmem = 0; | ||
381 | iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; | ||
382 | max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC; | ||
383 | |||
384 | /* Zones have to begin on a 1 << MAX_ORDER page boundary, | ||
385 | * so this makes sure that's true for highmem | ||
386 | */ | ||
387 | max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1); | ||
388 | if(physmem_size + iomem_size > max_physmem){ | ||
389 | highmem = physmem_size + iomem_size - max_physmem; | ||
390 | physmem_size -= highmem; | ||
391 | #ifndef CONFIG_HIGHMEM | ||
392 | highmem = 0; | ||
393 | printf("CONFIG_HIGHMEM not enabled - physical memory shrunk " | ||
394 | "to %ld bytes\n", physmem_size); | ||
395 | #endif | ||
396 | } | ||
397 | |||
398 | high_physmem = uml_physmem + physmem_size; | ||
399 | end_iomem = high_physmem + iomem_size; | ||
400 | high_memory = (void *) end_iomem; | ||
401 | |||
402 | start_vm = VMALLOC_START; | ||
403 | |||
404 | setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); | ||
405 | if(init_maps(physmem_size, iomem_size, highmem)){ | ||
406 | printf("Failed to allocate mem_map for %ld bytes of physical " | ||
407 | "memory and %ld bytes of highmem\n", physmem_size, | ||
408 | highmem); | ||
409 | exit(1); | ||
410 | } | ||
411 | |||
412 | virtmem_size = physmem_size; | ||
413 | avail = get_kmem_end() - start_vm; | ||
414 | if(physmem_size > avail) virtmem_size = avail; | ||
415 | end_vm = start_vm + virtmem_size; | ||
416 | |||
417 | if(virtmem_size < physmem_size) | ||
418 | printf("Kernel virtual memory size shrunk to %ld bytes\n", | ||
419 | virtmem_size); | ||
420 | |||
421 | uml_postsetup(); | ||
422 | |||
423 | task_protections((unsigned long) &init_thread_info); | ||
424 | os_flush_stdout(); | ||
425 | |||
426 | return(CHOOSE_MODE(start_uml_tt(), start_uml_skas())); | ||
427 | } | ||
428 | |||
429 | extern int uml_exitcode; | ||
430 | |||
431 | static int panic_exit(struct notifier_block *self, unsigned long unused1, | ||
432 | void *unused2) | ||
433 | { | ||
434 | bust_spinlocks(1); | ||
435 | show_regs(&(current->thread.regs)); | ||
436 | bust_spinlocks(0); | ||
437 | uml_exitcode = 1; | ||
438 | machine_halt(); | ||
439 | return(0); | ||
440 | } | ||
441 | |||
442 | static struct notifier_block panic_exit_notifier = { | ||
443 | .notifier_call = panic_exit, | ||
444 | .next = NULL, | ||
445 | .priority = 0 | ||
446 | }; | ||
447 | |||
448 | void __init setup_arch(char **cmdline_p) | ||
449 | { | ||
450 | notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); | ||
451 | paging_init(); | ||
452 | strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); | ||
453 | *cmdline_p = command_line; | ||
454 | setup_hostinfo(); | ||
455 | } | ||
456 | |||
457 | void __init check_bugs(void) | ||
458 | { | ||
459 | arch_check_bugs(); | ||
460 | check_ptrace(); | ||
461 | check_sigio(); | ||
462 | check_devanon(); | ||
463 | } | ||
464 | |||
465 | void apply_alternatives(void *start, void *end) | ||
466 | { | ||
467 | } | ||
diff --git a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c new file mode 100644 index 000000000000..186c28885016 --- /dev/null +++ b/arch/um/kernel/umid.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <errno.h> | ||
9 | #include <string.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <dirent.h> | ||
12 | #include <signal.h> | ||
13 | #include <sys/stat.h> | ||
14 | #include <sys/param.h> | ||
15 | #include "user.h" | ||
16 | #include "umid.h" | ||
17 | #include "init.h" | ||
18 | #include "os.h" | ||
19 | #include "user_util.h" | ||
20 | #include "choose-mode.h" | ||
21 | |||
22 | #define UMID_LEN 64 | ||
23 | #define UML_DIR "~/.uml/" | ||
24 | |||
25 | /* Changed by set_umid and make_umid, which are run early in boot */ | ||
26 | static char umid[UMID_LEN] = { 0 }; | ||
27 | |||
28 | /* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ | ||
29 | static char *uml_dir = UML_DIR; | ||
30 | |||
31 | /* Changed by set_umid */ | ||
32 | static int umid_is_random = 1; | ||
33 | static int umid_inited = 0; | ||
34 | |||
35 | static int make_umid(int (*printer)(const char *fmt, ...)); | ||
36 | |||
37 | static int __init set_umid(char *name, int is_random, | ||
38 | int (*printer)(const char *fmt, ...)) | ||
39 | { | ||
40 | if(umid_inited){ | ||
41 | (*printer)("Unique machine name can't be set twice\n"); | ||
42 | return(-1); | ||
43 | } | ||
44 | |||
45 | if(strlen(name) > UMID_LEN - 1) | ||
46 | (*printer)("Unique machine name is being truncated to %d " | ||
47 | "characters\n", UMID_LEN); | ||
48 | strlcpy(umid, name, sizeof(umid)); | ||
49 | |||
50 | umid_is_random = is_random; | ||
51 | umid_inited = 1; | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int __init set_umid_arg(char *name, int *add) | ||
56 | { | ||
57 | *add = 0; | ||
58 | return(set_umid(name, 0, printf)); | ||
59 | } | ||
60 | |||
61 | __uml_setup("umid=", set_umid_arg, | ||
62 | "umid=<name>\n" | ||
63 | " This is used to assign a unique identity to this UML machine and\n" | ||
64 | " is used for naming the pid file and management console socket.\n\n" | ||
65 | ); | ||
66 | |||
67 | int __init umid_file_name(char *name, char *buf, int len) | ||
68 | { | ||
69 | int n; | ||
70 | |||
71 | if(!umid_inited && make_umid(printk)) return(-1); | ||
72 | |||
73 | n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1; | ||
74 | if(n > len){ | ||
75 | printk("umid_file_name : buffer too short\n"); | ||
76 | return(-1); | ||
77 | } | ||
78 | |||
79 | sprintf(buf, "%s%s/%s", uml_dir, umid, name); | ||
80 | return(0); | ||
81 | } | ||
82 | |||
83 | extern int tracing_pid; | ||
84 | |||
85 | static int __init create_pid_file(void) | ||
86 | { | ||
87 | char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; | ||
88 | char pid[sizeof("nnnnn\0")]; | ||
89 | int fd, n; | ||
90 | |||
91 | if(umid_file_name("pid", file, sizeof(file))) return 0; | ||
92 | |||
93 | fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), | ||
94 | 0644); | ||
95 | if(fd < 0){ | ||
96 | printf("Open of machine pid file \"%s\" failed: %s\n", | ||
97 | file, strerror(-fd)); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | sprintf(pid, "%d\n", os_getpid()); | ||
102 | n = os_write_file(fd, pid, strlen(pid)); | ||
103 | if(n != strlen(pid)) | ||
104 | printf("Write of pid file failed - err = %d\n", -n); | ||
105 | os_close_file(fd); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int actually_do_remove(char *dir) | ||
110 | { | ||
111 | DIR *directory; | ||
112 | struct dirent *ent; | ||
113 | int len; | ||
114 | char file[256]; | ||
115 | |||
116 | directory = opendir(dir); | ||
117 | if(directory == NULL){ | ||
118 | printk("actually_do_remove : couldn't open directory '%s', " | ||
119 | "errno = %d\n", dir, errno); | ||
120 | return(1); | ||
121 | } | ||
122 | while((ent = readdir(directory)) != NULL){ | ||
123 | if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) | ||
124 | continue; | ||
125 | len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1; | ||
126 | if(len > sizeof(file)){ | ||
127 | printk("Not deleting '%s' from '%s' - name too long\n", | ||
128 | ent->d_name, dir); | ||
129 | continue; | ||
130 | } | ||
131 | sprintf(file, "%s/%s", dir, ent->d_name); | ||
132 | if(unlink(file) < 0){ | ||
133 | printk("actually_do_remove : couldn't remove '%s' " | ||
134 | "from '%s', errno = %d\n", ent->d_name, dir, | ||
135 | errno); | ||
136 | return(1); | ||
137 | } | ||
138 | } | ||
139 | if(rmdir(dir) < 0){ | ||
140 | printk("actually_do_remove : couldn't rmdir '%s', " | ||
141 | "errno = %d\n", dir, errno); | ||
142 | return(1); | ||
143 | } | ||
144 | return(0); | ||
145 | } | ||
146 | |||
147 | void remove_umid_dir(void) | ||
148 | { | ||
149 | char dir[strlen(uml_dir) + UMID_LEN + 1]; | ||
150 | if(!umid_inited) return; | ||
151 | |||
152 | sprintf(dir, "%s%s", uml_dir, umid); | ||
153 | actually_do_remove(dir); | ||
154 | } | ||
155 | |||
156 | char *get_umid(int only_if_set) | ||
157 | { | ||
158 | if(only_if_set && umid_is_random) return(NULL); | ||
159 | return(umid); | ||
160 | } | ||
161 | |||
162 | int not_dead_yet(char *dir) | ||
163 | { | ||
164 | char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; | ||
165 | char pid[sizeof("nnnnn\0")], *end; | ||
166 | int dead, fd, p, n; | ||
167 | |||
168 | sprintf(file, "%s/pid", dir); | ||
169 | dead = 0; | ||
170 | fd = os_open_file(file, of_read(OPENFLAGS()), 0); | ||
171 | if(fd < 0){ | ||
172 | if(fd != -ENOENT){ | ||
173 | printk("not_dead_yet : couldn't open pid file '%s', " | ||
174 | "err = %d\n", file, -fd); | ||
175 | return(1); | ||
176 | } | ||
177 | dead = 1; | ||
178 | } | ||
179 | if(fd > 0){ | ||
180 | n = os_read_file(fd, pid, sizeof(pid)); | ||
181 | if(n < 0){ | ||
182 | printk("not_dead_yet : couldn't read pid file '%s', " | ||
183 | "err = %d\n", file, -n); | ||
184 | return(1); | ||
185 | } | ||
186 | p = strtoul(pid, &end, 0); | ||
187 | if(end == pid){ | ||
188 | printk("not_dead_yet : couldn't parse pid file '%s', " | ||
189 | "errno = %d\n", file, errno); | ||
190 | dead = 1; | ||
191 | } | ||
192 | if(((kill(p, 0) < 0) && (errno == ESRCH)) || | ||
193 | (p == CHOOSE_MODE(tracing_pid, os_getpid()))) | ||
194 | dead = 1; | ||
195 | } | ||
196 | if(!dead) return(1); | ||
197 | return(actually_do_remove(dir)); | ||
198 | } | ||
199 | |||
200 | static int __init set_uml_dir(char *name, int *add) | ||
201 | { | ||
202 | if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){ | ||
203 | uml_dir = malloc(strlen(name) + 2); | ||
204 | if(uml_dir == NULL){ | ||
205 | printf("Failed to malloc uml_dir - error = %d\n", | ||
206 | errno); | ||
207 | uml_dir = name; | ||
208 | /* Return 0 here because do_initcalls doesn't look at | ||
209 | * the return value. | ||
210 | */ | ||
211 | return(0); | ||
212 | } | ||
213 | sprintf(uml_dir, "%s/", name); | ||
214 | } | ||
215 | else uml_dir = name; | ||
216 | return(0); | ||
217 | } | ||
218 | |||
219 | static int __init make_uml_dir(void) | ||
220 | { | ||
221 | char dir[MAXPATHLEN + 1] = { '\0' }; | ||
222 | int len; | ||
223 | |||
224 | if(*uml_dir == '~'){ | ||
225 | char *home = getenv("HOME"); | ||
226 | |||
227 | if(home == NULL){ | ||
228 | printf("make_uml_dir : no value in environment for " | ||
229 | "$HOME\n"); | ||
230 | exit(1); | ||
231 | } | ||
232 | strlcpy(dir, home, sizeof(dir)); | ||
233 | uml_dir++; | ||
234 | } | ||
235 | len = strlen(dir); | ||
236 | strncat(dir, uml_dir, sizeof(dir) - len); | ||
237 | len = strlen(dir); | ||
238 | if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){ | ||
239 | dir[len] = '/'; | ||
240 | dir[len + 1] = '\0'; | ||
241 | } | ||
242 | |||
243 | uml_dir = malloc(strlen(dir) + 1); | ||
244 | if(uml_dir == NULL){ | ||
245 | printf("make_uml_dir : malloc failed, errno = %d\n", errno); | ||
246 | exit(1); | ||
247 | } | ||
248 | strcpy(uml_dir, dir); | ||
249 | |||
250 | if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){ | ||
251 | printf("Failed to mkdir %s: %s\n", uml_dir, strerror(errno)); | ||
252 | return(-1); | ||
253 | } | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int __init make_umid(int (*printer)(const char *fmt, ...)) | ||
258 | { | ||
259 | int fd, err; | ||
260 | char tmp[strlen(uml_dir) + UMID_LEN + 1]; | ||
261 | |||
262 | strlcpy(tmp, uml_dir, sizeof(tmp)); | ||
263 | |||
264 | if(!umid_inited){ | ||
265 | strcat(tmp, "XXXXXX"); | ||
266 | fd = mkstemp(tmp); | ||
267 | if(fd < 0){ | ||
268 | (*printer)("make_umid - mkstemp(%s) failed: %s\n", | ||
269 | tmp,strerror(errno)); | ||
270 | return(1); | ||
271 | } | ||
272 | |||
273 | os_close_file(fd); | ||
274 | /* There's a nice tiny little race between this unlink and | ||
275 | * the mkdir below. It'd be nice if there were a mkstemp | ||
276 | * for directories. | ||
277 | */ | ||
278 | unlink(tmp); | ||
279 | set_umid(&tmp[strlen(uml_dir)], 1, printer); | ||
280 | } | ||
281 | |||
282 | sprintf(tmp, "%s%s", uml_dir, umid); | ||
283 | |||
284 | err = mkdir(tmp, 0777); | ||
285 | if(err < 0){ | ||
286 | if(errno == EEXIST){ | ||
287 | if(not_dead_yet(tmp)){ | ||
288 | (*printer)("umid '%s' is in use\n", umid); | ||
289 | return(-1); | ||
290 | } | ||
291 | err = mkdir(tmp, 0777); | ||
292 | } | ||
293 | } | ||
294 | if(err < 0){ | ||
295 | (*printer)("Failed to create %s - errno = %d\n", umid, errno); | ||
296 | return(-1); | ||
297 | } | ||
298 | |||
299 | return(0); | ||
300 | } | ||
301 | |||
302 | __uml_setup("uml_dir=", set_uml_dir, | ||
303 | "uml_dir=<directory>\n" | ||
304 | " The location to place the pid and umid files.\n\n" | ||
305 | ); | ||
306 | |||
307 | static int __init make_umid_setup(void) | ||
308 | { | ||
309 | /* one function with the ordering we need ... */ | ||
310 | make_uml_dir(); | ||
311 | make_umid(printf); | ||
312 | return create_pid_file(); | ||
313 | } | ||
314 | __uml_postsetup(make_umid_setup); | ||
315 | |||
316 | /* | ||
317 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
318 | * Emacs will notice this stuff at the end of the file and automatically | ||
319 | * adjust the settings for this buffer only. This must remain at the end | ||
320 | * of the file. | ||
321 | * --------------------------------------------------------------------------- | ||
322 | * Local variables: | ||
323 | * c-file-style: "linux" | ||
324 | * End: | ||
325 | */ | ||
diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S new file mode 100644 index 000000000000..76eadb309189 --- /dev/null +++ b/arch/um/kernel/uml.lds.S | |||
@@ -0,0 +1,106 @@ | |||
1 | #include <asm-generic/vmlinux.lds.h> | ||
2 | |||
3 | OUTPUT_FORMAT(ELF_FORMAT) | ||
4 | OUTPUT_ARCH(ELF_ARCH) | ||
5 | ENTRY(_start) | ||
6 | jiffies = jiffies_64; | ||
7 | |||
8 | SECTIONS | ||
9 | { | ||
10 | /*This must contain the right address - not quite the default ELF one.*/ | ||
11 | PROVIDE (__executable_start = START); | ||
12 | . = START + SIZEOF_HEADERS; | ||
13 | |||
14 | /* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start | ||
15 | * is remapped.*/ | ||
16 | __binary_start = .; | ||
17 | #ifdef MODE_TT | ||
18 | .thread_private : { | ||
19 | __start_thread_private = .; | ||
20 | errno = .; | ||
21 | . += 4; | ||
22 | arch/um/kernel/tt/unmap_fin.o (.data) | ||
23 | __end_thread_private = .; | ||
24 | } | ||
25 | . = ALIGN(4096); | ||
26 | .remap : { arch/um/kernel/tt/unmap_fin.o (.text) } | ||
27 | |||
28 | /* We want it only if we are in MODE_TT. In both cases, however, when MODE_TT | ||
29 | * is off the resulting binary segfaults.*/ | ||
30 | |||
31 | . = ALIGN(4096); /* Init code and data */ | ||
32 | #endif | ||
33 | |||
34 | _stext = .; | ||
35 | __init_begin = .; | ||
36 | .init.text : { | ||
37 | _sinittext = .; | ||
38 | *(.init.text) | ||
39 | _einittext = .; | ||
40 | } | ||
41 | . = ALIGN(4096); | ||
42 | .text : | ||
43 | { | ||
44 | *(.text) | ||
45 | SCHED_TEXT | ||
46 | LOCK_TEXT | ||
47 | *(.fixup) | ||
48 | /* .gnu.warning sections are handled specially by elf32.em. */ | ||
49 | *(.gnu.warning) | ||
50 | *(.gnu.linkonce.t*) | ||
51 | } | ||
52 | |||
53 | #include "asm/common.lds.S" | ||
54 | |||
55 | init.data : { *(init.data) } | ||
56 | .data : | ||
57 | { | ||
58 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ | ||
59 | *(.data.init_task) | ||
60 | *(.data) | ||
61 | *(.gnu.linkonce.d*) | ||
62 | CONSTRUCTORS | ||
63 | } | ||
64 | .data1 : { *(.data1) } | ||
65 | .ctors : | ||
66 | { | ||
67 | *(.ctors) | ||
68 | } | ||
69 | .dtors : | ||
70 | { | ||
71 | *(.dtors) | ||
72 | } | ||
73 | |||
74 | .got : { *(.got.plt) *(.got) } | ||
75 | .dynamic : { *(.dynamic) } | ||
76 | /* We want the small data sections together, so single-instruction offsets | ||
77 | can access them all, and initialized data all before uninitialized, so | ||
78 | we can shorten the on-disk segment size. */ | ||
79 | .sdata : { *(.sdata) } | ||
80 | _edata = .; | ||
81 | PROVIDE (edata = .); | ||
82 | . = ALIGN(0x1000); | ||
83 | .sbss : | ||
84 | { | ||
85 | __bss_start = .; | ||
86 | PROVIDE(_bss_start = .); | ||
87 | *(.sbss) | ||
88 | *(.scommon) | ||
89 | } | ||
90 | .bss : | ||
91 | { | ||
92 | *(.dynbss) | ||
93 | *(.bss) | ||
94 | *(COMMON) | ||
95 | } | ||
96 | _end = . ; | ||
97 | PROVIDE (end = .); | ||
98 | /* Stabs debugging sections. */ | ||
99 | .stab 0 : { *(.stab) } | ||
100 | .stabstr 0 : { *(.stabstr) } | ||
101 | .stab.excl 0 : { *(.stab.excl) } | ||
102 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
103 | .stab.index 0 : { *(.stab.index) } | ||
104 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
105 | .comment 0 : { *(.comment) } | ||
106 | } | ||
diff --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c new file mode 100644 index 000000000000..954ff67cc8b3 --- /dev/null +++ b/arch/um/kernel/user_util.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <limits.h> | ||
10 | #include <setjmp.h> | ||
11 | #include <sys/mman.h> | ||
12 | #include <sys/stat.h> | ||
13 | #include <sys/utsname.h> | ||
14 | #include <sys/param.h> | ||
15 | #include <sys/time.h> | ||
16 | #include "asm/types.h" | ||
17 | #include <ctype.h> | ||
18 | #include <signal.h> | ||
19 | #include <wait.h> | ||
20 | #include <errno.h> | ||
21 | #include <stdarg.h> | ||
22 | #include <sched.h> | ||
23 | #include <termios.h> | ||
24 | #include <string.h> | ||
25 | #include "user_util.h" | ||
26 | #include "kern_util.h" | ||
27 | #include "user.h" | ||
28 | #include "mem_user.h" | ||
29 | #include "init.h" | ||
30 | #include "helper.h" | ||
31 | #include "ptrace_user.h" | ||
32 | #include "uml-config.h" | ||
33 | |||
34 | void stop(void) | ||
35 | { | ||
36 | while(1) sleep(1000000); | ||
37 | } | ||
38 | |||
39 | void stack_protections(unsigned long address) | ||
40 | { | ||
41 | int prot = PROT_READ | PROT_WRITE | PROT_EXEC; | ||
42 | |||
43 | if(mprotect((void *) address, page_size(), prot) < 0) | ||
44 | panic("protecting stack failed, errno = %d", errno); | ||
45 | } | ||
46 | |||
47 | void task_protections(unsigned long address) | ||
48 | { | ||
49 | unsigned long guard = address + page_size(); | ||
50 | unsigned long stack = guard + page_size(); | ||
51 | int prot = 0, pages; | ||
52 | |||
53 | #ifdef notdef | ||
54 | if(mprotect((void *) stack, page_size(), prot) < 0) | ||
55 | panic("protecting guard page failed, errno = %d", errno); | ||
56 | #endif | ||
57 | pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2; | ||
58 | prot = PROT_READ | PROT_WRITE | PROT_EXEC; | ||
59 | if(mprotect((void *) stack, pages * page_size(), prot) < 0) | ||
60 | panic("protecting stack failed, errno = %d", errno); | ||
61 | } | ||
62 | |||
63 | int wait_for_stop(int pid, int sig, int cont_type, void *relay) | ||
64 | { | ||
65 | sigset_t *relay_signals = relay; | ||
66 | int status, ret; | ||
67 | |||
68 | while(1){ | ||
69 | CATCH_EINTR(ret = waitpid(pid, &status, WUNTRACED)); | ||
70 | if((ret < 0) || | ||
71 | !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){ | ||
72 | if(ret < 0){ | ||
73 | printk("wait failed, errno = %d\n", | ||
74 | errno); | ||
75 | } | ||
76 | else if(WIFEXITED(status)) | ||
77 | printk("process %d exited with status %d\n", | ||
78 | pid, WEXITSTATUS(status)); | ||
79 | else if(WIFSIGNALED(status)) | ||
80 | printk("process %d exited with signal %d\n", | ||
81 | pid, WTERMSIG(status)); | ||
82 | else if((WSTOPSIG(status) == SIGVTALRM) || | ||
83 | (WSTOPSIG(status) == SIGALRM) || | ||
84 | (WSTOPSIG(status) == SIGIO) || | ||
85 | (WSTOPSIG(status) == SIGPROF) || | ||
86 | (WSTOPSIG(status) == SIGCHLD) || | ||
87 | (WSTOPSIG(status) == SIGWINCH) || | ||
88 | (WSTOPSIG(status) == SIGINT)){ | ||
89 | ptrace(cont_type, pid, 0, WSTOPSIG(status)); | ||
90 | continue; | ||
91 | } | ||
92 | else if((relay_signals != NULL) && | ||
93 | sigismember(relay_signals, WSTOPSIG(status))){ | ||
94 | ptrace(cont_type, pid, 0, WSTOPSIG(status)); | ||
95 | continue; | ||
96 | } | ||
97 | else printk("process %d stopped with signal %d\n", | ||
98 | pid, WSTOPSIG(status)); | ||
99 | panic("wait_for_stop failed to wait for %d to stop " | ||
100 | "with %d\n", pid, sig); | ||
101 | } | ||
102 | return(status); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | int raw(int fd) | ||
107 | { | ||
108 | struct termios tt; | ||
109 | int err; | ||
110 | |||
111 | CATCH_EINTR(err = tcgetattr(fd, &tt)); | ||
112 | if (err < 0) { | ||
113 | printk("tcgetattr failed, errno = %d\n", errno); | ||
114 | return(-errno); | ||
115 | } | ||
116 | |||
117 | cfmakeraw(&tt); | ||
118 | |||
119 | CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); | ||
120 | if (err < 0) { | ||
121 | printk("tcsetattr failed, errno = %d\n", errno); | ||
122 | return(-errno); | ||
123 | } | ||
124 | |||
125 | /* XXX tcsetattr could have applied only some changes | ||
126 | * (and cfmakeraw() is a set of changes) */ | ||
127 | return(0); | ||
128 | } | ||
129 | |||
130 | void setup_machinename(char *machine_out) | ||
131 | { | ||
132 | struct utsname host; | ||
133 | |||
134 | uname(&host); | ||
135 | strcpy(machine_out, host.machine); | ||
136 | } | ||
137 | |||
138 | char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1]; | ||
139 | |||
140 | void setup_hostinfo(void) | ||
141 | { | ||
142 | struct utsname host; | ||
143 | |||
144 | uname(&host); | ||
145 | sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename, | ||
146 | host.release, host.version, host.machine); | ||
147 | } | ||
148 | |||
149 | int setjmp_wrapper(void (*proc)(void *, void *), ...) | ||
150 | { | ||
151 | va_list args; | ||
152 | sigjmp_buf buf; | ||
153 | int n; | ||
154 | |||
155 | n = sigsetjmp(buf, 1); | ||
156 | if(n == 0){ | ||
157 | va_start(args, proc); | ||
158 | (*proc)(&buf, &args); | ||
159 | } | ||
160 | va_end(args); | ||
161 | return(n); | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
166 | * Emacs will notice this stuff at the end of the file and automatically | ||
167 | * adjust the settings for this buffer only. This must remain at the end | ||
168 | * of the file. | ||
169 | * --------------------------------------------------------------------------- | ||
170 | * Local variables: | ||
171 | * c-file-style: "linux" | ||
172 | * End: | ||
173 | */ | ||