Merge branch 'master'
[linux-2.6] / arch / um / kernel / skas / mem_user.c
1 /* 
2  * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include <signal.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <sys/mman.h>
10 #include <sys/wait.h>
11 #include <asm/page.h>
12 #include <asm/unistd.h>
13 #include "mem_user.h"
14 #include "mem.h"
15 #include "skas.h"
16 #include "user.h"
17 #include "os.h"
18 #include "proc_mm.h"
19 #include "ptrace_user.h"
20 #include "user_util.h"
21 #include "kern_util.h"
22 #include "task.h"
23 #include "registers.h"
24 #include "uml-config.h"
25 #include "sysdep/ptrace.h"
26 #include "sysdep/stub.h"
27
28 extern unsigned long batch_syscall_stub, __syscall_stub_start;
29
30 extern void wait_stub_done(int pid, int sig, char * fname);
31
32 static inline unsigned long *check_init_stack(struct mm_id * mm_idp,
33                                               unsigned long *stack)
34 {
35         if(stack == NULL){
36                 stack = (unsigned long *) mm_idp->stack + 2;
37                 *stack = 0;
38         }
39         return stack;
40 }
41
42 extern int proc_mm;
43
44 int single_count = 0;
45 int multi_count = 0;
46 int multi_op_count = 0;
47
48 static long do_syscall_stub(struct mm_id *mm_idp, void **addr)
49 {
50         unsigned long regs[MAX_REG_NR];
51         unsigned long *data;
52         unsigned long *syscall;
53         long ret, offset;
54         int n, pid = mm_idp->u.pid;
55
56         if(proc_mm)
57 #warning Need to look up userspace_pid by cpu
58                 pid = userspace_pid[0];
59
60         multi_count++;
61
62         get_safe_registers(regs);
63         regs[REGS_IP_INDEX] = UML_CONFIG_STUB_CODE +
64                 ((unsigned long) &batch_syscall_stub -
65                  (unsigned long) &__syscall_stub_start);
66         n = ptrace_setregs(pid, regs);
67         if(n < 0)
68                 panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
69                       n);
70
71         wait_stub_done(pid, 0, "do_syscall_stub");
72
73         /* When the stub stops, we find the following values on the
74          * beginning of the stack:
75          * (long )return_value
76          * (long )offset to failed sycall-data (0, if no error)
77          */
78         ret = *((unsigned long *) mm_idp->stack);
79         offset = *((unsigned long *) mm_idp->stack + 1);
80         if (offset) {
81                 data = (unsigned long *)(mm_idp->stack +
82                                          offset - UML_CONFIG_STUB_DATA);
83                 syscall = (unsigned long *)((unsigned long)data + data[0]);
84                 printk("do_syscall_stub: syscall %ld failed, return value = "
85                        "0x%lx, expected return value = 0x%lx\n",
86                        syscall[0], ret, syscall[7]);
87                 printk("    syscall parameters: "
88                        "0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
89                        syscall[1], syscall[2], syscall[3],
90                        syscall[4], syscall[5], syscall[6]);
91                 for(n = 1; n < data[0]/sizeof(long); n++) {
92                         if(n == 1)
93                                 printk("    additional syscall data:");
94                         if(n % 4 == 1)
95                                 printk("\n      ");
96                         printk("  0x%lx", data[n]);
97                 }
98                 if(n > 1)
99                         printk("\n");
100         }
101         else ret = 0;
102
103         *addr = check_init_stack(mm_idp, NULL);
104
105         return ret;
106 }
107
108 long run_syscall_stub(struct mm_id * mm_idp, int syscall,
109                       unsigned long *args, long expected, void **addr,
110                       int done)
111 {
112         unsigned long *stack = check_init_stack(mm_idp, *addr);
113
114         if(done && *addr == NULL)
115                 single_count++;
116
117         *stack += sizeof(long);
118         stack += *stack / sizeof(long);
119
120         *stack++ = syscall;
121         *stack++ = args[0];
122         *stack++ = args[1];
123         *stack++ = args[2];
124         *stack++ = args[3];
125         *stack++ = args[4];
126         *stack++ = args[5];
127         *stack++ = expected;
128         *stack = 0;
129         multi_op_count++;
130
131         if(!done && ((((unsigned long) stack) & ~PAGE_MASK) <
132                      PAGE_SIZE - 10 * sizeof(long))){
133                 *addr = stack;
134                 return 0;
135         }
136
137         return do_syscall_stub(mm_idp, addr);
138 }
139
140 long syscall_stub_data(struct mm_id * mm_idp,
141                        unsigned long *data, int data_count,
142                        void **addr, void **stub_addr)
143 {
144         unsigned long *stack;
145         int ret = 0;
146
147         /* If *addr still is uninitialized, it *must* contain NULL.
148          * Thus in this case do_syscall_stub correctly won't be called.
149          */
150         if((((unsigned long) *addr) & ~PAGE_MASK) >=
151            PAGE_SIZE - (10 + data_count) * sizeof(long)) {
152                 ret = do_syscall_stub(mm_idp, addr);
153                 /* in case of error, don't overwrite data on stack */
154                 if(ret)
155                         return ret;
156         }
157
158         stack = check_init_stack(mm_idp, *addr);
159         *addr = stack;
160
161         *stack = data_count * sizeof(long);
162
163         memcpy(stack + 1, data, data_count * sizeof(long));
164
165         *stub_addr = (void *)(((unsigned long)(stack + 1) & ~PAGE_MASK) +
166                               UML_CONFIG_STUB_DATA);
167
168         return 0;
169 }
170
171 int map(struct mm_id * mm_idp, unsigned long virt, unsigned long len,
172         int r, int w, int x, int phys_fd, unsigned long long offset,
173         int done, void **data)
174 {
175         int prot, ret;
176
177         prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
178                 (x ? PROT_EXEC : 0);
179
180         if(proc_mm){
181                 struct proc_mm_op map;
182                 int fd = mm_idp->u.mm_fd;
183
184                 map = ((struct proc_mm_op) { .op        = MM_MMAP,
185                                              .u         =
186                                              { .mmap    =
187                                                { .addr  = virt,
188                                                  .len   = len,
189                                                  .prot  = prot,
190                                                  .flags = MAP_SHARED |
191                                                  MAP_FIXED,
192                                                  .fd    = phys_fd,
193                                                  .offset= offset
194                                                } } } );
195                 ret = os_write_file(fd, &map, sizeof(map));
196                 if(ret != sizeof(map))
197                         printk("map : /proc/mm map failed, err = %d\n", -ret);
198                 else ret = 0;
199         }
200         else {
201                 unsigned long args[] = { virt, len, prot,
202                                          MAP_SHARED | MAP_FIXED, phys_fd,
203                                          MMAP_OFFSET(offset) };
204
205                 ret = run_syscall_stub(mm_idp, STUB_MMAP_NR, args, virt,
206                                        data, done);
207         }
208
209         return ret;
210 }
211
212 int unmap(struct mm_id * mm_idp, void *addr, unsigned long len, int done,
213           void **data)
214 {
215         int ret;
216
217         if(proc_mm){
218                 struct proc_mm_op unmap;
219                 int fd = mm_idp->u.mm_fd;
220
221                 unmap = ((struct proc_mm_op) { .op      = MM_MUNMAP,
222                                                .u       =
223                                                { .munmap        =
224                                                  { .addr        =
225                                                    (unsigned long) addr,
226                                                    .len         = len } } } );
227                 ret = os_write_file(fd, &unmap, sizeof(unmap));
228                 if(ret != sizeof(unmap))
229                         printk("unmap - proc_mm write returned %d\n", ret);
230                 else ret = 0;
231         }
232         else {
233                 unsigned long args[] = { (unsigned long) addr, len, 0, 0, 0,
234                                          0 };
235
236                 ret = run_syscall_stub(mm_idp, __NR_munmap, args, 0,
237                                        data, done);
238                 if(ret < 0)
239                         printk("munmap stub failed, errno = %d\n", ret);
240         }
241
242         return ret;
243 }
244
245 int protect(struct mm_id * mm_idp, unsigned long addr, unsigned long len,
246             int r, int w, int x, int done, void **data)
247 {
248         struct proc_mm_op protect;
249         int prot, ret;
250
251         prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
252                 (x ? PROT_EXEC : 0);
253
254         if(proc_mm){
255                 int fd = mm_idp->u.mm_fd;
256                 protect = ((struct proc_mm_op) { .op    = MM_MPROTECT,
257                                                  .u     =
258                                                  { .mprotect    =
259                                                    { .addr      =
260                                                      (unsigned long) addr,
261                                                      .len       = len,
262                                                      .prot      = prot } } } );
263
264                 ret = os_write_file(fd, &protect, sizeof(protect));
265                 if(ret != sizeof(protect))
266                         printk("protect failed, err = %d", -ret);
267                 else ret = 0;
268         }
269         else {
270                 unsigned long args[] = { addr, len, prot, 0, 0, 0 };
271
272                 ret = run_syscall_stub(mm_idp, __NR_mprotect, args, 0,
273                                        data, done);
274         }
275
276         return ret;
277 }
278
279 void before_mem_skas(unsigned long unused)
280 {
281 }