1 /* we use l1 and l2 cache ops to help stability. */ 2 3 #include "u.h" 4 #include "../port/lib.h" 5 #include "mem.h" 6 #include "dat.h" 7 #include "fns.h" 8 #include "../port/error.h" 9 #include "../port/systab.h" 10 11 #include <tos.h> 12 #include "ureg.h" 13 14 #include "arm.h" 15 16 enum { 17 Psrsysbits = PsrMask | PsrDfiq | PsrDirq | PsrDasabt | PsrMbz, 18 }; 19 20 typedef struct { 21 uintptr ip; 22 Ureg* arg0; 23 char* arg1; 24 char msg[ERRMAX]; 25 Ureg* old; 26 Ureg ureg; 27 } NFrame; 28 29 /* 30 * Return user to state before notify() 31 */ 32 static void 33 noted(Ureg* cur, uintptr arg0) 34 { 35 NFrame *nf; 36 Ureg *nur; 37 38 qlock(&up->debug); 39 if(arg0 != NRSTR && !up->notified){ 40 qunlock(&up->debug); 41 pprint("call to noted() when not notified\n"); 42 pexit("Suicide", 0); 43 } 44 up->notified = 0; 45 fpunoted(); 46 47 nf = up->ureg; 48 49 /* sanity clause */ 50 if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){ 51 qunlock(&up->debug); 52 pprint("bad ureg in noted %#p\n", nf); 53 pexit("Suicide", 0); 54 } 55 56 /* don't let user change system flags */ 57 nur = &nf->ureg; 58 nur->psr &= Psrsysbits; 59 nur->psr |= cur->psr & ~Psrsysbits; 60 61 memmove(cur, nur, sizeof(Ureg)); 62 63 switch((int)arg0){ 64 case NCONT: 65 case NRSTR: 66 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){ 67 qunlock(&up->debug); 68 pprint("suicide: trap in noted\n"); 69 pexit("Suicide", 0); 70 } 71 up->ureg = nf->old; 72 qunlock(&up->debug); 73 break; 74 case NSAVE: 75 if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){ 76 qunlock(&up->debug); 77 pprint("suicide: trap in noted\n"); 78 pexit("Suicide", 0); 79 } 80 qunlock(&up->debug); 81 82 splhi(); 83 nf->arg1 = nf->msg; /* arg 1 is string */ 84 nf->arg0 = &nf->ureg; /* arg 0 XX(FP) is ureg* */ 85 nf->ip = 0; 86 cur->sp = PTR2UINT(nf); 87 cur->r0 = PTR2UINT(nf->arg0); /* arg 0 in reg is ureg* */ 88 break; 89 default: 90 pprint("unknown noted arg %#p\n", arg0); 91 up->lastnote.flag = NDebug; 92 /*FALLTHROUGH*/ 93 case NDFLT: 94 if(up->lastnote.flag == NDebug){ 95 qunlock(&up->debug); 96 pprint("suicide: %s\n", up->lastnote.msg); 97 } 98 else 99 qunlock(&up->debug); 100 pexit(up->lastnote.msg, up->lastnote.flag != NDebug); 101 } 102 } 103 104 /* 105 * Call user, if necessary, with note. 106 * Pass user the Ureg struct and the note on his stack. 107 */ 108 int 109 notify(Ureg* ureg) 110 { 111 int l; 112 Note *n; 113 u32int s; 114 uintptr sp; 115 NFrame *nf; 116 117 if(up->procctl) 118 procctl(up); 119 if(up->nnote == 0) 120 return 0; 121 122 fpunotify(ureg); 123 124 s = spllo(); 125 qlock(&up->debug); 126 127 up->notepending = 0; 128 n = &up->note[0]; 129 if(strncmp(n->msg, "sys:", 4) == 0){ 130 l = strlen(n->msg); 131 if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */ 132 l = ERRMAX-23; 133 snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->pc); 134 } 135 136 if(n->flag != NUser && (up->notified || up->notify == 0)){ 137 if(n->flag == NDebug) 138 pprint("suicide: %s\n", n->msg); 139 qunlock(&up->debug); 140 pexit(n->msg, n->flag != NDebug); 141 } 142 143 if(up->notified){ 144 qunlock(&up->debug); 145 splhi(); 146 return 0; 147 } 148 149 if(up->notify == nil){ 150 qunlock(&up->debug); 151 pexit(n->msg, n->flag != NDebug); 152 } 153 if(!okaddr(PTR2UINT(up->notify), 1, 0)){ 154 pprint("suicide: notify function address %#p\n", up->notify); 155 qunlock(&up->debug); 156 pexit("Suicide", 0); 157 } 158 159 sp = ureg->sp - sizeof(NFrame); 160 if(!okaddr(sp, sizeof(NFrame), 1)){ 161 qunlock(&up->debug); 162 pprint("suicide: notify stack address %#p\n", sp); 163 pexit("Suicide", 0); 164 } 165 166 nf = UINT2PTR(sp); 167 memmove(&nf->ureg, ureg, sizeof(Ureg)); 168 nf->old = up->ureg; 169 up->ureg = nf; 170 memmove(nf->msg, up->note[0].msg, ERRMAX); 171 nf->arg1 = nf->msg; /* arg 1 is string */ 172 nf->arg0 = &nf->ureg; /* arg 0 XX(FP) is ureg* */ 173 ureg->r0 = PTR2UINT(nf->arg0); /* arg 0 in r0 is ureg* */ 174 nf->ip = 0; 175 176 ureg->sp = sp; 177 ureg->pc = PTR2UINT(up->notify); 178 179 up->notified = 1; 180 up->nnote--; 181 memmove(&up->lastnote, &up->note[0], sizeof(Note)); 182 memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); 183 184 qunlock(&up->debug); 185 splx(s); 186 187 l1cache->wb(); /* is this needed? */ 188 return 1; 189 } 190 191 void 192 syscall(Ureg* ureg) 193 { 194 char *e; 195 u32int s; 196 ulong sp; 197 long ret; 198 int i, scallnr; 199 vlong startns, stopns; 200 201 if(!userureg(ureg)) 202 panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux", 203 ureg->pc, ureg->r14, ureg->psr); 204 205 cycles(&up->kentry); 206 207 m->syscall++; 208 up->insyscall = 1; 209 up->pc = ureg->pc; 210 up->dbgreg = ureg; 211 212 scallnr = ureg->r0; 213 up->scallnr = scallnr; 214 if(scallnr == RFORK) 215 fpusysrfork(ureg); 216 spllo(); 217 sp = ureg->sp; 218 219 if(up->procctl == Proc_tracesyscall){ 220 /* 221 * Redundant validaddr. Do we care? 222 * Tracing syscalls is not exactly a fast path... 223 * Beware, validaddr currently does a pexit rather 224 * than an error if there's a problem; that might 225 * change in the future. 226 */ 227 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) 228 validaddr(sp, sizeof(Sargs)+BY2WD, 0); 229 230 syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD)); 231 up->procctl = Proc_stopme; 232 procctl(up); 233 if (up->syscalltrace) 234 free(up->syscalltrace); 235 up->syscalltrace = nil; 236 } 237 238 up->nerrlab = 0; 239 ret = -1; 240 startns = todget(nil); 241 242 l1cache->wb(); /* system is more stable with this */ 243 if(!waserror()){ 244 if(scallnr >= nsyscall){ 245 pprint("bad sys call number %d pc %#lux\n", 246 scallnr, ureg->pc); 247 postnote(up, 1, "sys: bad sys call", NDebug); 248 error(Ebadarg); 249 } 250 251 if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD)) 252 validaddr(sp, sizeof(Sargs)+BY2WD, 0); 253 254 up->s = *((Sargs*)(sp+BY2WD)); 255 up->psstate = sysctab[scallnr]; 256 257 /* iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */ 258 259 ret = systab[scallnr](up->s.args); 260 poperror(); 261 }else{ 262 /* failure: save the error buffer for errstr */ 263 e = up->syserrstr; 264 up->syserrstr = up->errstr; 265 up->errstr = e; 266 } 267 if(up->nerrlab){ 268 print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab); 269 for(i = 0; i < NERR; i++) 270 print("sp=%#p pc=%#p\n", 271 up->errlab[i].sp, up->errlab[i].pc); 272 panic("error stack"); 273 } 274 275 /* 276 * Put return value in frame. On the x86 the syscall is 277 * just another trap and the return value from syscall is 278 * ignored. On other machines the return value is put into 279 * the results register by caller of syscall. 280 */ 281 ureg->r0 = ret; 282 283 if(up->procctl == Proc_tracesyscall){ 284 stopns = todget(nil); 285 up->procctl = Proc_stopme; 286 sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns); 287 s = splhi(); 288 procctl(up); 289 splx(s); 290 if(up->syscalltrace) 291 free(up->syscalltrace); 292 up->syscalltrace = nil; 293 } 294 295 up->insyscall = 0; 296 up->psstate = 0; 297 298 if(scallnr == NOTED) 299 noted(ureg, *(ulong*)(sp+BY2WD)); 300 301 splhi(); 302 if(scallnr != RFORK && (up->procctl || up->nnote)) 303 notify(ureg); 304 305 l1cache->wb(); /* system is more stable with this */ 306 307 /* if we delayed sched because we held a lock, sched now */ 308 if(up->delaysched){ 309 sched(); 310 splhi(); 311 } 312 kexit(ureg); 313 } 314 315 long 316 execregs(ulong entry, ulong ssize, ulong nargs) 317 { 318 ulong *sp; 319 Ureg *ureg; 320 321 sp = (ulong*)(USTKTOP - ssize); 322 *--sp = nargs; 323 324 ureg = up->dbgreg; 325 // memset(ureg, 0, 15*sizeof(ulong)); 326 ureg->r13 = (ulong)sp; 327 ureg->pc = entry; 328 //print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs); 329 allcache->wbse(ureg, sizeof *ureg); /* is this needed? */ 330 331 /* 332 * return the address of kernel/user shared data 333 * (e.g. clock stuff) 334 */ 335 return USTKTOP-sizeof(Tos); 336 } 337 338 void 339 sysprocsetup(Proc* p) 340 { 341 fpusysprocsetup(p); 342 } 343 344 /* 345 * Craft a return frame which will cause the child to pop out of 346 * the scheduler in user mode with the return register zero. Set 347 * pc to point to a l.s return function. 348 */ 349 void 350 forkchild(Proc *p, Ureg *ureg) 351 { 352 Ureg *cureg; 353 354 p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg); 355 p->sched.pc = (ulong)forkret; 356 357 cureg = (Ureg*)(p->sched.sp); 358 memmove(cureg, ureg, sizeof(Ureg)); 359 360 /* syscall returns 0 for child */ 361 cureg->r0 = 0; 362 363 /* Things from bottom of syscall which were never executed */ 364 p->psstate = 0; 365 p->insyscall = 0; 366 367 fpusysrforkchild(p, cureg, up); 368 } 369