Re: fork() problems.

From: Tim Newsham <newsham_at_nospam.org>
Date: Thu Dec 01 1994 - 02:10:36 PST

> (I not 100% certain, but I'm fairly sure this is right)
>
> When setjmp() is called to save the kernel state of a thread, esp
> points to the return address in the calling function. This is
> standard i386 calling convention. This is the value of esp saved
> in the jmpbuf (in this case the jmpbuf is t_uregs). longjmp() loads
> the esp value from the trapframe and saves the return address at
> where esp points to (note that it's doesn't push, it overwrites).
> The last thing it does is execute a ret instruction which pops this
> return address off the stack.

yes I understand the distinction between the threads kernel and user
saved state.

> > This buffer is later used by retuser() to warp to the new context
> > (restore registers from t_kregs).
>
> Not really. t_kregs is the *kernel* state of the thread as saved
> by setjmp (or set up by dup_stack). resume() is used to load this.
> The user state is stored in a trapframe and loaded by retuser().
>
> > When the longjmp occurs the
> > instruction pointer gets set to "retuser" which causes the program
> > to enter the retuser() function (again). How does a process
> > continue execution if it keeps warping back to retuser()?

argh! somehow I confused retuser (locore.s) and resume() (machproc.c).
Resume() does the warp out to another context, retuser does the
return to user-land, and this all makes sense (of course it does :).
Ok. That clears up a few things.

> > At any rate here is the code I am working with right now:
> >
> > /*
> > * New entity returns with 0 value; SP is one lower so that
> > * the resume() path has a place to write its return address.
> > * This simulates the normal context switch mechanism of
> > * setjmp/longjmp.
> > */
> > new->t_kregs->pc = (ulong)retuser;
> > new->t_kregs->sp = ((ulong)(new->t_uregs)) - sizeof(ulong);
> > new->t_uregs->f_regs[REG_D0] = 0;
> >
> > you'll notice this is basically just a blind copy of the above.
>
> What does your setjmp/longjmp do?

my setjmp:
        .globl _setjmp
_setjmp:
        movl sp@(4), a0 | get pointer
        moveml #0xfefe, a0@ | save a1-a7/d1-d7
        movl sp@, a0@(56) | store return address
        movel #0, d0 | return value
        rts

Basically saves all registers except d0 and a0. This is because
d0 just changes again at the end of longjmp (return value) and a0
is used as scratch anyway (so I use it as scratch). It also saves
the return value.
My longjmp is also pretty simple:
        .globl _longjmp
_longjmp:
        movl sp@(8), d0 | get return value
        movl sp@(4), a0 | get pointer
        moveml a0@, #0xfefe | restore a1-a7/d1-d7
        movl a0@(56), sp@ | restore return address
        rts

> > in resume():
> > /*
> > * Make kernel stack come in on our own stack now. This
> > * isn't used until we switch out to user mode, at which
> > * time our stack will always be empty.
> > * XXX esp is overkill; only esp0 should ever be used.
> > */
> > tss->esp0 = tss->esp = (ulong)
> > ((char *)(t->t_kstack) + KSTACK_SIZE);
> >
> > This one has me really confused. I'm not to clear on what the tss
> > does. In my port I have this stuff commented out. I have a feeling
> > this has something to do with my problems and the 1 word space left
> > on the stack after the longjmp.
>
> I think this is the kernel stack which the i386 switches to on
> receipt of an interrupt/exception/whatever. I believe the 68k has
> a supervisor stack of some description to do this. If you're current
> doing it on the user stack you shoudln't, because you can't trust it
> to be right.

The 680x0 has two stack pointers. Both are refered to by "a7" or
"sp". When the supervisor bit is set this refers to the SSP (supervisor
stack pointer) otherwise it refers to USP (user stack pointer). There
is also an instruction (priveledged) to read the USP. (some chips
have more than 1 supervisor stack I believe).
In my trap/interupt handlers USP is overwritten onto the a7 position
of the trap frame after all registers are pushed, and read back from
the a7 position before popping the other registers (a7 is never really
popped from the stack). If the above code is just to set the user
stack pointer then I guess I already covered this.

> Cheers,
>
> Christopher.

Thanks for your response.

I'm still a bit confused with respect to the extra decrement on the
new stack created by dup_stack(). I wouldn't be suprised if this had
something to do with my problems. As far as I can tell the stack
pointer should point to the bottom most value of the trap frame when
a thread enters the kernel, this is the same condition that should
exist when the thread returns to userland through retuser(). What
I don't understand though is - if this was the case then why would
any processes run at all? If the stack pointer was at the wrong
position after new processes were created the registers would be
read all wrong and the hardware trapframe read by the return-from-
exception would be all wrong. This leads me to believe that it
is currently correct.

                               Tim N.
Received on Thu Dec 1 01:47:26 1994

This archive was generated by hypermail 2.1.8 : Thu Sep 22 2005 - 15:12:10 PDT