Newsgroups: vmsnet.internals
Subject: RE: System service calls & access modes
From: don@zl2tnm.gen.nz
Date: 8 Apr 92 21:14:13 +1200
Organization: The Wolery

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
Newsgroups: vmsnet.pdp-11