From:	MERC::"uunet!WKUVX1.BITNET!MacroMan"  8-APR-1992 10:26:01.02
To:	<raxco!galaxy.dnet!gleeve@UUNET.UU.NET>
CC:	
Subj:	RE: System service calls & access modes

In article <702441165.770861.MILLER@TGV.COM>, MILLER@TGV.COM writes:
> It's worse than that.  Any references to auto variables (FP-relative)
> will fail.  The best solution would probably be to copy a portion of the
> user stack into kernel space and back.  We could substitute the kernel
> FP with the user FP too.  If we did that, we would have rebuild the
> call frame above the call to SYS$CMKRNL and guarentee that the kernel
> code doesn't allocate any more auto variables.

Not at all.  Just save FP for when we return to user mode and point it at
the user mode call frame.  The only gotcha is that if your routine RETs
before returning to user mode, things will crash and burn rather
horribly.  There are probably things I can do about this, but I can't be
bothered.

Allocating more auto variables doesn't seem to be a problem.  GCC at
least just allocates a chunk of stack space big enough for all auto
variables right after the entry mask, so there's no danger of the routine
allocating more.  I don't see any reason for any other language to want
to add extra instructions -- one SUBL2 is plenty.

I thought about duplicating auto variables onto the kernel stack, but
decided that it was a bad idea since there may be a large number of auto
variables, which could overflow the (fixed size) stack.  The thought of
moving everything onto the user stack did occur to me too, but only
briefly..... 8-)

> It seemed more likely that someone would try to nest the calls to
> kmon() so I was opposed to a static variable.

I've attached my cleaned up (ie as *I* write this stuff) code, including
sanity checks, to this message.  Since you can't really nest kernel
modes, I figured recursive calls shouldn't be allowed either.  If I
detect an out of sequence call, I just exit quietly.

> I'll remember to only submit the binary next time.

Well, here's some (shock! horror!) commented code for you folk to have a
play with and pick holes in....

8<-----------------------------------------------------------------------
                .title Kernel switch between user & kernel modes
                .ident /1.0/
;++
; Module:
;       KERNEL.MAR
;       These routines gratuitously abuse the kernel and user stacks
;       to get into and out fo kernel mode without requiring a separate
;       routine.
;
;       For example:
;               <user code>
;               KERNEL_MODE();
;               <kernel code>
;               USER_MODE();
;               <user code>
;
; Usage:
;       kernel_mode();  /* turns on kernel mode */
;       user_mode();    /* turns off kernel mode */
;
; Authors:
;       Original concept by:
;               Bruce R. Miller, MILLER@TGV.COM
;               TGV, Inc.
;               603 Mission St.
;               Santa Cruz, CA 95060
;               (408) 427-4366
;       Hacked about somewhat by:
;               Don Stokes, don@zl2tnm.gen.nz, don@vuw.ac.nz
;               Network Manager, Computing Services Centre
;               Victoria University of Wellington,
;               PO Box 600, Wellington, New Zealand
;               +64-4-495-5052
;
; Date:
;       8-Apr-1992
;--


                .psect kernel_rw, long, wrt, noexe, noshr

savedufp:       .long 0         ; Stores location of user mode caller's FP
savedkfp:       .long 0         ; Stores location of $CMKRNL call's FP


                .psect kernel_re, nowrt, exe, shr

;
; kernel_mode()
;
; This switches us into kernel mode.  We save our FP so we can fiddle with the
; call frame, then jump into kernel mode.  When we get there, the magic begins.
;
                .entry kernel_mode, ^M<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>
                                        ; Save all general registers so we
                                        ; can tweak 'em later

                tstl savedufp           ; Check if we've been here before
                bneq 1$
                movl FP, savedufp       ; Save our FP for later

                pushaw switch_frame
                calls #1, g^SYS$CMKRNL  ; Call switch routine in kernel mode

                cmpl R0, #SS$_NOPRIV    ; if oops then abort.
                bneq 1$
                movl #SS$_NORMAL, R0

1$:             clrl savedufp           ; Make sanity check ok for next time
                ret

;
; This is called from $CMKRNL.  We make it look like we just returned, but
; we're really on the kernel stack now.  Just to fool everybody, we point
; AP & FP back to the user stack.
;
                .entry switch_frame, ^M<>
                movl fp, savedkfp       ; Save our FP for when we return
                movl savedufp, R0       ; Get user FP

                movq 20(R0), R2         ; Get general registers from user mode
                movq 28(R0), R4         ; call frame
                movq 36(R0), R6
                movq 44(R0), R8
                movq 52(R0), R10

                movl 8(R0), AP          ; Get AP so references to args work
                movl 12(R0), FP         ; Get FP so auto variables work

                movl #SS$_NORMAL, R0    ; Declare success
                jmp @16(R0)             ; Jump back into routine (with stack
                                        ; frames intact for later)


;
; user_mode()
;
; Restore user context and come home.
; Basically, we arrange to fall back through the call frames we left behind
; on the way to getting into kernel mode.  But we change the paths slightly...
;
                .entry user_mode, ^M<>
                movl savedufp, R0       ; Sanity check
                beql 1$

                movq r2, 20(R0)         ; Put modified registers back into
                movq r4, 28(R0)         ; user mode call frame
                movq r6, 36(R0)
                movq r8, 44(R0)
                movq r10, 52(R0)

                movl 16(FP), 16(R0)     ; Put PC from our call frame back
                                        ; into the user mode stack frame
                                        ; This makes us RET out of kernel_mode()
                                        ; to the instruction after the
                                        ; user_mode() call.

                movl savedkfp, FP       ; Restore FP to point back to kernel
                                        ; frame, so we RET from here back thru
                                        ; the $CMKRNL and kernel_mode() calls.

1$:             movl #SS$_NORMAL, R0    ; Declare success
                ret

                .end
8<-----------------------------------------------------------------------

Don Stokes, ZL2TNM (DS555)                       don@zl2tnm.gen.nz (home)
Network Manager, Computing Services Centre           don@vuw.ac.nz (work)
Victoria University of Wellington, New Zealand             +64-4-495-5052
