From:	MERC::"uunet!CRVAX.SRI.COM!RELAY-INFO-VAX" 18-MAY-1992 20:41:07.03
To:	info-vax@kl.sri.com
CC:	
Subj:	Re: Accessing caller's SP

winalski@tle.enet.dec.com (Paul S. Winalski) writes:
> In article <262@suite.uunet.uu.net>,
> suite!tarbell@uunet.uu.net (Mark A. Tarbell) writes:
> |>
> |>I'm trying to write a routine in macro to retrieve the SP of
> |>the calling routine, but don't see how to do it...
> For the CALLS or CALLG case, the caller's SP will be saved as part of the
> stack frame for the call.  The FP register always points to this stack frame.

[explanation deleted]

Here's a little ditty for those who don't read vmsnet.internals, hacked 
together by Bruce Miller @ TGV & myself (he came up with the idea, so gets
a good deal of the credit ... or the blame 8-).  

The attached module has two routines, kernel_mode() and user_mode() which
put the processor into kernel mode or user mode, without having to place
the kernel mode code in a separate routine.

This code is for educational and amusement purposes only -- while Bruce and
I beat it around the ears a bit with both GCC & VAXC, there's no guarantee
that other languages (or even different revs of GCC and/or VAXC) won't do 
things in ways that are incompatible with these routines.  So, don't come 
crying to me (or Bruce) if you use it in production code and it crashes your 
machine -- you won't get any sympathy (or damages)!  (I'd be interested to 
see why it died though!)  

--
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

		.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>
;
;	NOTE: This is kernel code.  If it dies, it will take your machine
;	      with it.  It comes with no warranty whatsoever.  Losses suffered
;	      from using it come out of your pocket, not ours.  So there!
;
; 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

