dstevens@sitvxb (David L Stevens) (07/08/87)
Greetings,
I am trying to modify FINGER to go out and get the Server and Port
name from the UCB blocks for the LAT device, we are using LAT-Plus. I
have below the code that I use, the problem I am having is that the MOVC3
statement in the Kernal Mode code, crashes the system every time I run it.
If I take out the MOVC3 statements, and run the program the Length of the
port string, the length of the Server string are retrieved no problem. Also
I checked, and R8 does point at the beginning of the LAT information area in
the UCB block. If anyone can tell me what I am doing wrong here please let
me know, I'm stumpted. Remember this is KERNAL Mode code that is broken, and
as such will crash the system, and requires CMKRNL priv, so be carefull when
you test it/(hopfully) fix it.
------------------------------------------------------------------------------
LAT_PLUS_INFO.MAR
.title lat_plus_info
;;; lat_info - get information about LAT terminals (privileged)
;++
; Module : LAT_INFO.MAR
; Author : David L. Stevens
; Stevens Institute of Technology
;
; This module was based on the TTUCB.MAR file of the Finger system. I
; gratefully thank the author of that program for the help that it
; gave me while hacking out this program. This program requires
; CMKRNL privileges to run. It will retrieve the Server name, the
; Server Port Number and the Server's Ethernet address. Hopefully
; DEC will not modify the format of a LAT Plus UCB and destroy the offsets.
;
; revision history:
;
; 7-July-1987 Create this Module. [DLS]
;
;--
.library /sys$library:lib.mlb/
$ucbdef
$ddbdef
$ipldef
$ttyucbdef
;
; Useful constants
;
num_args = 4 ;; Number of arguments
ap_numb = 4 ;; Offset to unit number
ap_name = 8 ;; Offset to return buff addr
ap_port = 12 ;; Offset to return port buffer
ap_eth = 16 ;; Offset to return eth buffer
ucb$b_srv_info = ^x134 ;; offset in UCB to server info
srv_info_port = 1
srv_info_name = 2
;;
;; LAT_INFO
;;
;; this routine is called to get information about a specified lat line
;;
;; r2 - LTA device number to get info on
;; r3 - Pointer to return buffer for the Server name
;; r4 - Pointer to return buffer for the Server port
;; r5 - Pointer to return buffer for the Server ethernet address
;; r6 - Scratch
;;
.entry lat_info,^M<r2,r3,r4,r5,r6>
cmpl #num_args,(ap) ; need this many arguments
beql 10$ ; branch if correct
movl #ss$_badparam,r0 ; setup error status
ret ; go give him the bad news
10$: clrl eth_addr ; Clear out the --
clrw eth_addr+4 ; -- ethernet addr buffer
clrb srv_name_len ; Clear out the server name len
clrb port_len ; Clear out the port # buff
$lkwset_s inadr=lock1 ; lock routine into working set
cmpl #ss$_wasclr,r0 ; did we lock the pages?
beql 20$ ; branch if so
cmpl #ss$_wasset,r0 ; were they already locked?
bneq err ; if not, then error
20$: movw ap_numb(ap),unit_numb ; Get the needed unit number
$cmkrnl_s routin=latinfkrn ; do the privileged stuff
movl r0,r6 ; save status for later
$ulwset_s inadr=lock1 ; unlock kernel routine
cmpl #ss$_wasclr,r0 ; did we unlock the pages?
beql info ; branch if so
cmpl #ss$_wasset,r0 ; were they already unlocked?
beql info ; branch if so
err: ret ; ERROR; that's all folks!
info: tstb srv_name_len
beql 40$
movl ap_name(ap),r3 ; Get the return name buf addr
movc3 srv_name_len,srv_name_str,(r3)
40$: tstb port_len
beql 50$
movl ap_port(ap),r4 ; Get the return port buf addr
movc3 port_len,port_numb,(r4)
50$: tstl eth_addr
beql 60$
movl ap_eth(ap),r5 ; Get the return eth-adr buf adr
movl eth_addr,(r5)
movw eth_addr+4,4(r5)
60$: movl r6,r0
ret ; that's all folks!
;
; Descriptor used to lock kernel routine into our working set
;
lock1: .address latinfkrn
.address latinfkrnend
;;
;; LATINFKRN
;;
;; Entry point for the privileged search and slurp. The following
;; registers are used:
;;
;; r0 - return status
;; r6 - Port Buffer
;; r7 - Server name buffer
;; r8 - Start address of LAT Plus Info
;; r9 - address of current LAT UCB
;; r10 - address of current DDB
;;
.entry latinfkrn,^M<r6,r7,r8,r9,r10>
jsb sch$iolockr ;; lock io data base for read
dsbint #ipl$_synch ;; raise ipl while mucking...
;;
;; First we need to search the DDB's for the one for the desired device
;;
movl ioc$gl_devlist,r10 ;; ddb list in r10
movl #port_numb, r6 ;; Port buffer address
movl #srv_name_str, r7 ;; Server name buffer address
brb jump1 ;; jump into middle loop
loop1: movl ddb$l_link(r10),r10 ;; look at next ddb
jump1: beql finish ;; already did last controller
cmpc3 #3,dev_name,ddb$t_name+1(r10) ;; Is it an LTA DDB ???
bneq loop1 ;; No try for the next one.
;;
;; Now we traverse down the string of UCB's for the correct unit
;;
movl ddb$l_ucb(r10),r9 ;; fetch first ucb for this ddb
brb jump2 ;; jump into innermost loop
loop2: movl ucb$l_link(r9),r9 ;; look at next ucb
jump2: beql finish ;; no more units left
cmpw unit_numb,ucb$w_unit(r9) ;; is this the right unit number
bneq loop2 ;; next ucb if not
;;
;; Establish a pointer to the beginning of the LAT Plus Data
;;
addl3 #ucb$b_srv_info, r9, r8
;;
;; Now we get the information we need about the LAT line
;;
movzbw (r8), port_len ;; Get the length of port string
beql srvinf ;; If Zero length then branch
;;
;; Move the data into the storage area
;;
movc3 port_len, srv_info_port(r8), (r6)
;;
;; Get the Server name information
;;
srvinf:
addl2 port_len, r8 ;; Offset to srv name length
;;
;; Get the length of the string
;;
movzbw srv_info_port(r8), srv_name_len
beql finish ;; If Zero length then branch
;;
;; Get the data
;;
;; movc3 srv_name_len, srv_info_name(r8), (r7)
;;
;; Now set up for a return
;;
finish: jsb sch$iounlock ;; unlock io data base
enbint ;; restore ipl
movl #ss$_normal,r0 ;; set up normal return
ret ;; back to user mode
;;
;; Storage locations
;;
dev_name: .asciz "LTA" ;; Generic device name
unit_numb: .blkw 1 ;; Current unit number
port_len: .blkw 1 ;; Length of port string
port_numb: .blkb 16 ;; Remote port string
srv_name_len: .blkw 1 ;; Length of server name string
srv_name_str: .blkb 16 ;; Server name string
eth_addr: .blkw 3 ;; Servers Ethernet Address
latinfkrnend: ;; end of locked memory
.end
-------------------------------------------------------------------------------
TEST.C
#include <stdio.h>
main()
{
char name[20];
char port[20];
int ether[2];
int i = 0;
ether[0] = 0; ether[1] = 0;
for (;i<20;i++)
{
name[i] = NULL;
port[i] = NULL;
}
lat_info(1,&name[0], &port[0], ðer[0]);
printf(" LTA1, Server name %s, port %s, ether %x%x. \n",
name, port, ether[0],ether[1]);
}
-------------------------------------------------------------------------------
The command to link the programs together is:
$ LINK TEST,LAT_PLUS_INFO,SYS$SYSTEM:SYS.STB/SELECT
Any and all help will be gratefully appreciated.
- Thanx
- Dave Stevens
- Systems Programmer
- Stevens Institute of Technology
- Address:
BITnet: DSTEVENS@SITVXA
ARPAnet: DSTEVENS%SITVXA.BITNET@WISCVM.ARPA
CCnet: SITVXA::DSTEVENSgkn@SDS.SDSC.EDU (07/09/87)
From: dstevens (David L Stevens) @ sitvxb
Subject: Help with a Kernal mode macro program
Date: Wed, 8 Jul 87 12:29:44 EDT
I am trying to modify FINGER to go out and get the Server and Port
name from the UCB blocks for the LAT device, we are using LAT-Plus.
I have below the code that I use, the problem I am having is that the
MOVC3 statement in the Kernal Mode code, crashes the system every time
I run it.
Well, here are a few problems with this:
You don't really need to lock any pages in your working set; you don't
need to run above IPL 2.
$lkwset_s inadr=lock1 ; lock routine into working set
Owning the I/O database mutex is a sufficient lock on the I/O database; you
don't need to raise IPL all the way to SYNCH. This just makes things more
difficult. Also, you need to have your PCB address in R4 when you attempt
to gain ownership of a mutex. I suspect that you were just lucky and R4
already contained your PCB address...
jsb sch$iolockr ;; lock io data base for read
dsbint #ipl$_synch ;; raise ipl while mucking...
Also, all references to locations inside VMS should use the general addressing
mode (to ensure that the code is PIC. Writing non-PIC code is a hard habit
to break if you're a PDP-11 programmer ...)
MOVL G^SCH$GL_CURPCB,R4 ;;Grab my PCB address
JSB G^SCH$IOLOCKR ;;Lock the I/O database for reading
;;
;; First we need to search the DDB's for the one for the desired device
;;
If you make a descriptor of the device name and unit number, you can save
yourself tons of work by calling IOC$SEARCHDEV, eg:
LTA_DEV: .Ascid "LTA1"
MOVAQ LTA_DEV,R1 ;;Here's the device to search for
JSB G^IOC$SEARCHDEV ;;Go find the UCB
You must own the I/O database mutex to call IOC$SEARCHDEV. It returns status
in R0 (low bit clear means it couldn't find the device). The device's UCB
address will be returned in R1. IOC$SEARCHDEV is in module IOSUBPAGD, on
fiche panel 443 B05 (for VMS V4.5).
movl ioc$gl_devlist,r10 ;; ddb list in r10
Use G^ addressing, eg:
MOVL G^IOC$GL_DEVLIST,R10 ;;DDB list in R10
movl #port_numb, r6 ;; Port buffer address
movl #srv_name_str, r7 ;; Server name buffer address
Use the MOVAB instruction, eg:
MOVAB PORT_NUMB,R6 ;;Port buffer address
MOVAB SRV_NAME_STR,R7 ;;Server name buffer address
;;
;; Establish a pointer to the beginning of the LAT Plus Data
;;
addl3 #ucb$b_srv_info, r9, r8
You could save a few cycles and use:
MOVAB UCB$B_SRV_INFO(R9),R8 ;;Address the LAT data area
Your system is crashing because the instruction below is adding more than you
think to R8. Recall that you only stored 16 bits worth of information in
PORT_LEN, and you're using 32 bits with the ADDL instrction. The high order
16 bits are comming from the low order 16 bits of PORT_NUMB, which is not
making the processor very happy when it goes to check to make sure you can
see the entire source string described by the first two operands for the MOVC3
instruction ...
addl2 port_len, r8 ;; Offset to srv name length
;; movc3 srv_name_len, srv_info_name(r8), (r7)
Also, you're going to run into trouble with disconnectable LAT terminals...
A while back, I wrote a subroutine which will retrieve information about a
LAT port. Here's another copy:
--------------------------------------------------------------------------------
.Title LAT_Info - Get information about a LAT terminal
.Ident /V01.001/
.Enable SUP
.Default Displacement,Word
.Subtitle Introduction
;+
;
; ----- LAT_Info: Get information about a LAT terminal
;
;
; Facility:
;
; VAX/VMS system programming
;
; Abstract:
;
; This module provides a routine which can be called from any
; VAX native language to obtain information about a specific
; LAT terminal.
;
; Environment:
;
; VAX/VMS native mode, VMS V4.2 or later, LATPlus V01.012 or later,
; CMKRNL privilege.
;
;
;
; Version: V01.001
; Date: 03-Feb-1987
;
; Gerard K. Newman 19-Dec-1986
; San Diego Supercomputer Center
; GA Technologies
; P.O. Box 85608
; San Diego, CA 92138
; 619.534.5076
;
; Internet: GKN@SDS.SDSC.EDU
; Bitnet: GKN@SDSC.BITNET
; Span: SDSC::GKN (27.1)
;
;
; Modifications:
;
; 3-Feb-1987 GKN Don't allow LTA0: to be used.
;
;-
.Page
.Subtitle Local definitions
.Library "SYS$LIBRARY:LIB" ;Get special macros from here
.Link "SYS$SYSTEM:SYS.STB"/Selective_Search ;Ease the link process a bit
.NoCross ;Save a tree
$DCDEF ;Device class & type definitions
$DDBDEF ;Device data block offsets
$SSDEF ;System service codes
$TTYUCBDEF ;Terminal UCB offsets
$UCBDEF ;UCB offsets
.Cross ;Turn CREF back on
; Local definitions
; Constants that are likely to change in a future release of VMS that are not
; defined in SYS.STB or in a macro anywhere. These values come from looking
; at the running system with SDA.
; Offset in an LT UCB to the length of the port name, which is a counted
; string. Immediately following the port name is another counted string
; which is the LAT node name.
UCB$B_LT_PORT = ^X134 ;Offset to the port name length
UCB$B_LT_SESS = ^X195 ;Offset to the session number
.Page
.Subtitle LAT_INFO - Get information about a LAT terminal
;+
;
; ----- LAT_INFO: Get information about a LAT terminal
;
;
; This routine can be called by any VAX native language to obtain the server
; name, port name and session number of a LAT connection given a LT device
; name. The calling program must have CMKRNL privilege and must be linked
; with SYS.STB.
;
; Caveats:
;
; Will only work with fixed (non-dynamic) string descriptors.
;
; Call sequence:
;
; status.wlv = LAT_INFO (terminal.rt.dx, session.wlr, server.wt.dx, port.wt.dx)
;
; Inputs:
;
; 4(AP) - Address of a descriptor of the LT device name.
; 8(AP) - Address of a longword to return the session number in.
; 12(AP) - Address of a descriptor to return the server name.
; 16(AP) - Address of a descriptor to return the server port name.
;
; Outputs:
;
; R0 - SS$_NOPRIV: No CMKRNL privilege.
; - SS$_ACCVIO: One of the arguments is not accessible.
; - SS$_NOSUCHDEV: The specified LT device can't be found.
; - SS$_IVDEVNAM: The specified device isn't a LAT terminal.
; - SS$_NORMAL: Success.
;
;-
.Psect LAT_INFO EXE,RD,NOWRT,PIC,SHR,PAGE
.Entry LAT_INFO,^M<> ;Entry here
$CMKRNL_S ROUTIN=B^20$,- ;Do this
ARGLST=(AP) ; in kernel mode
10$: RET ;Done, status in R0
; Here in kernel mode to do all of the actual work.
20$: .Word ^M<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11> ;Here in kernel mode to get some info
; First, check to see if we can read the argument list.
MOVL #SS$_ACCVIO,R0 ;Presume we can't
IFNORD #<5*4>,(AP),10$ ;Probe the argument list
; Check the number of arguments
MOVL #SS$_INSFARG,R0 ;Presume we have too few arguments
CMPB #4,(AP) ;Do we have enough arguments?
BNEQ 10$ ;If NEQ no, it's wrong somehow
; Check to see if we can write the session number.
MOVL #SS$_ACCVIO,R0 ;Presume we can't
IFNOWRT #4,@8(AP),10$ ;Probe the output session number
; See if we can read the device name descriptor.
MOVL 4(AP),R1 ;Address the LT device name descriptor
JSB G^EXE$PROBER_DSC ;Probe the descriptor
BLBC R0,10$ ;Sigh.
MOVQ R1,-(SP) ;Save a safe copy of the probed descriptor
MOVL SP,R11 ;Remember where it is
CLRW 2(R11) ;Never mind the type and class info
; See if we have write access to the two output descriptors.
MOVL 12(AP),R1 ;Address the server name output descriptor
JSB G^EXE$PROBEW_DSC ;Probe the descriptor
BLBC R0,10$ ;Sigh.
MOVQ R1,-(SP) ;Stash a safe copy of the descriptor
MOVL SP,R10 ;Remember where I put it
CLRW 2(R10) ;Never mind the type and class info
MOVL 16(AP),R1 ;Address the port name output descriptor
JSB G^EXE$PROBEW_DSC ;Probe the descriptor
BLBC R0,10$ ;Sigh.
MOVQ R1,-(SP) ;Stash a safe copy of said descriptor
MOVL SP,R9 ;Remember where it is
CLRW 2(R9) ;Never mind the type and class info
; Ok. Now go hunt down the device the user told us was a LAT terminal
; and see if it really is.
MOVL G^SCH$GL_CURPCB,R4 ;Get my PCB address
JSB G^SCH$IOLOCKR ;Lock the I/O database mutex
MOVL R11,R1 ;Address the device name descriptor
JSB G^IOC$SEARCHDEV ;Go search for the device.
BLBC R0,30$ ;We lose.
; Now check to see if it's even a terminal, and if it is see if it's a LAT
; terminal. Remember to chain to the physical UCB first in case this is
; a disconnectable terminal.
MOVL #SS$_IVDEVNAM,R0 ;Presume it isn't a terminal
CMPB #DC$_TERM,UCB$B_DEVCLASS(R1) ;Is it a terminal?
BNEQ 30$ ;If NEQ no.
MOVL UCB$L_TL_PHYUCB(R1),R1 ;Get to the "real" UCB
BGEQ 30$ ;If GEQ this is probably LTA0:
MOVL UCB$L_DDB(R1),R2 ;Find the DDB
CMPW #^A/LT/,DDB$T_NAME+1(R2) ;Is this a LAT terminal?
BNEQ 30$ ;If NEQ no
; It's a LAT terminal all right. Obtain the server name, port name and
; session number.
MOVZBL UCB$B_LT_SESS(R1),@8(AP) ;Stash the session number
MOVAB UCB$B_LT_PORT(R1),R1 ;Address the port name
MOVL R9,R6 ;Here's the output descriptor address
BSBB MOVE_ASCIC ;Move the port name
MOVW (R6),@16(AP) ;Replace the length
MOVL R10,R6 ;R1 now points at the server name
BSBB MOVE_ASCIC ;So go move it, too
MOVW (R6),@12(AP) ;Replace the length
MOVL #SS$_NORMAL,R0 ;Success!
MOVL G^SCH$GL_CURPCB,R4 ;Get my PCB address again
30$: PUSHL R0 ;Save the return status
JSB G^SCH$IOUNLOCK ;Unlock the I/O database mutex
SETIPL #0 ;Drop back down from IPL$_ASTDEL
POPL R0 ;Restore the return status
RET ;Back to user mode
; Short subroutine to move a .Ascic string to a place described by a
; descriptor. Forms a proper descriptor of the result, and handles
; short buffers, etc.
;
; Inputs:
;
; R1 - Address of the .Ascic string
; R6 - Address of the output descriptor
;
; Outputs:
;
; R1 - Address of one byte past the end of the .Ascic string
; R0-R5 - Smashed
MOVE_ASCIC: ;Here to move a .Ascic string
MOVZBL (R1)+,R0 ;Grab the string length
CMPW (R6),R0 ;Check the string length
BGEQU 10$ ;If GEQU then we can use it
MOVZWL (R6),R0 ;Else only copy what will fit
10$: MOVW R0,(R6) ;Stash the length
MOVC3 R0,(R1),@4(R6) ;Copy the string
RSB ;Done
.End
--------------------------------------------------------------------------------
gkn
--------------------------------------
Arpa: GKN@SDSC.ARPA
Bitnet: GKN@SDSC
Span: SDSC::GKN (27.1)
USPS: Gerard K. Newman
San Diego Supercomputer Center
P.O. Box 85608
San Diego, CA 92138
AT&T: 619.534.5076ted@blia.BLI.COM (Ted Marshall) (07/10/87)
This on is easy! :-) (But don't feel too bad. The reason it's easy is that I've been bitten on this one too!) Your problem is that the movc instructions destroy registers R0 through R5. Reading from my trusty VAX Architecture Handbook, 1981, after a movc3, these registers are left with the following values: R0 = 0 R1 = address of 1 byte beyond the source string R2 = 0 R3 = address of 1 byte beyond the destination string R4 = 0 R5 = 0 One of the reasons this is done is that the instruction is interruptible. If an interrupt does occur during the movc3, the processor saves the state of the instruction in these registers and sets the FPD (First Part Done) bit in the PSL. When the instruction stream resumes, the PC still points at the movc3. The FPD bit tells the processor to restore the state from the registers and continue from there. Anyway, in your program, the routine that does the movc3 does not save R2 through R5. Note also that since this is called with the $CMKRNL system service, the direct caller is not your program but the EXE$CMKRNL routine in the executive. I haven't looked at it in the fiche, but I expect that when your routine returns to it, it tries to use a value it saved in one of these registers and gets an access violation. Thus, I expect that the fix is to add R2, R3, R4 and R5 to the entry mask for that routine. [Note: I don't have a LAT server and have not verified that this is the complete fix, but it is at least part of it.] -- Ted Marshall ...!ucbvax!mtxinu!blia!ted <or> mtxinu!blia!ted@Berkeley.EDU Britton Lee, Inc., 14600 Winchester Blvd, Los Gatos, Ca 95030 (408)378-7000 The opinions expressed above are those of the poster and not his employer.
LEICHTER-JERRY@YALE.ARPA (07/11/87)
Greetings,
I am trying to modify FINGER to go out and get the Server and Port
name from the UCB blocks for the LAT device, we are using LAT-Plus. I
have below the code that I use, the problem I am having is that the MOVC3
statement in the Kernal Mode code, crashes the system every time I run
it....
.entry latinfkrn,^M<r6,r7,r8,r9,r10>
...
movc3 port_len, srv_info_port(r8), (r6)
movc3 modifies registers r0-r5. r0-r1 are not a problem unless your code
uses them (I didn't look at it that closely), but you MUST save r2-r5 in
your entry mask (or save/restore them yourself, if that's preferable for
some reason).
BTW, it's not clear to me that locking the pages involved in the working set
is sufficient for your purposes; you have to be sure they are in memory before
they are accessed. I don't know for certain, but I suspect $LKWSET will
simply start the paging going - I don't think it will necessarily wait for
it to complete.
The usual technique, if all the code fits in no more than two pages, is to
place the IPL you are setting in memory at the end of the code. The instruc-
tion that sets the IPL is then itself guaranteed to be in memory, and it
is certain to fault in the page containing the IPL. While at elevated IPL,
neither page will fault out. (This can, of course, fail horribly if the
SETIPL and the IPL itself get more than 2 pages apart, as there can then be
a "hole" between the two.) If you do this, the $LKWSET is redundant.
-- Jerry
-------jeh@crash.CTS.COM (Jamie Hanrahan) (07/11/87)
In article <8707110215.AA28319@ucbvax.Berkeley.EDU>, <LEICHTER-JERRY@YALE.ARPA> writes: >BTW, it's not clear to me that locking the pages involved in the working set >is sufficient for your purposes; you have to be sure they are in memory before >they are accessed. I don't know for certain, but I suspect $LKWSET will >simply start the paging going - I don't think it will necessarily wait for >it to complete. > >The usual technique, if all the code fits in no more than two pages, is to >place the IPL you are setting in memory at the end of the code. The instruc- >tion that sets the IPL is then itself guaranteed to be in memory, and it >is certain to fault in the page containing the IPL. While at elevated IPL, >neither page will fault out. (This can, of course, fail horribly if the >SETIPL and the IPL itself get more than 2 pages apart, as there can then be >a "hole" between the two.) If you do this, the $LKWSET is redundant. $LKWSET will in fact wait to complete until all of the requested pages are faulted in. The technique of referencing the IPL value, which is stored at the end of the code to be locked, at the beginning (via DSBINT or SETIPL), is a hack. It's true that it's a _common_ hack inside VMS, but I still feel it should be avoided. After all, someone who isn't familiar with this particular idiom might have to understand the code someday. The use of the $LKWSET service makes the intent absolutely clear.
jeh@crash.CTS.COM (Jamie Hanrahan) (07/11/87)
In article <870708122836.001@sitvxb> dstevens@sitvxb (David L Stevens) writes: > > ...the MOVC3 > statement in the Kernal Mode code, crashes the system every time I run it. I replied to this via mail, but since then several not-quite-correct responses have been posted as news, so here goes... The folks who point out that MOVC3 clobbers R0-R5 are correct. (And, by the bye, LOC3 hits R0-R3.) BUT, simply mentioning R2 through R5 in the kernel (not "kernal", please!) mode routine's entry point mask is not sufficient to avoid the crashes. The code shown is calling the VMS system routines EXE$IOLOCKR (lock I/O data base via mutex for read) and EXE$IOUNLOCK (unlock I/O data base mutex). These routines require R4 to point to the current process's PCB. The call to IOLOCKR works because the $CMKRNL service calls the target routine with R4 pointing to the PCB, but after the MOVC3, R4 contains 0. The mutex-handling routines check to ensure that R4 is pointing to a valid PCB and bugcheck if it doesn't; hence the crash. R4 can be pushed at the beginning of the routine and popped just before the call to UNLOCK, or pushed and popped around the MOVC3s. Personally, I would put the following statement just before the calls to both EXE$IOUNLOCK and EXE$IOLOCKR: MOVL G^SCH$GL_CURPCB, R4 ; get addr of cur proc PCB Sure, it's not necessary for IOLOCKR because of the context that this code happens to run in... but that might change someday. The MOVL makes the code less context-dependent, and also more understandable. One other thing: All references to system-space labels (EXE$IOLOCKR, EXE$UNLOCK) should be preceded with the G^ prefix to ensure that they're position independent. DISCLAIMER: Names of system-space labels in the above were typed from memory. The suffixes are correct but the prefixes (EXE$, SCH$, etc.) may be mixed up... it's late/early/not good.