Subject: Re: trap frames <[email-masked]>

From: [email-masked]

> Hi,
>   I'm going over some things and I came across something I dont fully
> understand.  Why are the stack frames set up the way they are?
> 
> struct frame {
>         int     f_regs[16];
>         short   f_pad;
>         short   f_stackadj;
>         u_short f_sr;
>         u_int   f_pc;
>         u_short f_format:4,
>                 f_vector:12;
>         union F_u {
>  
> What is the purpose of the pad and stackadj fields?
> If there was just one field I might think that it is used
> to make sure other fields are aligned but thats 4 extra bytes.

F_pad is used for alignment.  The hardware frame begins on f_sr
the rest is place on the stack at the point of interrupt/exception.

> Another thing is sometimes the usp is saved in f_regs and at
> other times the ssp is saved.  I'm not so clear as to why
> the usp is not always saved.  It also seems that if there
> is a trap in supervisor mode then the usp gets written in
> when the ssp should.  I guess this isnt a problem since
> we're probably talking about a panic condition (?).

Nope exceptions and interrupts happen in supervisor all the time.

Your confusion perhaps arises from misunderstanding the frame.
Everything below (in memory) frame.f_sr is the software frame, i.e. its
there becuase we put it there and the HW doesn't care anything about
it.

Note that when SSP is saved in f_regs, its really just popped
right back out like so:

	movl	a0,sp@(FR_SP)	| save new SSP in f_regs
	movml	sp@+,0x7fff	| pop all but stack pointer
	movel	sp@,sp		| pop saved SSP from 2 lines up.
	rte			| return to previous supervisor execution.

The only thing that uses stack adjustments are things that do *not*
want the kernel to continue to execute the code that causes the
exception.  Basically when copying from a user buffer or similar you
want to catch faults and then quit copying.  So you setup an `onfault'
handler and if a fault occurs then the fault's HW frame is replaced with
a Four-Word Stack Frame.  This stack frame has only f_sr, f_pc, f_format
f_vector.  f_sr will be what it was at the time of the exception, f_pc
will be set to the fault handler and f_format will be set to 0.  The
field f_stkadj is set in trapcpfault() in trap.c to the size of the
current exception frame, then in locore.s the four-word fram is built
over top of this frame as it is not needed.  When the rte is executed the
stack pointer is just incremented by the size of the HW frame, 8 bytes.
And thus the stack pointer is now where it was when the exception occured
excpet execution is resumed in the `onfault' handler.  The `onfault'
handler returns as if it where the original function called.

Say for instance you are copying in ioctl() args.  You call the function
copyin() from ioctl() it then sets up an `onfault' handler.  All this 
function will do is set d0 to EFAULT and return.  So if a fault occurs
the function call in ioctl() to copyin() returns EFAULT.

Make sense?

Chris.