plan9fox/sys/src/9/teg2/syscall.c
cinap_lenrek 8d51e7fa1a kernel: implement portable userinit() and simplify process creation
replace machine specific userinit() by a portable
implemntation that uses kproc() to create the first
process. the initcode text is mapped using kmap(),
so there is no need for machine specific tmpmap()
functions.

initcode stack preparation should be done in init0()
where the stack is mapped and can be accessed directly.

replacing the machine specific userinit() allows some
big simplifications as sysrfork() and kproc() are now
the only callers of newproc() and we can avoid initializing
fields that we know are being initialized by these
callers.

rename autogenerated init.h and reboot.h headers.
the initcode[] and rebootcode[] blobs are now in *.i
files and hex generation was moved to portmkfile. the
machine specific mkfile only needs to specify how to
build rebootcode.out and initcode.out.
2020-01-26 19:01:36 +01:00

357 lines
7.3 KiB
C

/* we use l1 and l2 cache ops to help stability. */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "../port/systab.h"
#include <tos.h>
#include "ureg.h"
#include "arm.h"
enum {
Psrsysbits = PsrMask | PsrDfiq | PsrDirq | PsrDasabt | PsrMbz,
};
typedef struct {
uintptr ip;
Ureg* arg0;
char* arg1;
char msg[ERRMAX];
Ureg* old;
Ureg ureg;
} NFrame;
/*
* Return user to state before notify()
*/
static void
noted(Ureg* cur, uintptr arg0)
{
NFrame *nf;
Ureg *nur;
qlock(&up->debug);
if(arg0 != NRSTR && !up->notified){
qunlock(&up->debug);
pprint("call to noted() when not notified\n");
pexit("Suicide", 0);
}
up->notified = 0;
fpunoted();
nf = up->ureg;
/* sanity clause */
if(!okaddr((uintptr)nf, sizeof(NFrame), 0)){
qunlock(&up->debug);
pprint("bad ureg in noted %#p\n", nf);
pexit("Suicide", 0);
}
/* don't let user change system flags */
nur = &nf->ureg;
nur->psr &= Psrsysbits;
nur->psr |= cur->psr & ~Psrsysbits;
memmove(cur, nur, sizeof(Ureg));
switch((int)arg0){
case NCONT:
case NRSTR:
if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
qunlock(&up->debug);
pprint("suicide: trap in noted\n");
pexit("Suicide", 0);
}
up->ureg = nf->old;
qunlock(&up->debug);
break;
case NSAVE:
if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
qunlock(&up->debug);
pprint("suicide: trap in noted\n");
pexit("Suicide", 0);
}
qunlock(&up->debug);
splhi();
nf->arg1 = nf->msg;
nf->arg0 = &nf->ureg;
nf->ip = 0;
cur->sp = (uintptr)nf;
cur->r0 = (uintptr)nf->arg0;
break;
default:
up->lastnote.flag = NDebug;
/*FALLTHROUGH*/
case NDFLT:
qunlock(&up->debug);
if(up->lastnote.flag == NDebug)
pprint("suicide: %s\n", up->lastnote.msg);
pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
}
}
/*
* Call user, if necessary, with note.
* Pass user the Ureg struct and the note on his stack.
*/
int
notify(Ureg* ureg)
{
int l;
Note *n;
u32int s;
uintptr sp;
NFrame *nf;
if(up->procctl)
procctl();
if(up->nnote == 0)
return 0;
fpunotify(ureg);
s = spllo();
qlock(&up->debug);
up->notepending = 0;
n = &up->note[0];
if(strncmp(n->msg, "sys:", 4) == 0){
l = strlen(n->msg);
if(l > ERRMAX-23) /* " pc=0x0123456789abcdef\0" */
l = ERRMAX-23;
snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->pc);
}
if(n->flag != NUser && (up->notified || up->notify == 0)){
qunlock(&up->debug);
if(n->flag == NDebug)
pprint("suicide: %s\n", n->msg);
pexit(n->msg, n->flag != NDebug);
}
if(up->notified){
qunlock(&up->debug);
splhi();
return 0;
}
if(up->notify == nil){
qunlock(&up->debug);
pexit(n->msg, n->flag != NDebug);
}
if(!okaddr((uintptr)up->notify, 1, 0)){
qunlock(&up->debug);
pprint("suicide: notify function address %#p\n", up->notify);
pexit("Suicide", 0);
}
sp = ureg->sp - sizeof(NFrame);
if(!okaddr(sp, sizeof(NFrame), 1)){
qunlock(&up->debug);
pprint("suicide: notify stack address %#p\n", sp);
pexit("Suicide", 0);
}
nf = (void*)sp;
memmove(&nf->ureg, ureg, sizeof(Ureg));
nf->old = up->ureg;
up->ureg = nf;
memmove(nf->msg, up->note[0].msg, ERRMAX);
nf->arg1 = nf->msg;
nf->arg0 = &nf->ureg;
nf->ip = 0;
ureg->sp = sp;
ureg->pc = (uintptr)up->notify;
ureg->r0 = (uintptr)nf->arg0;
up->notified = 1;
up->nnote--;
memmove(&up->lastnote, &up->note[0], sizeof(Note));
memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
qunlock(&up->debug);
splx(s);
l1cache->wb(); /* is this needed? */
return 1;
}
void
syscall(Ureg* ureg)
{
char *e;
u32int s;
ulong sp;
long ret;
int i, scallnr;
vlong startns, stopns;
if(!userureg(ureg))
panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux",
ureg->pc, ureg->r14, ureg->psr);
cycles(&up->kentry);
m->syscall++;
up->insyscall = 1;
up->pc = ureg->pc;
up->dbgreg = ureg;
scallnr = ureg->r0;
up->scallnr = scallnr;
spllo();
sp = ureg->sp;
if(up->procctl == Proc_tracesyscall){
/*
* Redundant validaddr. Do we care?
* Tracing syscalls is not exactly a fast path...
* Beware, validaddr currently does a pexit rather
* than an error if there's a problem; that might
* change in the future.
*/
if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
validaddr(sp, sizeof(Sargs)+BY2WD, 0);
syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD));
up->procctl = Proc_stopme;
procctl();
if (up->syscalltrace)
free(up->syscalltrace);
up->syscalltrace = nil;
}
up->nerrlab = 0;
ret = -1;
startns = todget(nil);
l1cache->wb(); /* system is more stable with this */
if(!waserror()){
if(scallnr >= nsyscall){
pprint("bad sys call number %d pc %#lux\n",
scallnr, ureg->pc);
postnote(up, 1, "sys: bad sys call", NDebug);
error(Ebadarg);
}
if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
validaddr(sp, sizeof(Sargs)+BY2WD, 0);
up->s = *((Sargs*)(sp+BY2WD));
up->psstate = sysctab[scallnr];
/* iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */
ret = systab[scallnr]((va_list)up->s.args);
poperror();
}else{
/* failure: save the error buffer for errstr */
e = up->syserrstr;
up->syserrstr = up->errstr;
up->errstr = e;
}
if(up->nerrlab){
print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab);
for(i = 0; i < NERR; i++)
print("sp=%#p pc=%#p\n",
up->errlab[i].sp, up->errlab[i].pc);
panic("error stack");
}
/*
* Put return value in frame. On the x86 the syscall is
* just another trap and the return value from syscall is
* ignored. On other machines the return value is put into
* the results register by caller of syscall.
*/
ureg->r0 = ret;
if(up->procctl == Proc_tracesyscall){
stopns = todget(nil);
sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns);
s = splhi();
up->procctl = Proc_stopme;
procctl();
splx(s);
if(up->syscalltrace)
free(up->syscalltrace);
up->syscalltrace = nil;
}
up->insyscall = 0;
up->psstate = 0;
if(scallnr == NOTED)
noted(ureg, *(ulong*)(sp+BY2WD));
splhi();
if(scallnr != RFORK && (up->procctl || up->nnote))
notify(ureg);
l1cache->wb(); /* system is more stable with this */
/* if we delayed sched because we held a lock, sched now */
if(up->delaysched){
sched();
splhi();
}
kexit(ureg);
}
uintptr
execregs(uintptr entry, ulong ssize, ulong nargs)
{
ulong *sp;
Ureg *ureg;
sp = (ulong*)(USTKTOP - ssize);
*--sp = nargs;
ureg = up->dbgreg;
// memset(ureg, 0, 15*sizeof(ulong));
ureg->r13 = (ulong)sp;
ureg->pc = entry;
//print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs);
allcache->wbse(ureg, sizeof *ureg); /* is this needed? */
/*
* return the address of kernel/user shared data
* (e.g. clock stuff)
*/
return USTKTOP-sizeof(Tos);
}
void
sysprocsetup(Proc* p)
{
fpusysprocsetup(p);
}
/*
* Craft a return frame which will cause the child to pop out of
* the scheduler in user mode with the return register zero. Set
* pc to point to a l.s return function.
*/
void
forkchild(Proc *p, Ureg *ureg)
{
Ureg *cureg;
p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg);
p->sched.pc = (ulong)forkret;
cureg = (Ureg*)(p->sched.sp);
memmove(cureg, ureg, sizeof(Ureg));
/* syscall returns 0 for child */
cureg->r0 = 0;
}