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