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