&9$LD071.B7`LD071.BBACKUP/NOASSIST/NODISMOUNT/COMMENT=OpenVMS AXP SPKITBLD Procedure/INTER/LOG/VERIFY [.SRC]*.* USER1:[VDBURG.LD.V71]LD071.B/LABEL=(LD)/SAVE/BLOCK=9000/GROUP=25/NOINIT/NOREWIOpenVMS AXP SPKITBLD Procedure VDBURG @@}B-V7.3 _UTRA41:: _$8$DUA15: AXP731R006 $%*[VDBURG.LD.V71.SRC]FORGE_GEOMETRY.C;1+,/.$/@@ 4$4-0123KPWO%5 6OT">+-7OB-89G@@HJ##pragma module FORGE_GEOMETRY "X-2"J//**********************************************************************//J// //J// Copyright 1994-2004 Hewlett-Packard Development Company, L.P. //J// //J// Confidential computer software. Valid license from HP and/or its //J// subsidiaries required for possession, use, or copying. //J// //J// Consistent with FAR 12.211 and 12.212, Commercial Computer Software, //J// Computer Software Documentation, and Technical Data for Commercial //J// Items are licensed to the U.S. Government under vendor's standard //J// commercial license. //J// //J// Neither HP nor any of its subsidiaries shall be liable for technical //J// or editorial errors or omissions contained herein. The information //J// in this document is provided "as is" without warranty of any kind //J// and is subject to change without notice. The warranties for HP //J// products are set forth in the express limited warranty statements //J// accompanying such products. Nothing herein should be construed as //J// constituting an additional warranty. //J// //J//**********************************************************************//J//**********************************************************************// // //// Abstract: //E// This module forges a disk geometry (tracks, sectors, and //J// cylinders) compliant with the expectations of the VERIFY //J// (ANALYZE/DISK_STRUCTURE) utility, based on a specified //J// disk size. // // //// Revision History: // // //(// X-2 Stephen Hoffman 21-Apr-2004 //<// Fix argument return bug (and thanks to Oliver Hellwig //J// for spotting it) during handling of tracks argument. // // //'// X-1 Stephen Hoffman 5-Feb-2004 //J// Code arrived here from the VERIFY algorthm by way //J// of Forrest Kenney and Ken Blaylock, and a conversion //J// into C that happened somewhere along the way. // // //J//**********************************************************************//#define __NEW_STARLET 1*#include // Integer definitions8#include // Define system service conditions.#include // Standard Definitions )#include // Standard Library,#include // String definitions 7#include // Status values, testing macros 7#include // Status values, testing macros /*//++// FUNCTIONAL DESCRIPTION://3// FORGEO_ISQRT performs a quadword square root//// FORMAL PARAMETERS://E// x -- the quadword value that is the target of the square root//// RETURN VALUE:// // the quadword square root//// SIDE EFFECTS://6// physically warms parts of the processor's core// // DESIGN://// generic square root////--*/static long longforgeo_isqrt( long long x ) { long long result; if ( x <= 1L ) return 1L; result = x / 2;9 // use the built-in to avoid requiring the include file //. while ( __LABS( x / result - result ) > 1L )- result = ( result + ( x / result )) / 2L; return result; }/*//++// FUNCTIONAL DESCRIPTION://L// FORGE_GEOMETRY is used to synthesize the archaic geometry constructL// for the target logical unit (disk drive), based on the total number6// of blocks (ucb->ucb$l_maxblock, or MaxBlock).//// FORMAL PARAMETERS://M// ucb -- UCB address; maxblocks, cyls, trks, secs updated; can be nullL// MaxBlock -- the maximum block; overrides ucb$l_maxblock if specifiedH// Cylinders -- pointer to word field to receive cylinders, or nullB// Tracks -- pointer to byte field to receive tracks, or nullD// Sectors -- pointer to byte field to receive sectors, or null//// RETURN VALUE:// // void//// SIDE EFFECTS://L// initializes the geometry fields within the specified ucb, if the ucbF// argument is provided. Can be called without the ucb argument.// // DESIGN://G// This routine exists solely to keep ANALYZE/DISK_STRUCTURE (VFY)E// happy, and serves no other particular use. As VFY thankfullyG// doesn't know from notched devices (differing numbers of sectorsC// across the media surface, typically with larger numbers of C// sectors nearest the outer edge and fewer numbers toward theD// center of traditional rotating media) and other such arcana,*// this code is comparatively simple.////--*/ extern voiddexe$forge_geometry( DT_UCB *ucb, uint32 MaxBlock, uint16 *Cylinders, uint8 *Tracks, uint8 *Sectors ) { long long sqrt; uint32 lsec; uint32 ltrk; uint32 lcyl; uint32 lmblk;> // If the disk UCB is available and we have not been passed= // an explicit MaxBlock, use the ucb$l_maxblock value. If< // UCB was not passed or if MaxBlock was passed, the code, // will prefer to use the MaxBlock value. //> lmblk = (ucb && !MaxBlock) ? ucb->ucb$l_maxblock : MaxBlock; sqrt = forgeo_isqrt( lmblk ); lsec = forgeo_isqrt( sqrt ); ltrk = sqrt / lsec;0 sqrt = ((long long) lsec * (long long) ltrk );# lcyl = (lmblk + sqrt - 1) / sqrt;? // If the geometry arguments were passed, return the values. // if ( Cylinders ) *Cylinders = (uint16) lcyl; if ( Tracks ) *Tracks = (uint8) ltrk; if ( Sectors ) *Sectors = (uint8) lsec;> // If the UCB argument was specified, initialize the fields? // based on the calculations. (Finding the path back to the? // base UCB is, um, mildly interesting when you're expecting // a Disk Tape UCB (DT_UCB).) // if ( ucb ) {- ucb->ucb$l_maxblock = (uint32) lmblk;O ucb->ucb$r_dpucb.ucb$r_erlucb.ucb$r_ucb.ucb$w_cylinders = (uint16) lcyl;N ucb->ucb$r_dpucb.ucb$r_erlucb.ucb$r_ucb.ucb$b_tracks = (uint8) ltrk;N ucb->ucb$r_dpucb.ucb$r_erlucb.ucb$r_ucb.ucb$b_sectors = (uint8) lsec; } return; }*[VDBURG.LD.V71.SRC]LD.C;1+,./@@ 4q-0123KPWO5 6Èؾ7<,SB-89G@@HJ/* Facility: VAX/VMS Logical Disk Utility Abstract:; This utility enables the user to create, setup and display; information of the Logical Disks available in the system, via the LDDRIVER. Author:* Jur van der Burg 13-OCT-1992 Version 2.0 jur.vanderburg@compaq.com? Rewritten from original MACRO version (by A. Sweep) for easier maintenance. Revision:* Jur van der Burg 14-Jan-2004 Version 7.0 Reworked edit history Add /CLONE Add PROTECT /PERMANENP}sY$LD071.B[VDBURG.LD.V71.SRC]LD.C;1q"T% Add saving of geometry in fileheader4 Disable caching of containerfile on create and open4 Flush eventual XFC cache contents of container file Add LD HELP) Jur van der Burg 5-JUL-2000 Version 6.3 Add optional FDT logging1 Add accurate timing logging (only used on Alpha) Modify status display Modify disconnect logging infoF Drop requirement for inputfilespec on disconnect. This allows deleted2 containerfiles to be disconnected without /ABORT.* Jur van der Burg 19-AUG-1998 Version 6.2B Added a way to use more controller letters to bypass the limit ofF 9999 cloned devices. Do this by specifying another device on connect:? LD CONNECT FILE.DSK LDC44. The driver can already handle this.* Jur van der Burg 15-NOV-1996 Version 6.01 Lifted restriction for contiguous container file7 Corrected problem setting virtual watchpoint on a fileG Make sure filename is terminated in open_file to prevent errors9 during LD DISCONNECT/ALL/LOG with lots of devices? Add /FUNCTION=ALL to watchpoint to allow trapping of all I/O's* Jur van der Burg 28-OCT-1994 Version 5.1" Add /SYMBOL qualifier for CONNECTD Add /SHARE qualifier to allow sharing of containerfiles clusterwide= Add /TRACKS, /SECTORS, /CYLINDERS and /MAXBLOCKS switches to@ CONNECT to specify geometry, removed /ALLOCATED (superceeded byA /MAXBLOCKS). Also added /ALLOCLASS to set allocation class which6 may be different from the system parameter ALLOCLASS.* Jur van der Burg 14-OCT-1993 Version 5.0 Add WATCH commands( Adapted for new interface with LDdriver) Jur van der Burg 8-JUL-1993 Version 4.1= Corrected bytecount display and iosb display to use longword instead of word field for data.> Bumped datafile versionnumber because of change in trace data layout.* Jur van der Burg 14-APR-1993 Version 4.0, Modified command interface to use DCLTABLES Added cloned device support% Reworked error handling and display. Add remote trace stop* Jur van der Burg 23-FEB-1993 Version 3.1? Expand given devicespec in 'replace' mode to full spec instead of spec from the commandline. Add more trace functionality* Jur van der Burg 17-FEB-1993 Version 3.0 Added trace commands Minor bugfixes Commands:H - LD CREATE [/LOG] [/SIZE=xxx] [/BACKUP] [/CONTIGUOUS] [/LBN=xxx]< [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]8 [/MAXBLOCKS=xxx] [/CLONE=device] FilespecH - LD CONNECT [/LOG] [/SYMBOL] [/REPLACE] [/SHARE] [/CLONE=device]< [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]@ [/MAXBLOCKS=xxx] [/ALLOCLASS=xxx] [/AUTOGEOMETRY]' [/SAVE] Filespec [LDan:]3 - LD DISCONNECT [/ALL] [/LOG] [/ABORT] LDan:? - LD TRACE [/ACCURATE] [/FDT] [/SIZE=xxx] [/RESET] LDan:% - LD TRACE/STOP [/ALL] [LDan:] - LD NOTRACE LDan:I - LD WATCH LDan: lbn [,lbn...] [/FUNCTION=READ,WRITE,ALL,CODE=xxx]8 [/ACTION=SUSPEND,CRASH,OPCOM,ERROR[=xxx]] [/FILE=filespec]4 - LD NOWATCH LDan: [lbn [,lbn...]] [/INDEX=n]9 - LD WATCH/RESUME LDan: [lbn [,lbn...]] [/INDEX=n]& - LD PROTECT [/PERMANENT] LDan:( - LD NOPROTECT [/PERMANENT] LDan: - LD SHOW [/ALL] [LDan:], - LD SHOW/WATCH LDan: [lbn [,lbn...]]N - LD SHOW/TRACE [/STATUS] [/RESET] [/OUTPUT=Filespec] [/INPUT=filespec]G [/BINARY] [/ENTRIES=[(XXX,YYY)]] [/HEADER] [/CONTINUOUS]= [/VERSION_LIMIT=xxx] [/BLOCKS=xxx] [/WARNINGS]3 [/NUMBER] [/PID] [/LBN] [/BYTECOUNT]5 [/IOSB[=COMBINATION,TEXT,HEX,LONGHEX]]@ [/TIMESTAMP[=ABSOLUTE,ELAPSED,COMBINATION,DELTA]]> [/FUNCTION[=TEXT,HEX]] [/ACCURATE] [/FDT] LDan: - LD HELP [topic]*/#include "ld.h"main(){ int stat; int length; char cmdbuf[1024]; struct dsc commandline = {sizeof(cmdbuf), cmdbuf}; short rlen; char *p; struct cmndtbl *q; ld_fab = cc$rms_fab; ld_rab = cc$rms_rab; ld_nam = cc$rms_nam; ld_xabfhc = cc$rms_xabfhc; ld_xaball = cc$rms_xaball; outfile = 0;%/* Get the rest of the commandline */6 stat = lib$get_foreign(&commandline, 0, &rlen, 0); signal_error(stat, 0); commandline.len = rlen; p = getqualstring(&command);, length = strlen(p) >= 4 ? 4 : strlen(p); q = cmnds; while (*(q->what)) {( if (strncmp(p, q->what, length) == 0) {) (*(q->where)) (); /* Call command */ exit(1); } q++; }! signal_error(CLI$_NOCOMD, 0);}int getqual(arg)struct dsc$descriptor_s *arg;{ int stat;9 stat = cli$present(arg); /* Get qualifier presence */ if (stat == CLI$_PRESENT || stat == CLI$_LOCPRES || stat == CLI$_DEFAULTED) return (1); /* It's there */9 else if (stat == CLI$_NEGATED || stat == CLI$_LOCNEG) return (-1); /* Negated */! else if (stat == CLI$_ABSENT) return (0); /* Not there */ signal_error(stat, 0);}int getqualvalue(arg)struct dsc$descriptor_s *arg;{ char value[256]; struct dsc out = {sizeof(value), &value}; int val; short rlen; *value = 0;5 if ((cli$present(arg) & 1) && /* If present... */> (cli$get_value(arg, &out, &rlen) & 1)) { /* Get it's value */ *(value + (rlen & 0377)) = 0;0 return ((sscanf(value, "%d", &val)) ? val : 0); } else return (0); /* Default */}int getmulqualvalue(arg, more)struct dsc$descriptor_s *arg; int *more;{ int stat; char value[256]; struct dsc out = {sizeof(value), &value}; int val; short rlen; *value = 0; if (*more < 0)5 if (!(cli$present(arg) & 1)) /* If not present... */ return (0);@ stat = cli$get_value(arg, &out, &rlen); /* Get it's value */ *more = 0; if (!(stat & 1)) return (0); if (stat == CLI$_COMMA) *more = 1;! *(value + (rlen & 0377)) = 0;3 return ((sscanf(value, "%d", &val)) ? val : 0);}char *getqualstring(arg)struct dsc$descriptor_s *arg;{ char *string, *p; struct dsc out; short rlen;3 p = string = malloc(256); /* Get some memory */+ out.addr = string; /* Setup pointer */" out.len = 256; /* and size */5 if ((cli$present(arg) & 1) && /* If present... */> (cli$get_value(arg, &out, &rlen) & 1)) { /* Get it's value */) p += rlen; /* Point to end of string */# *p = '\0'; /* Place terminator */ return (string); } else return (0); /* Default */}/*: Show connection between LD device and corresponding file*/ void show(){ int stat; int loop = 0; char matchname[256]; struct dsc matchdev =# {sizeof(matchname), matchname}; short rlen; int context[2] = {0, 0}; if (getqual(&sw_trace) > 0)2 show_trace(); /* Special processing for trace */$ else if (getqual(&sw_watch) > 0)2 show_watch(); /* Special processing for watch */ else {; s_device = getqualstring(&sw_device); /* Get devicename */) if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;3 while (stat & 1) { /* Loop until we're done */C stat = sys$device_scan(&matchdev, &rlen, &wildname, 0, &context); if (stat == SS$_NOMOREDEV) { if (loop) { if (loop == 1)2 lib$signal(&ld_nounitsfound, 1, matchname); else" return; /* No more, quit */ } else9 signal_error(SS$_NOSUCHDEV, 0); /* No device at all */ } loop++; signal_error(stat, 0);0 matchname[rlen] = '\0'; /* Place terminator */7 stat = show_one(matchname, 0); /* Show this device */7 if (stat == SS$_DEVINACT) { /* If not connected... */? lib$signal(&ld_notconnected, 1, matchname); /* Flag it */" stat = 1; /* and continue */ } }* } else { /* Only one specified device */= stat = show_one(fulldevspec(s_device), 1); /* Show it */: if (stat == SS$_DEVINACT) { /* If not connected... */G lib$signal(&ld_notconnected, 1, fulldevspec(s_device)); /* Flag it */: stat = &ld_notconnected; /* Convert to better message */4 stat |= STS$M_INHIB_MSG; /* Don't show it again */ } } }}/* Show one specific device*/int show_one(which, single) char *which; int single;{ int stat; short chan; struct iosb iosb; char ldfile[256]; char lddev[64]; struct fiddef fid; struct dsc sfilename = {strlen(which), which};9 if (get_unit(which, 1) == 0) { /* Don't use unit 0 */ if (single)B lib$signal(`$LD071.B[VDBURG.LD.V71.SRC]LD.C;1q'"&ld_badunit, 1, which); /* Show it to the world */ else return (1); }Q stat = sys$assign(&sfilename, &chan, 0, 0, 0); /* Assign chan to LD device */@ if (stat == SS$_DEVALLOC) { /* Allocated on remote node ? */' lib$signal(&ld_remotealloc, 1, which); } else { signal_error(stat, 0);I stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Inquire stat */A lddev, sizeof(lddev), &fid, 0, 0, LDIO_GET_CONNECTION);! signal_error(stat, iosb.status);0 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);D if (!iosb_flags.connected) /* Check 'connected' flag from driver */ return (&ld_notconnected);5 lddev[iosb.size] = '\0'; /* Terminate device spec */ if (iosb_flags.replaced) { strcpy(ldfile,lddev);# strcat(ldfile, " (Replaced)"); } else( create_filename(lddev,&fid,ldfile); if (iosb_flags.protect)( strcat(ldfile, " (Write protect)"); if (iosb_flags.share)! strcat(ldfile, " (Shared)");H lib$signal(&ld_connected, 2, which, ldfile); /* Show it to the world */ } return (1);}/*% Connect LD device and file together*/void connect_unit(){ struct dsc sdev; int stat; short chan; int cloneavail; int replacefl; int shflag; int unit; int q_log; int func; int geospecified = 0; int tracks = 0; int sectors = 0; int cylinders = 0;  int alloclass; int tmp; struct iosb iosb;# $DESCRIPTOR(symbol, "LD_UNIT"); char tmpstr[10]; char *tmpptr; struct metadata mdat;8 s_file = getqualstring(&sw_file); /* Get filename */B s_device = getqualstring(&sw_device); /* Get LD device name */, q_log = getqual(&sw_log); /* Get /LOG */K if ((alloclass = getqualvalue(&sw_alloclass)) > 0) { /* /ALLOCLASS ? */ if (alloclass > 255)= lib$signal(&ld_badalloclass); /* Bad allocation class */ }% replacefl = getqual(&sw_replace); maxblocks = 0; if (!replacefl) { #ifndef __vaxJ flush_xfc(s_file); /* Flush eventual XFC contents for this file */#endif- open_file(s_file); /* Attempt normal open */M cloneavail = getclonedata(&tracks, §ors, &cylinders, &maxblocks);. geospecified = getfilemetadata(&mdat); if (!cloneavail) {; if ((getqual(&sw_autogeometry)) > 0 && geospecified) { tracks = mdat.tracks; sectors = mdat.sectors; cylinders = mdat.cylinders; maxblocks = mdat.maxblock; }. if ((tmp = getqualvalue(&sw_tracks)) > 0) tracks = tmp;/ if ((tmp = getqualvalue(&sw_sectors)) > 0) sectors = tmp;1 if ((tmp = getqualvalue(&sw_cylinders)) > 0) cylinders = tmp;1 if ((tmp = getqualvalue(&sw_maxblocks)) > 0): maxblocks = tmp; /* Set new value if specified */4 if ((maxblocks == 0) && !geospecified) {M/* No maxblocks specified, check if geometry specified. If true set maxblocks according to that */A if ((tracks != 0) || (sectors != 0) || (cylinders != 0))0 maxblocks = ((tracks == 0) ? 1 : tracks) *+ ((sectors == 0) ? 1 : sectors) *. ((cylinders == 0) ? 1 : cylinders); } } } else {B realdev_dsc.addr = fulldevspec(s_file); /* Set real devicename */, realdev_dsc.len = strlen(realdev_dsc.addr); } shflag = 0; sdev.addr = "LDA0:"; sdev.len = 5;K if (getqual(&sw_share) > 0 && !s_device) { /* /SHARE and no device ? */4 if ((unit = findshareddevice(s_file)) > 0) {# s_device = malloc(256);. sprintf(s_device, "LDA%d:", unit); } }/ if (!s_device) { /* No device specified */ s_device = malloc(256);? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0); if (alloclass > 0) {L stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,/* Init LD dev. */1 alloclass, 0, 0, 0, 0, LDIO_SET_ALLOCLASS);% signal_error(stat, iosb.status); } getdevnam(chan, s_device); shflag++; } else { unit = get_unit(s_device, 0);5 if ((unit == 0) || (unit > 9999)) /* Check range */* lib$signal(&ld_badunit, 1, s_device); set_seed(unit,s_device); strcpy(tmpstr,"LDA0:"); tmpptr = s_device+2; tmpstr[2] = *tmpptr; sdev.addr = tmpstr;? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0); if (alloclass > 0) {L stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,/* Init LD dev. */1 alloclass, 0, 0, 0, 0, LDIO_SET_ALLOCLASS);% signal_error(stat, iosb.status); } getdevnam(chan, s_device);# if (get_unit(s_device, 1) != unit)2 lib$signal(&ld_dupunit); /* Duplicate unit */ }/ if (getqual(&sw_share) > 0) /* /SHARE ? */$ func = LDIO_CONNECT | LDIO_M_SHARE; else- func = LDIO_CONNECT; /* Normal function */6 if (!replacefl) /* Only if not replacing drive */I stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Init LD dev. */9 &sbk, maxblocks, tracks, sectors, cylinders, func); elseI stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Init LD dev. */7 &realdev_dsc, 0, 0, 0, 0, func | LDIO_M_REPLACE);' if (iosb.status == SS$_FILALRACC) {T /* There's more information for this error in the second longword of the iosb */ switch(iosb_lw) { case LDREASON_NOTSHARED: iosb_lw = &ld_notshared; break; case LDREASON_NOSHARE: iosb_lw = &ld_noshare; break; case LDREASON_ALLOCLASS: iosb_lw = &ld_alloclass; break; case LDREASON_UNITNUMBER: iosb_lw = &ld_unitnumber; break; case LDREASON_TRACKS: iosb_lw = &ld_tracks; break; case LDREASON_SECTORS: iosb_lw = &ld_sectors; break; case LDREASON_CYLINDERS: iosb_lw = &ld_cylinders; break; case LDREASON_MAXBLOCK: iosb_lw = &ld_maxblock; break; default: iosb_lw = 0; break; } }- signal_error(stat, iosb.status, iosb_lw);8 if (!replacefl) { /* Only if not replacing drive */3 if (getqual(&sw_save) > 0) { /* Update metadata */ mdat.tracks = tracks; mdat.sectors = sectors; mdat.cylinders = cylinders; mdat.maxblock = maxblocks;# setfilemetadata(&mdat); } close_file(s_file);J if (mdat.flags.protected) { /* Check for write protect 'button' */A stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,2 0, 0, 0, 0, 0, LDIO_ENABLE_PROTECT);, signal_error(stat, iosb.status); } }3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0); if (getqual(&sw_symbol)) {B sprintf(tmpstr,"%d",get_unit(s_device, 1)); /* Get unit number */ sdev.addr = tmpstr; sdev.len = strlen(tmpstr);D stat = lib$set_symbol(&symbol, &sdev, 0); /* Create local symbol */ signal_error(stat, 0); }" if (q_log > 0) { /* /LOG ? */O stat = show_one(fulldevspec(s_device), 1); /* Show this one's now connected */ signal_error(stat, 0); } else { if (shflag)O lib$signal(&ld_unit, 1, fulldevspec(s_device)); /* Show it to the world */ }}int findshareddevice(filename)char *filename;{ char resnam[31]; char scratch[32]; int arglist[12]; int tmp1, tmp2; int i, j; int unit; int stat; int retlen; int count; struct iosb iosb; struct lkidef locks[10]; struct lkidef *p; struct itmlst item[] =0 {sizeof(locks), LKI$_LOCKS, &locks, &retlen, 0, 0, 0, 0}; struct dsc dev;2 struct dsc lknam = { sizeof(resnam), resnam };6 struct dsc scrdsc = { sizeof(scratch), &scratch };: dev.addr = ld_nam.nam$l_dev; /* Get real devicename */ dev.len = ld_nam.nam$b_dev; strcpy(resnam,"$LOGDISK_");@ stat = lib$getdvi(&DVI$_DEVLOCKNAM, 0, &dev, 0, &scrdsc, 0); signal_error(stat, 0);/* Convert hex string to ascii*// for (i = 31, j = 0; i > 3*2; i -= 2, j++) { tmp1 = scratch[i] - '0'; if (tmp1 > 9) tmp1 -= 7;" tmp2 = scratch[i-1] - '0'; if (tmp2 > 9) tmp2 -= 7;) resnam[9+j] = tmp1 + (tmp2 * 16); }- resnam[22] = resnam[23] = resnam[24] = 0;7 resnam[25] = ld_nam.nam$w_fid[0]; /* Get file-id */* resnam[26] = ld_nam.nam$w_fid[0] >> 8;% resnam[27] = ld_nam.nam$w_fid[1];* resnam[28] = ld_nam.nam$w_fid[1] >> 8;% resnam[29] = ld_nam.nam$w_fid[2];* resnam[3vQ$LD071.B[VDBURG.LD.V71.SRC]LD.C;1qO+"$0] = ld_nam.nam$w_fid[2] >> 8; arglist[0] = 11; arglist[1] = 0; arglist[2] = LCK$K_NLMODE; arglist[3] = &ld_lksb;- arglist[4] = LCK$M_SYSTEM | LCK$M_VALBLK; arglist[5] = &lknam; arglist[6] = 0; arglist[7] = 0; arglist[8] = 0; arglist[9] = 0; arglist[10] = 0; arglist[11] = 0;A stat = sys$cmkrnl(sys$enqw, arglist); /* Enqueue null lock */ if (stat == SS$_NOPRIV)< stat = SS$_NOCMKRNL; /* Convert to a meaningfull error */ signal_error(stat, 0); arglist[0] = 7; arglist[1] = 0;! arglist[2] = &ld_lksb.lockid; arglist[3] = &item; arglist[4] = &iosb; arglist[5] = 0; arglist[6] = 0; arglist[7] = 0;[ stat = sys$cmkrnl(sys$getlkiw, arglist); /* Get info of other locks on this resource */$ signal_error(stat, iosb.status);K count = (retlen & 0xffff) / ((retlen >> 16) & 0x7fff); /* Lock count */ p = locks; unit = 0;& for (i = 0; i < count; i++, p++) {B if (p->lki$l_lkid == ld_lksb.lockid) /* Disregard our own lock */ continue;/*N We found someone using the same lock. Get the unitnumber from the valueblock*/' unit = ld_lksb.u.ldvalblk.unit; break; } arglist[0] = 4; arglist[1] = ld_lksb.lockid; arglist[2] = 0; arglist[3] = 0; arglist[4] = 0;B stat = sys$cmkrnl(sys$deq, arglist); /* Get rid of our lock */ signal_error(stat, 0); return unit;}void getdevnam(chan, s_device) short chan;char *s_device;{ int stat; short rlen; struct dsc dev = {256, s_device};A stat = lib$getdvi(&DVI$_ALLDEVNAM, &chan, 0, 0, &dev, &rlen); signal_error(stat, 0);+ s_device[rlen] = '\0'; /* Terminator */}2/* Get unitnumber from unit which may not exist */int get_unit(str, exist) char *str; int exist;{ char *p, prev; int num, stat; short rlen; struct dsc dev = {strlen(str), str};0 if (exist) { /* If it's there use getdvi */4 stat = lib$getdvi(&DVI$_UNIT, 0, &dev, &num, 0, 0); signal_error(stat, 0); return (num);8 } else { /* If it's not there find it ourselves */ p = str; prev = '\0'; while (*p) { if (isdigit(*p)) { if (prev == '$') { while (isdigit(*p)) p++; } else/ return (atoi(p)); /* Return unitnumber */ } prev = *p++; }7 lib$signal(&ld_baddevsyntax, 1, str); /* Bad syntax */ }}void set_seed(num,device)int num; char *device;{ struct dsc sdev; int stat; short chan; struct iosb iosb; char *tmpstr = "LDA0:"; char *tmpptr;! if (strncmp(device,"LD",2) ||1 ((device[2] < 'A') || (device[2] > 'Z')))A lib$signal(&ld_baddevsyntax, 1, device); /* Bad syntax */ tmpptr = device+2;9 tmpstr[2] = *tmpptr; /* Insert controller letter */ sdev.addr = tmpstr; sdev.len = 5;B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);L stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Init LD dev. */V num - 1, 0, 0, 0, 0, LDIO_SET_SEED); /* minus one so next one will be correct */$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Enable trace*/ void trace(){ struct dsc sdev; int stat; short chan; int numbuf; int func; struct iosb iosb; if (getqual(&sw_stop) > 0) stop_trace(); else {? s_device = getqualstring(&sw_device); /* Get LD device name */7 if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */R lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);M if ((numbuf = getqualvalue(&sw_size)) == 0) /* Get number of tracebuffers */ numbuf = 512; /* Default */0 func = LDIO_ENABLE_TRACE; /* Normal function */+ if (getqual(&sw_reset) > 0) /* /RESET ? */ func = LDIO_RESET_TRACE;' if (getqual(&sw_fdt) > 0) /* /FDT ? */ func |= LDIO_M_FDTTRACE;1 if (getqual(&sw_accurate) > 0) /* /ACCURATE ? */ func |= LDIO_M_ACCURATE;? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,0 numbuf, 0, 0, 0, 0, func); /* Enable trace */! signal_error(stat, iosb.status);/ stat = sys$dassgn(chan);/* Deassign channel */ signal_error(stat, 0); }}/* Disable trace*/void notrace(){ struct dsc sdev; int stat; short chan; struct iosb iosb;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */N lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,= 0, 0, 0, 0, 0, LDIO_DISABLE_TRACE); /* Disable trace */$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Enable write protect*/void protect(){ protect_common(1);}/* Disable write protect*/void noprotect(){ protect_common(0);}void protect_common(how)int how;{ struct dsc sdev; int stat; int func; short chan; struct iosb iosb; struct metadata mdat; char ldfile[256]; char lddev[64]; struct fiddef fid;+ if (how) /* Select correct function */# func = LDIO_ENABLE_PROTECT; else$ func = LDIO_DISABLE_PROTECT;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */N lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, 0, 0, 0, 0, 0, func);$ signal_error(stat, iosb.status);% if (getqual(&sw_permanent) > 0) {R stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Inquire stat */K lddev, sizeof(lddev), &fid, 0, 0, LDIO_GET_CONNECTION); signal_error(stat, 0);] if (iosb_flags.connected && !iosb_flags.replaced) { /* Check if connected to file */G lddev[iosb.size] = '\0'; /* Terminate device spec *// create_filename(lddev,&fid,ldfile); open_file(ldfile);# getfilemetadata(&mdat); mdat.flags.protected = how;# setfilemetadata(&mdat); close_file(ldfile); } }3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Enable watch*/ void watch(){ struct dsc sdev; int stat; short chan; int func; int cnt, i, size; struct iosb iosb;) struct watchpt *wpt, *wpt1, wptindex; int *blkpnt, wptcnt;C int function_read, function_write, function_code, function_all,> action_suspend, action_crash, action_error, action_opcom;, int function_code_val, action_error_val;' int resume, index, index_val, file; char *filename;/* Get switches */! resume = getqual(&sw_resume); index = getqual(&sw_index);/ function_read = getqual(&sw_function_read);1 function_write = getqual(&sw_function_write);/ function_code = getqual(&sw_function_code);- function_all = getqual(&sw_function_all);1 action_suspend = getqual(&sw_action_suspend);- action_crash = getqual(&sw_action_crash);- action_opcom = getqual(&sw_action_opcom);- action_error = getqual(&sw_action_error); file = getqual(&sw_file);9 action_error_val = SS$_BUGCHECK; /* Default return */ if (action_error) {3 action_error_val = getqualvalue(&sw_action_error); if (action_error_val == 0)% action_error_val = SS$_BUGCHECK; } if (function_code)5 function_code_val = getqualvalue(&sw_function_code);B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */N lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0)$LD071.B[VDBURG.LD.V71.SRC]LD.C;1qҲ"5; /* Assign channel */ signal_error(stat, 0); if (file) {$ filename = getqualstring(&sw_file);% open_file(filename); /* Open file */ }2 if (index) { /* /INDEX? (for WATCH/RESUME) */% index_val = getqualvalue(&sw_index);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,H 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */! signal_error(stat, iosb.status);. wptcnt = iosb_lw; /* Number of watchpoints */- if ((index_val < 1) || (index_val > wptcnt))< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ if (wptcnt) {, size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size))) signal_error(SS$_INSFMEM, 0);: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,@ wpt, size, 0, 0, 0, LDIO_GET_WATCH); /* Get watch data */% signal_error(stat, iosb.status);6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action; wptindex.func = wpt->func;% wptindex.retcode = wpt->retcode;! wptindex.flags = wpt->flags; wpt = &wptindex; cnt = 1; /* One entry */ } } else {> blkpnt = getlist(&sw_lblock, &cnt); /* Get LBN's to handle */ if (cnt) {7 if (!(wpt = malloc(cnt * sizeof(struct watchpt)))) signal_error(SS$_INSFMEM, 0);4 for (i = 0, wpt1 = wpt; i < cnt; i++, wpt1++) {' wpt1->lbn = *blkpnt++; /* Fill lbn */ if (function_all) wpt1->func = 0xffff; else if (function_read) wpt1->func = IO$_READPBLK; else if (function_write)! wpt1->func = IO$_WRITEPBLK;9 else if (function_code) { /* User specified function */% wpt1->func = function_code_val;P switch (function_code_val & IO$M_FCODE) { /* Function without modifiers */ case IO$_READPBLK: case IO$_WRITEPBLK: case IO$_WRITECHECK: case IO$_DSE:1 wpt1->flags = 0; /* Function with transfer */ break; default: if (file)# lib$signal(&ld_noreadwrite, 0);0 wpt1->flags = FLAGS_NOLBN; /* Special case */! wpt1->lbn = 0; /* Zap lbn */. break; /* For functions without a lbn */ } } if (file) {9 wpt1->flags = FLAGS_FILE; /* Signal 'file' Function */( wpt1->sbk = &sbk; /* Point to sbk */- if (wpt1->lbn == 0) /* VBN starts at 1 */ lib$signal(&ld_vbnerror, 0); }& if (action_suspend) /* Set action */* wpt1->action = WATCH_ACTION_SUSPEND; else if (action_crash)( wpt1->action = WATCH_ACTION_CRASH; else if (action_opcom)( wpt1->action = WATCH_ACTION_OPCOM; else if (action_error) {( wpt1->action = WATCH_ACTION_ERROR;= wpt1->retcode = action_error_val; /* Error to return */ } else wpt1->retcode = 0; } } } if (resume)8 func = LDIO_RESUME_WATCH; /* Resume suspended thread */ else2 func = LDIO_ENABLE_WATCH; /* Enable watchpoint */9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, wpt, cnt, 0, 0, 0, func);$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0); if (file)' close_file(filename); /* Close file */}/* Disable watch*/void nowatch(){ struct dsc sdev; int stat; int chan;) struct watchpt *wpt, *wpt1, wptindex; struct iosb iosb; int cnt; int i, size; int *blkpnt, wptcnt; int index, index_val;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */N lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0); index = getqual(&sw_index); if (index) { /* /INDEX? */% index_val = getqualvalue(&sw_index);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,H 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */! signal_error(stat, iosb.status); wptcnt = iosb_lw;- if ((index_val < 1) || (index_val > wptcnt))< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ if (wptcnt) {, size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size))) signal_error(SS$_INSFMEM, 0);: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,@ wpt, size, 0, 0, 0, LDIO_GET_WATCH); /* Get watch data */% signal_error(stat, iosb.status);6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action; wptindex.func = wpt->func;% wptindex.retcode = wpt->retcode;! wptindex.flags = wpt->flags; wpt = &wptindex; cnt = 1; /* One entry */ } } else {7 blkpnt = getlist(&sw_lblock, &cnt); /* Get lbn list */ if (cnt) {7 if (!(wpt = malloc(cnt * sizeof(struct watchpt)))) signal_error(SS$_INSFMEM, 0);4 for (i = 0, wpt1 = wpt; i < cnt; i++, wpt1++) {< wpt1->flags = FLAGS_REMOVE_ALL; /* Flag all of this lbn *// wpt1->lbn = *blkpnt++; /* Fill lbn to kill */ } } }9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,A wpt, cnt, 0, 0, 0, LDIO_DISABLE_WATCH); /* Disable watch */$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Show watch info*/void show_watch(){ struct dsc sdev; int stat; int chan; struct watchpt *wpt; struct suspend_list *slist; struct iosb iosb;' int wptcnt, blkcnt, slistcnt, size; int *blkpnt;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */N lib$signal(&ld_badunit, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);* blkpnt = getlist(&sw_lblock, &blkcnt);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,K 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */ $ signal_error(stat, iosb.status);3 if (wptcnt = iosb_lw) { /* Something to do ? */d( size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size)))t" signal_error(SS$_INSFMEM, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,< wpt, size, 0, 0, 0, LDIO_GET_WATCH); /* Get watch data */! signal_error(stat, iosb.status);/ }N9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,gW 0, 0, 0, 0, 0, LDIO_GET_SUSPEND_LIST | LDIO_M_INQUIRE); /* Get suspend list size */e$ signal_error(stat, iosb.status);9 if (slistcnt = iosb_lw) { /* Something suspended ? */a/ size = slistcnt * sizeof(struct suspend_list);d if (!(slist = malloc(size)))c" signal_error(SS$_INSFMEM, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,G slist, size, 0, 0, 0, LDIO_GET_SUSPEND_LIST); /* Get suspend list */ ! signal_error(stat, iosb.status);a } 3 stat = sys$dassgn(chan); /* Deassign channel */9 signal_error(stat, 0);N display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt); /* Show it */}n@void display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt)struct watchpt *wpt; int wptcnt;o int blkpnt[];l int blkcnt;dstruct suspend_list *slist;h int slistcnt;{ int i, j;  int index, index_val;p int *bpt;e int found, printed;g struct suspend_list *spt;s char albn[9];/ char filename[256];t1 if (index = getqual(&sw_index)) /* /INDEX? */ 5 index_val = getqualvalue(&sw_index); /* get value */C* if (wptcnt) { /* Something to do ? */ printed = 0;f@ for (i = 0; i < wptcnt; i++, wpt++) { /* For all watchpoints */ found = 0;C< if (blkcnt) { /* Check if in list the user specified */3 for (j = 0, bpt = blkpnt; j < blkcnt; j++, bpt++)o if (*bpt == wpt->lbn) {n found++;s break;e }S } else {d& if (index) { /* Or check by index */ if (index_val == i + 1)n found++;w } else found++; } if (found) { . if (!printed) { /* Print header only once */N printf("Index LBN Action Function Error return code\n");W printf("--------------------------------------------------------------------\n");i printed = 1; }n if (wpt->flags & FLAGS_FILE) {0 create_filename(s_device,&wpt->fid,filename);) printf(" %s:\n",filename);J }n if (wpt->flags & FYt4$LD071.B[VDBURG.LD.V71.SRC]LD.C;1qvX"FLAGS_NOLBN)1F sprintf(albn, "%8s", ""); /* Blank for watchpoint without lbn */ else% sprintf(albn, "%8d", wpt->lbn);u switch (wpt->action) { case WATCH_ACTION_SUSPEND:6 printf("%2d %s Suspend %-18s\n", i + 1, albn, decode_func(wpt->func));8 for (j = 0, spt = slist; j < slistcnt; j++, spt++) if ((spt->lbn == wpt->lbn) && (spt->func == wpt->func))D printf(" Suspended process: %08.8X\n", spt->pid); break; case WATCH_ACTION_ERROR:7 printf("%2d %s Error %-18s %04.4X (%s)\n",I* i + 1, albn, decode_func(wpt->func),) wpt->retcode, ssdef(wpt->retcode)); break; case WATCH_ACTION_CRASH:6 printf("%2d %s Crash %-18s\n", i + 1, albn, decode_func(wpt->func)); break; case WATCH_ACTION_OPCOM:6 printf("%2d %s Opcom %-18s\n", i + 1, albn, decode_func(wpt->func)); break; default: break; }  } } if (!printed)< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ } else; lib$signal(&ld_nowatchdata); /* No watch data available */a}[/*) Get a list of lbn's from the commandline/*/int *getlist(sw, cnt) char *sw;/ int *cnt;L{: int more;H int *blklist, *blkpnt;C if (!(blklist = malloc(sizeof(int)))) /* get mem for 1 entry */[7 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ *cnt = 0;E more = -1; blkpnt = blklist;O for (;;) {6 *blkpnt = getmulqualvalue(sw, &more); /* Get entry */% if (more < 0) /* Not specified ? */B return (blklist); *cnt += 1;M# if (more) { /* more coming up? */ + /* Get new block to accomodate size */IA if (!(blklist = realloc(blklist, (*cnt + 1) * sizeof(int))))]8 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ blkpnt = blklist + *cnt; } elsee break;h }u return blklist;d}c/* Stop running trace*/void stop_trace()r{l int stat;* int loop = 0;t char matchname[256]; struct dsc matchdev =m# {sizeof(matchname), matchname};  short rlen;s int context[2] = {0, 0};b> s_device = getqualstring(&sw_device); /* Get devicename */, if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;/ while (stat & 1) { /* Loop until we're done */lF stat = sys$device_scan(&matchdev, &rlen, &wildname, 0, &context);! if (stat == SS$_NOMOREDEV) {h if (loop) {) if (loop == 1). lib$signal(&ld_nounitsfound, 1, matchname); else return; /* No more, quit */ } else< signal_error(SS$_NOSUCHDEV, 0); /* No device at all */ } loop++; signal_error(stat, 0);3 matchname[rlen] = '\0'; /* Place terminator */s= stop_one(matchname, 0); /* Stop trace for this device */E }, } else /* Only one specified device */2 stop_one(fulldevspec(s_device), 1); /* Stop it */} /*$ Stop trace for one specific device*/void stop_one(which, single) char *which; int single;e{* struct dsc lknam;a int stat;n char *qname; struct iosb iosb;o int lockid;  int retlen;; int count; int i; int found; int grp, mem;a union prvdef oldpriv;  struct dsc sfilename = {strlen(which), which};s struct lkidef locks[10]; struct lkidef *p;  struct itmlst item[] =0 {sizeof(locks), LKI$_LOCKS, &locks, &retlen, 0, 0, 0, 0};: struct itmlst jpi_item[] =" {8, JPI$_CURPRIV, &oldpriv, 0, 4, JPI$_GRP, &grp, 0, 4, JPI$_MEM, &mem, 0, 0, 0, 0, 0};t9 if (get_unit(which, 1) == 0) { /* Don't use unit 0 */( if (single)B lib$signal(&ld_badunit, 1, which); /* Show it to the world */ else return; }g= lknam.addr = build_lock(which); /* Build resource name */c# lknam.len = strlen(lknam.addr);tK stat = sys$enqw(0, LCK$K_NLMODE, &ld_lksb, LCK$M_SYSTEM | LCK$M_VALBLK,t8 &lknam, 0, 0, 0, 0, 0, 0); /* Enqueue null lock */ signal_error(stat, 0);q stat = sys$getlkiw(0, &ld_lksb.lockid, &item, &iosb, 0, 0, 0); /* Get info of other locks on this resource */*$ signal_error(stat, iosb.status);K count = (retlen & 0xffff) / ((retlen >> 16) & 0x7fff); /* Lock count */u p = locks; found = 0;& for (i = 0; i < count; i++, p++) {B if (p->lki$l_lkid == ld_lksb.lockid) /* Disregard our own lock */ continue; found = 1;)U stat = sys$getjpiw(0, 0, 0, &jpi_item, &iosb, 0, 0); /* Get privs+ group + member */l! signal_error(stat, iosb.status);); if (!((grp == ld_lksb.u.valblk[0]) && /* Group the same */wE (mem == ld_lksb.u.valblk[1]))) { /* as well as the member ? */? if ((grp != ld_lksb.u.valblk[0]) && /* Group the same ? */;9 (mem != ld_lksb.u.valblk[1])) { /* Member the same ? */n/ if (!oldpriv.prv$v_world) /* We need WORLD */ , lib$signal(&ld_noworldpriv, 1, which);D } else if (grp == ld_lksb.u.valblk[0]) { /* Group the same ? *// if (!oldpriv.prv$v_group) /* We need GROUP */p, lib$signal(&ld_nogrouppriv, 1, which); } }N stat = sys$enqw(0, LCK$K_EXMODE, &ld_lksb, /* Convert to fire blocking ast *// LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK,L 0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0);&C stat = sys$deq(ld_lksb.lockid, 0, 0, 0); /* Get rid of our lock */E signal_error(stat, 0); break;o } if (!found)a8 lib$signal(&ld_conttracenotact, 1, fulldevspec(which));}//* Show trace info */void show_trace()S{O struct dsc sdev; struct dsc lknam;  int fdt_active;r int accurate;c int stat;\ int chan;r int trcsize; int trcnum;n int contin;t int qstat; int func;S int inited;I int overrun; struct iosb iosb;& char *trcbuf;  struct itmlst item[] =* {4, JPI$_GRP, &ld_lksb.u.valblk[0], 0,* 4, JPI$_MEM, &ld_lksb.u.valblk[1], 0, 0, 0, 0, 0};h if (getqual(&sw_input) > 0)** process_input(); /* Process input file */ else {; s_device = getqualstring(&sw_device); /* Get devicename */e if (!s_device)F; lib$signal(&ld_confqual); /* Conflicting qualifiers */e< s_device = fulldevspec(s_device); /* Get full devicespec */7 if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */eE lib$signal(&ld_badunit, 1, s_device); /* Show it to the world */  sdev.addr = s_device; sdev.len = strlen(s_device); qstat = getqual(&sw_status);r? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);} /* Get number of tracebuffers */6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,3 0, 0, 0, 0, 0, LDIO_GET_TRACE | LDIO_M_INQUIRE);! signal_error(stat, iosb.status); * fdt_active = (iosb.status == SS$_WASSET);9 trcnum = iosb_lw; /* Size of tracebuffer (in entries) */E- trcsize = trcnum * sizeof(struct trace_ent);b< trcbuf = malloc(trcsize); /* Get memory for trace buffer */ if (trcbuf == 0)a: signal_error(SS$_INSFMEM, 0); /* Not enough memory */= func = LDIO_GET_TRACE | LDIO_M_NOWAIT; /* Normal function */T+ if (getqual(&sw_reset) > 0) /* /RESET ? */u func |= LDIO_M_RESET;B if (contin = (getqual(&sw_continuous) > 0)) { /* /CONTINUOUS ? */* func = LDIO_GET_TRACE | LDIO_M_RESET;0 set_outband(); /* Set out-of-band escape */F lknam.addr = build_lock(s_device); /* Build lock resource name */$ lknam.len = strlen(lknam.addr);N stat = sys$getjpiw(0, 0, 0, &item, &iosb, 0, 0); /* Get group + member */% signal_error(stat, iosb.status); / stat = sys$enqw(0, LCK$K_PWMODE, &ld_lksb,l$ LCK$M_SYSTEM | LCK$M_NOQUEUE,! &lknam, 0, 0, 0, 0, 0, 0);, if (stat == SS$_NOTQUEUED) 9 lib$signal(&ld_conttraceact, 1, fulldevspec(s_device));  signal_error(stat, 0);L stat = sys$enqw(0, LCK$K_PRMODE, &ld_lksb, /* Write lock value block */3 LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK,  0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0);cF stat = sys$enqw(0, LCK$K_PWMODE, &ld_lksb, /* Convert up again */3 LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK, # 0, 0, 0, 0, &ld_exit, 0, 0);  signal_error(stat, 0);T } inited = (contin > 0) ? 0 : -1; do {: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,< trcbuf, trcsize, 0, 0, 0, func); /* Get trace info */ signal_error(stat, 0);m stat = iosb.status;" if (stat == SS$_DATAOVERUN) {2 overrun = iosb_lw; /* Save number of overruns */ iosb_lw = trcnum; } else {5 overrun = 0;D signal_error(stat, 0); /* Test for other error than DATAOVERRUN */ } if (qstat) {2 lib$signal(&ld_status, 3, fulldevsR$LD071.B[VDBURG.LD.V71.SRC]LD.C;1q"Wpec(s_device),2 trcnum, iosb_lw); /* Show it to the world */ if (fdt_active) 9 lib$signal(&ld_fdtactive); /* FDT tracing active */ 7 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,t( 0, 0, 0, 0, 0, LDIO_GET_CONNECTION);" signal_error(stat, iosb.status); if (iosb_flags.accurate)< lib$signal(&ld_accurate); /* Accurate timing active */ break; } else {% if (iosb_lw) /* Anything there ? */rK process_trace(trcbuf, iosb_lw, 0, overrun, /* Process the contents */e, nodename(), cvttime(0, 0, 0), inited); } inited = 1; } while (contin);/ stat = sys$dassgn(chan);/* Deassign channel */  signal_error(stat, 0);e' free(trcbuf); /* Get rid of buffer */  }(} /* Process the trace buffer*/Cvoid process_trace(buf, size, ovrbuf, overrun, nname, when, inited) char *buf; int size; struct ovrrun *ovrbuf; int overrun; char *nname; char *when;e int inited;u{e static int q_bin, q_entries;0 static int q_blocks, q_version_limit, q_vsw; static int v_end1, v_end2; static int re_open = 0;: static int written = 0;o struct trace_ent *p; static int outflg; int i; int version, recnum; int end1, end2; int entr, numpack;* if ((inited <= 0) || (re_open <= 0)) { if (re_open == 0) {7 q_entries = getqual(&sw_entries); /* /ENTRIES ? */a if (q_entries > 0) {nC v_end1 = getqualvalue(&sw_entries); /* Get #of entries to show */nD v_end2 = getqualvalue(&sw_entries); /* Get second part (if any) */ }1 q_bin = getqual(&sw_binary); /* /BINARY ? */_4 q_blocks = getqual(&sw_blocks); /* /BLOCKS ? */ if (q_blocks)6 q_blocks = getqualvalue(&sw_blocks); /* Get value */? q_vsw = getqual(&sw_version_limit); /* /VERSION_LIMIT ? */_ if (q_vsw) { D q_version_limit = getqualvalue(&sw_version_limit); /* Get value */ if (q_version_limit >= 32767)/ q_version_limit = 32767; if (q_version_limit <= 0)g q_version_limit = 1; q_version_limit--; }2 outflg = getqual(&sw_output); /* /OUTPUT ? */ if (outflg > 0)= outfilename = getqualstring(&sw_output); /* Get filename */b } if (outflg > 0) { if (q_bin)sP outfilename = fullfilespec(outfilename, "LD_TRACE.DAT"); /* Provide default */ elserP outfilename = fullfilespec(outfilename, "LD_TRACE.LOG"); /* Provide default */6 if (re_open < 0) { /* New open, close old file */C if (outfile >= 0) { /* Closing outfile closes outfilei as well */N if (fclose(outfile) < 0): lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); }g if (q_vsw)/ purge_file(q_version_limit, outfilename);d }/*G We want to create a normal file with some nice RMS attributes. SinceiI this can't be done with fopen() we will create the file first, and usenI fdopen to setup the correct file pointers. This takes also care of thea/ substitution of the defaults filename parts.o*/ if (q_bin)9 outfilei = creat(outfilename, 0, "rfm=var", "ctx=bin");n else*8 outfilei = creat(outfilename, 0, "rfm=var", "rat=cr"); if ((outfilei < 0) ||/ ((outfile = fdopen(outfilei, "a+")) == NULL))tX lib$signal(&ld_outfilerr, 1, outfilename, vaxc$errno, 0); /* Can't open output file */ re_open = 1; ) } else if (outflg < 0) /* /NOOUTPUT ? */h return; elsed+ outfile = stdout; /* Default output */s } end2 = 0;N) if (q_entries > 0) { /* /ENTRIES ? */. end1 = v_end1; /* Get #of entries to show *// end2 = v_end2; /* Get second part (if any) */S- if (end2 != 0) { /* There's a second part */ if ((end1 <= 0) ||S (end2 <= 0) || (end2 < end1)); lib$signal(&ld_badentparam); /* Bad /ENTRIES parameter */w } if (end1 == 0)b# return; /* Nothing to show */  if (end1 > 0) {8 p = (struct trace_ent *) buf; /* Start of buffer */, recnum = 1; /* First one to display */ if (end2 > 0) {2 p += (end1 - 1);/* New start, first parameter */* recnum = end1; /* Adjust recordnumber */ } } else {s& end1 = -end1; /* Make absolute */1 i = ((size - end1) < 0) ? 0 : (size - end1); & p = (struct trace_ent *) buf + i;/ recnum = i + 1; /* First one to display */e } } else {4 p = (struct trace_ent *) buf; /* Start of buffer */" end1 = size; /* Default = all */( recnum = 1; /* First one to display */ } 2 if (end1 > size) { /* More than we've got? */ if (end2 > 0)5 lib$signal(&ld_pastdata); /* Past end of data */o( end1 = end2 = size; /* Enforce limit */ }B if (end2 > size)& end2 = size; /* And another limit */ if (end2 > 0)=A numpack = end2 - end1 + 1; /* Total number of packets to show */0 else numpack = end1;# if (q_bin) { /* Binary mode */(# version = DATA_FILE_VERSION << 24;5 if ((inited <= 0) || ((written == 0) && q_blocks)) {aT if ((fwrite(&version, sizeof(int), 1, outfile) != 1) || /* Data file version */K (fwrite(fulldevspec(s_device), 32, 1, outfile) != 1) || /* Devicename */c9 (fwrite(nname, 32, 1, outfile) != 1) || /* Nodename */a7 (fwrite(when, 24, 1, outfile) != 1)) /* Timestamp */ Z lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ if (q_blocks)D written += sizeof(int) + 32 + 32 + 24 + (4 * 2); /* Header size */ }P if ((fwrite(&numpack, sizeof(int), 1, outfile) != 1) || /* Number of entries */R (fwrite(&overrun, sizeof(int), 1, outfile) != 1) || /* Number of overruns */T (fwrite(p, sizeof(struct trace_ent), numpack, outfile) != numpack)) /* Data */] lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */n if (q_blocks) {) written += (2 * (sizeof(int) + 2)) +i1 ((sizeof(struct trace_ent) + 2) * numpack);.- if (written >= ((q_blocks - 1) * 512)) {e+ re_open = -1; /* Set flag for new file */$ written = 0; } } } else4 show_trace_data(p, numpack, recnum, outfile, nname,, s_device, when, inited, ovrbuf, overrun);F if ((outfile != stdout) && (inited < 0)) { /* Close output file */ if (fclose(outfile) < 0) < lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); } };/* Format all tracedata*/5void show_trace_data(p, numpack, from, outfile, node,)- device, when, inited, ovrbuf, overrun)mstruct trace_ent *p; int numpack; int from;[FILE *outfile; char *node;e char *device;a char *when;] int inited;estruct ovrrun *ovrbuf; int overrun;{n struct ovrrun *op;! int i, j, stat, func, ovrcnt;.2 static int pid, lbn, bytecount, fdt, accurate;) static int iosb, function, timestamp;[I static int time_elapsed, time_absolute, time_combination, time_delta;r+ static int function_hex, function_text; & static int iosb_hex, iosb_longhex;+ static int iosb_text, iosb_combination;' static int number, header, linenum; % static int prev_tim[2], warnings;s int restim[2]; char record[256];l char tmp[256];/* Parse various qualifiers*/ if (inited <= 0) { pid = getqual(&sw_pid); lbn = getqual(&sw_lbn); fdt = getqual(&sw_fdt);" accurate = getqual(&sw_accurate);$ bytecount = getqual(&sw_bytecount);$ timestamp = getqual(&sw_timestamp);* time_elapsed = getqual(&sw_time_elapsed);, time_absolute = getqual(&sw_time_absolute);2 time_combination = getqual(&sw_time_combination);& time_delta = getqual(&sw_time_delta);" function = getqual(&sw_function);* function_hex = getqual(&sw_function_hex);, function_text = getqual(&sw_function_text); iosb = getqual(&sw_iosb);" iosb_hex = getqual(&sw_iosb_hex);* iosb_longhex = getqual(&sw_iosb_longhex);$ iosb_text = getqual(&sw_iosb_text);2 iosb_combination = getqual(&sw_iosb_combination); number = getqual(&sw_number); header = getqual(&sw_header);" warnings = getqual(&sw_warnings); linenum = from; prev_tim[0] = 0;r prev_tim[1] = 0;  }[8 if ((header > 0) && (inited <= 0)) { /* /HEADER ? */A if (fprintf(outfile, " I/O trace for device %s\n %s",0 device, when) <= 0)\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */& if (node) /* Nodename available ? */6 stat = fprintf(outfile, " on node %s\n\n", node); else0% stat = fprintf(outfile, "\n\n");, if (stat <= 0)e\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */) *record = '\0'; /* Initially nothing */x if (number > 0) strcpy(record, "Entry "); if (timestamp > 0) {t if (time_elapsed) strcat(record, "Elaps ");x else if (time_abs$LD071.B[VDBURG.LD.V71.SRC]LD.C;1qA"holute)- strcat(record, "Start Time End Time ");v else if (time_combination)t' strcat(record, "Start Time Elaps ");  else if (time_delta)u' strcat(record, " Delta Time Elaps ");w if (accurate) strcat(record, "uSecs ");v } if (pid > 0)h! strcat(record, " Pid ");l if (lbn > 0)n# strcat(record, " Lbn "); if (bytecount > 0)p strcat(record, " Bytes ");y if (iosb > 0) { if (iosb_hex) strcat(record, " Iosb[0] "); else if (iosb_longhex)' strcat(record, " Iosb[0] Iosb[1] "); else if (iosb_text) strcat(record, " Iosb ");s else if (iosb_combination)$ strcat(record, " Iosb Count "); } if (function > 0)" strcat(record, " Function "); if (!(*record)), return; /* Nothing to display, quit */ j = strlen(record);/ record[j - 1] = '\n'; /* Zap trailing space */e record[j] = '\0';8 for (i = 0; i < j - 1; i++) /* Form string of dashes */ tmp[i] = '-';( tmp[i++] = '\n'; /* Trailing newline */ tmp[i] = '\0';t- if ((fprintf(outfile, "%s", record) <= 0) ||,( (fprintf(outfile, "%s", tmp) <= 0))\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ }tA for (i = 0; i < numpack; i++) { /* For all records to show */n? if ((overrun != 0) && (warnings > 0)) { /* Signal lost data */< if (ovrbuf == 0) { /* Called with number of overruns */ ovrcnt = overrun;n5 overrun = 0; /* Prevent display for every record */_ } else {o4 op = ovrbuf; /* Called after reading input file */) ovrcnt = 0; /* Check overrun records */tB for (j = 0; j < overrun; j++, op++) /* For all overrunrecords */& if (op->recnum == linenum - 1) {* ovrcnt = op->overrun; /* Found match */ break;. }l } if (ovrcnt > 0)3 if (fprintf(outfile, "[%d trace record%slost]\n",b. ovrcnt, ovrcnt == 1 ? " " : "s ") <= 0)] lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */  }S if ((p->iosb[0] != SS$_FDT_COMPL) || ((p->iosb[0] == SS$_FDT_COMPL) && fdt > 0)) {_ *record = '\0';@ func = p->func & IO$M_FCODE; /* Strip function modifiers */H if ((func == IO$_SETMODE) || /* These functions don't have a lbn */! (func == IO$_PACKACK) || # (func == IO$_AVAILABLE) ||s (func == IO$_UNLOAD))4 p->lbn = 0; /* Clear for better readout */( if (number > 0) /* Line numbers */* sprintf(record, "%5d ", linenum);$ linenum++; /* One more line */, if (timestamp > 0) { /* Time display */ if (time_elapsed) {? stat = lib$sub_times(p->end_time, p->start_time, restim);v signal_error(stat, 0);- strcat(record, cvttime(&restim, 1, 1));u$ } else if (time_absolute) {4 strcat(record, cvttime(&p->start_time, 0, 1)); strcat(record, " ");2 strcat(record, cvttime(&p->end_time, 0, 1));' } else if (time_combination) {h? stat = lib$sub_times(p->end_time, p->start_time, restim);, signal_error(stat, 0);4 strcat(record, cvttime(&p->start_time, 0, 1)); strcat(record, " ");- strcat(record, cvttime(&restim, 1, 1));n! } else if (time_delta) {n5 if ((prev_tim[0] == 0) && (prev_tim[1] == 0)) {/( strcat(record, " 0:00:00.00"); } else {@ stat = lib$sub_times(p->start_time, prev_tim, restim); if (!(stat & 1)) {! if (stat == LIB$_NEGTIM) {uA stat = lib$sub_times(prev_tim, p->start_time, restim); ! signal_error(stat, 0);d strcat(record, "-"); } elsev! signal_error(stat, 0);  } else strcat(record, " "); 1 strcat(record, cvttime(&restim, 1, 0));  }I strcat(record, " ");? stat = lib$sub_times(p->end_time, p->start_time, restim);f signal_error(stat, 0);- strcat(record, cvttime(&restim, 1, 1));,% prev_tim[0] = p->start_time[0];t% prev_tim[1] = p->start_time[1];e } if (accurate) {i& sprintf(tmp, "%6Ld",p->elapsed); strcat(record, tmp); } strcat(record, " ");  } if (pid > 0) { /* Pid */' sprintf(tmp, "%08X ", p->pid);) strcat(record, tmp);. }/ if (lbn > 0) { /* Logical block number */ ' sprintf(tmp, "%10d ", p->lbn);& strcat(record, tmp);/ }) if (bytecount > 0) { /* Bytecount */,' sprintf(tmp, "%6d ", p->bcnt);l strcat(record, tmp);o }, if (iosb > 0) { /* I/O status block */ if (iosb_hex)* sprintf(tmp, "%08.8X ", p->iosb[0]); else if (iosb_longhex)= sprintf(tmp, "%08.8X %08.8X ", p->iosb[0], p->iosb[1]);e else if (iosb_text) {g if ((p->iosb[0] == SS$_FDT_COMPL) && (p->iosb[1] == 0) && (p->lbn == 0) && (p->bcnt == 0))($ sprintf(tmp,"%-8s","FDT"); else> sprintf(tmp, "%-7s ", ssdef((p->iosb[0]) & 0xffff));' } else if (iosb_combination) {) if (func == IO$_SENSEMODE) j = 0; else- j = ((p->iosb[0] >> 16) & 0xffff) +t) ((p->iosb[1] & 0xffff) << 16);1h if ((p->iosb[0] == SS$_FDT_COMPL) && (p->iosb[1] == 0) && (p->lbn == 0) && (p->bcnt == 0))% sprintf(tmp,"%-15s","FDT");a elseE sprintf(tmp, "%-7s %6d ", ssdef((p->iosb[0]) & 0xffff), j);e } strcat(record, tmp);i }0 if (function > 0) { /* I/O function code */ if (function_hex)' sprintf(tmp, "%04.4X ", p->func);q else if (function_text)0 sprintf(tmp, "%s ", decode_func(p->func)); strcat(record, tmp);i } if (!(*record))/ break; /* Nothing to display, quit */= j = strlen(record);3 record[j - 1] = '\n'; /* Zap trailing space */ record[j] = '\0';6 if ((stat = fprintf(outfile, "%s", record)) <= 0)` lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ } p++; /* Next record */r }a}o/*! Convert status to readable codeo*/char *ssdef(code)_ int code;{  static char msgbuf[64];E struct dsc msgdsc =c {sizeof(msgbuf), msgbuf};= int stat;s short rlen;2 stat = sys$getmsg(code, &rlen, &msgdsc, 2, 0); signal_error(stat, 0);, msgbuf[rlen] = '\0'; /* Trailing zero */. return &msgbuf[1]; /* Remove leading % */}e/* Process input file*/void process_input(){ char *buf, *savbuf;_& struct ovrrun *ovrbuf, *savovrbuf; char *infilename;g int infilei; FILE *infile;0 char namebuf[32]; char nodebuf[32];n char timestmp[24]; int i; int size; int recnum;f int overruncnt;? int version; int overrun;N if ((infilename = getqualstring(&sw_input)) == 0) /* Get input filename */E infilename = fullfilespec("", "LD_TRACE.DAT"); /* Provide default */  elseM infilename = fullfilespec(infilename, "LD_TRACE.DAT"); /* Provide default */iY if (((infilei = open(infilename, O_RDONLY, 0, "dna=LD_TRACE.DAT", "ctx=bin")) < 0) ||n, ((infile = fdopen(infilei, "rb")) == NULL))T lib$signal(&ld_infilerr, 1, infilename, vaxc$errno, 0); /* Can't open input file */P if (fread(&version, sizeof(int), 1, infile) != 1) /* Get datafile version */W lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */a% version = (version >> 24) & 0xff;o- if ((version == 0) || /* Old version ? */-= (version != DATA_FILE_VERSION)) /* Incompatible version ? */ L lib$signal(&ld_cantreadoldfmt, 1, version); /* Can't read old input file */D if ((fread(namebuf, 32, 1, infile) != 1) || /* Get devicename */; (fread(nodebuf, 32, 1, infile) != 1) || /* Get nodename */k; (fread(timestmp, 24, 1, infile) != 1)) /* Get timestamp */wV lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */2 s_device = namebuf; /* Point to devicename */ recnum = 0;; overruncnt = 0;f buf = savbuf = 0;  ovrbuf = savovrbuf = 0;i while (1) {O if ((fread(&size, sizeof(int), 1, infile) != 1) || /* Get number of records */=R (fread(&overrun, sizeof(int), 1, infile) != 1)) /* Get number of overruns */[ lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */ /*G We will call realloc to increase the size of our databuffer. The data B remains valid up till the address where we've written it before.*/N if ((savbuf = realloc(buf, (size + recnum) * sizeof(struct trace_ent))) == 0); signal_err xI$$LD071.B[VDBURG.LD.V71.SRC]LD.C;1qG"yor(SS$_INSFMEM, 0); /* Not enough memory */S& buf = savbuf; /* Save new address */M savbuf += (recnum * sizeof(struct trace_ent)); /* Start writing from here */f if (overrun != 0) {6 if ((savovrbuf = realloc(ovrbuf, (overruncnt + 1)( * sizeof(struct ovrrun))) == 0)8 signal_error(SS$_INSFMEM, 0); /* Not enough memory *// ovrbuf = savovrbuf; /* Save new address */SB savovrbuf += overruncnt; /* Count total number of overruns */F savovrbuf->recnum = recnum; /* Save recordnumber with overflow */O savovrbuf->overrun = overrun; /* Save count of overruns for this record */! overruncnt++; /* One more */R }4 recnum += size; /* Total number of data records */R if (fread(savbuf, sizeof(struct trace_ent), size, infile) != size) /* Get data */[ lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */C/*K fread returns a 0 on EOF as well as when an error occurred. Since we want,N to distinguish these two we try to read one more byte to find the difference*/ if ((i = fgetc(infile)) == EOF)' break; /* Ready for processing */ else(< ungetc(i, infile); /* Return character and try again */ } I process_trace(buf, recnum, ovrbuf, overruncnt, nodebuf, timestmp, 0); $ free(buf); /* Return buffer */ if (ovrbuf != 0)# free(ovrbuf); /* Return buffer */ 2 if (fclose(infile) < 0) /* Close input file */7 lib$signal(&ld_closerr, 1, infilename, vaxc$errno, 0);)}*/*) Decode the I/O function to readable text,*/char *decode_func(what) int what;g{( static char funcstr[80]; char str[80];t int modifier;  int function;i if (what == 0xffff)( return ("All");s: modifier = what & ~IO$M_FCODE; /* Get modifier bits */= function = what & IO$M_FCODE; /* Get functioncode bits */(. if (!modifier) /* No modifiers present */9 return cvttbl[function];/* Return pointer to function */ else { *str = '\0';; switch (function) { case IO$_PACKACK:8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);> chk_mod(&modifier, IO$M_MSCP_FORMAT, "MSCP_FORMAT", str); break;a case IO$_AVAILABLE:8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);8 chk_mod(&modifier, IO$M_ALLHOSTS, "ALLHOSTS", str);8 chk_mod(&modifier, IO$M_DISSOLVE, "DISSOLVE", str);: chk_mod(&modifier, IO$M_NOCLEANUP, "NOCLEANUP", str); break;_ case IO$_REMSHAD:8 chk_mod(&modifier, IO$M_SPINDOWN, "SPINDOWN", str); break;_ case IO$_SENSECHAR:4 chk_mod(&modifier, IO$M_SHADOW, "SHADOW", str); break;  case IO$_SETMODE:6 chk_mod(&modifier, IO$M_LINE_ON, "LINE_ON", str);8 chk_mod(&modifier, IO$M_LINE_OFF, "LINE_OFF", str);6 chk_mod(&modifier, IO$M_NEWLINE, "NEWLINE", str);2 chk_mod(&modifier, IO$M_ABORT, "ABORT", str); break;/ case IO$_UNLOAD:: chk_mod(&modifier, IO$M_CLSEREXCP, "CLSEREXCP", str); break;  case IO$_DSE:: chk_mod(&modifier, IO$M_DATACHECK, "DATACHECK", str);2 chk_mod(&modifier, IO$M_ERASE, "ERASE", str); break;b case IO$_WRITEVBLK: case IO$_WRITELBLK: case IO$_WRITEPBLK:6 chk_mod(&modifier, IO$M_TRUSTED, "TRUSTED", str);4 chk_mod(&modifier, IO$M_EXFUNC, "EXFUNC", str);8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);: chk_mod(&modifier, IO$M_DATACHECK, "DATACHECK", str);: chk_mod(&modifier, IO$M_CLSEREXCP, "CLSEREXCP", str);8 chk_mod(&modifier, IO$M_INHRETRY, "INHRETRY", str);2 chk_mod(&modifier, IO$M_ERASE, "ERASE", str);< chk_mod(&modifier, IO$M_MSCPMODIFS, "MSCPMODIFS", str); break;l case IO$_READVBLK: case IO$_READLBLK: case IO$_READPBLK:l8 chk_mod(&modifier, IO$M_NOVCACHE, "NOVCACHE", str);6 chk_mod(&modifier, IO$M_TRUSTED, "TRUSTED", str);4 chk_mod(&modifier, IO$M_EXFUNC, "EXFUNC", str);8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);: chk_mod(&modifier, IO$M_DATACHECK, "DATACHECK", str);8 chk_mod(&modifier, IO$M_INHRETRY, "INHRETRY", str); break;/ case IO$_ACCESS:s case IO$_DEACCESS:s case IO$_MODIFY:$ case IO$_CREATE:  case IO$_DELETE:  case IO$_ACPCONTROL: 4 chk_mod(&modifier, IO$M_ACCESS, "ACCESS", str);4 chk_mod(&modifier, IO$M_CREATE, "CREATE", str);4 chk_mod(&modifier, IO$M_DELETE, "DELETE", str);4 chk_mod(&modifier, IO$M_DMOUNT, "DMOUNT", str);4 chk_mod(&modifier, IO$M_EXFUNC, "EXFUNC", str); break; default:, break;e }L sprintf(funcstr, "%s%s", cvttbl[function], str); /* Form function string */7 if (modifier & ~IO$M_FCODE) /* Any modifiers left ? */&T sprintf(funcstr, "%s (%08.8X)", funcstr, modifier); /* Show excess modifiers */1 return (funcstr); /* Return pointer to string */s } }(/*< Check if bit set in modifier function, and copy function to output string if true.l*/.void chk_mod(modifier, mask, modifstr, outstr)int *modifier; int mask;achar *modifstr;h char *outstr;R{ 7 if (*modifier & mask) { /* Check if mask bit set */I+ strcat(outstr, "|"); /* Concatenate bar */t: strcat(outstr, modifstr); /* Concatenate inpout string */6 *modifier &= ~mask; /* Flags this bit as processed */ }a}(/* Convert binary time to ascii,s' return only the time and not the datew*/char *cvttime(time, how, full) int time[2];int how; int full;;{n short rlen;l int stat;l struct dsc timdsc; static char timstr[256]; timdsc.len = 256;n timdsc.addr = timstr;nU stat = sys$asctim(&rlen, &timdsc, time, full); /* Convert binary time to ascii */ signal_error(stat, 0);0 timstr[rlen] = '\0'; /* Add trailing zero */) if (how) /* Display only seconds */g return (&timstr[6]);p return timstr;}o/* Open specified filer*/void open_file(name) char *name; { int stat; int i; struct iosb iosb;{ #ifndef __vaxi/ struct XAB_CACHING_OPTIONS_FLAGS cacheopts;p struct itmlst items[2];#endif6 ld_fab.fab$l_dna = ".DSK"; /* Default extension */* ld_fab.fab$b_dns = 4; /* and length */5 ld_fab.fab$l_fop = FAB$M_NAM; /* Use NAM block */+: ld_fab.fab$l_nam = &ld_nam; /* Pointer to NAM block */4 ld_fab.fab$l_fna = name; /* File name address */5 ld_fab.fab$b_fns = strlen(name); /* and length */e9 ld_fab.fab$l_xab = &ld_xabfhc; /* Point to FHC XAB */-> ld_fab.fab$b_rtv = 255; /* Open with cathedral windows */> ld_nam.nam$b_rss = NAM$C_MAXRSS; /* Max. resultant size */7 ld_nam.nam$l_rsa = resspec; /* Resultant address */"= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */i6 ld_nam.nam$l_esa = expspec; /* Expanded address */J ld_nam.nam$b_nop = NAM$M_NOCONCEAL; /* Don't hide concealed devices */ #ifndef __vaxH9 ld_xabfhc.xab$l_nxt = &ld_xabitm; /* Link next xab */ ( cacheopts.xab$v_file_attributes = 0;Q cacheopts.xab$v_file_contents = XAB$K_NOCACHING;/* Disable caching of data */pE cacheopts.xab$v_flush_on_close= XAB$K_FLUSH; /* Flush on close */ % cacheopts.xab$v_cachectl_mbz = 0;dK items[0].buflen = sizeof(cacheopts); /* Descriptor for cache options */ ) items[0].item = XAB$_CACHING_OPTIONS;,7 items[0].addr = &cacheopts; /* Point to options */r items[0].rlength = 0; . items[1].buflen = 0; /* Terminate list */ items[1].item = 0;6 ld_xabitm.xab$b_cod = XAB$C_ITM; /* Init xabitm */' ld_xabitm.xab$b_bln = XAB$C_ITMLEN;"> ld_xabitm.xab$l_itemlist = &items; /* Point to itemlist */) ld_xabitm.xab$b_mode = XAB$K_SETMODE;r ld_xabitm.xab$l_nxt = 0;#endif3 stat = sys$parse(&ld_fab); /* Parse this one */o if (!(stat & 1))F lib$signal(&ld_openerr, 1, name, ld_fab.fab$l_sts, ld_fab.fab$l_stv);/*B Copy devicename + directoryname. This is done to include possible5 rooted devicenames which we will lose after the openk*/, i = ld_nam.nam$b_dev + ld_nam.nam$b_dir;+ strncpy(realspec, ld_nam.nam$l_dev, i);l8 realspec[i] = '\0'; /* Make sure it's terminated *// stat = sys$open(&ld_fab); /* And open it */  if (!(stat & 1))F lib$signal(&ld_openerr, 1, name, ld_fab.fab$l_sts, ld_fab.fab$l_stv);R strncat(&realspec[i], ld_nam.nam$l_name, ld_nam.nam$b_name + /* Append rest */+ ld_nam.nam$b_type + ld_nam.nam$b_ver);i9 realspec_dsc.addr = realspec; /* Get real filespec */s( realspec_dsc.len = strlen(realspec);X ldfilename_dsc.addr = &realspec[ld_nam.nam$b_dev]; /* Get filename without device */= ldfilename_dsc.len = strlen(realspec) - ld_nam.nam$b_dev;B realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len =  C$LD071.B[VDBURG.LD.V71.SRC]LD.C;1q2ld_nam.nam$b_dev;eB maxblocks = ld_xabfhc.xab$l_ebk; /* Get current eof pointer */; if (ld_xabfhc.xab$w_ffb == 0) /* Adjust if necessary */& maxblocks--;G#ifdef __DECC_MODE_VAXCf= fib.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */t+ fib.fib$w_fid[1] = ld_nam.nam$w_fid[1];a+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];m#elsecO fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */)= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1]; = fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];N#endifT stat = sys$assign(&realdev_dsc, &fchan, 0, 0, 0); /* Assign channel to device */6 signal_error(stat, 0); /* where file resides */% atr[0].atr$w_size = SBK$K_LENGTH;rA atr[0].atr$w_type = ATR$C_STATBLK; /* Get statistics block */ atr[0].atr$l_addr = &sbk;,, atr[1].atr$w_size = 0; /* Terminator */ atr[1].atr$w_type = 0;#ifdef __DECC_MODE_VAXCl6 fib.fib$v_notrunc = 1; /* Truncate not allowed */< fib.fib$b_wsize = -1; /* Open with cathedral windows */#else [ fib.fib$r_acctl_overlay.fib$r_acctl_bits0.fib$v_notrunc = 1; /* Truncate not allowed */ c fib.fib$r_acctl_overlay.fib$r_acctl_fields2.fib$b_wsize = -1; /* Open with cathedral windows */ #endif+ fib_dsc.addr = &fib; /* Point to FIB */  fib_dsc.len = FIB$K_LENGTH;$D stat = sys$qiow(0, fchan, IO$_ACCESS | IO$M_ACCESS, &iosb, 0, 0,8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);}/*3 Flush the XFC for the specified file (not for Vax) */ #ifndef __vaxvoid flush_xfc(name) char *name;u{, struct xfcfib {t union { struct fibdef f;t struct fibdef1 f1;d } u; } lfib;B int stat;a char recbuf[512];  struct iosb iosb;  short fchan; struct dsc devdsc; struct dsc fibdsc;6 open_file(name); /* Open file to get file-id */( bzero((void *) &lfib, sizeof(lfib));> devdsc.addr = ld_nam.nam$l_dev; /* Get real devicename */" devdsc.len = ld_nam.nam$b_dev;#ifdef __DECC_MODE_VAXCrA lfib.u.f.fib$w_fid[0] = ld_nam.nam$w_fid[0];/* Get file-id */ 0 lfib.u.f.fib$w_fid[1] = ld_nam.nam$w_fid[1];0 lfib.u.f.fib$w_fid[2] = ld_nam.nam$w_fid[2];; lfib.u.f.fib$v_norecord = 1; /* Don't record update */o2 lfib.u.f.fib$v_write = 1; /* Allow writes */#elseiT lfib.u.f.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */B lfib.u.f.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];B lfib.u.f.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];` lfib.u.f.fib$r_acctl_overlay.fib$r_acctl_bits0.fib$v_norecord = 1; /* Don't record update */V lfib.u.f.fib$r_acctl_overlay.fib$r_acctl_bits0.fib$v_write = 1; /* Allow writes */#endif close_file(name);wO stat = sys$assign(&devdsc, &fchan, 0, 0, 0); /* Assign channel to device */s6 signal_error(stat, 0); /* where file resides */. fibdsc.addr = &lfib; /* Point to FIB */ fibdsc.len = sizeof(lfib);D stat = sys$qiow(0, fchan, IO$_ACCESS | IO$M_ACCESS, &iosb, 0, 0,4 &fibdsc, 0, 0, 0, 0, 0); /* Access the file */I /* Continue on success, quit on writelock or access conflict error */ X if ((stat & 1) && (iosb.status != SS$_WRITLCK) && (iosb.status != SS$_ACCONFLICT)) {! signal_error(stat, iosb.status);1$ /* Disable caching for this file */$ lfib.u.f.fib$v_file_attributes = 0;0 lfib.u.f.fib$v_file_contents = ATR$C_NOCACHING;- lfib.u.f.fib$v_flush_on_close = ATR$C_FLUSH;n( lfib.u.f.fib$v_caching_options_mbz = 0;; lfib.u.f1.fib$l_cntrlval = lfib.u.f.fib$l_caching_options; 3 lfib.u.f1.fib$w_cntrlfunc = FIB$C_CACHING_OPTIONS;tA /* Tell the xqp to disable caching. The xqp will tell the xfc */ 7 stat = sys$qiow(0, fchan, IO$_ACPCONTROL, &iosb, 0, 0,k: &fibdsc, 0, 0, 0, 0, 0); /* Set access to 'no cache' */! signal_error(stat, iosb.status);* /* Read first block */ 5 stat = sys$qiow(0, fchan, IO$_READVBLK, &iosb, 0, 0,i( &recbuf, sizeof(recbuf), 1, 0, 0, 0);! signal_error(stat, iosb.status);uO /* Rewrite block - This will force the XFC to flush the cache for this file */t6 stat = sys$qiow(0, fchan, IO$_WRITEVBLK, &iosb, 0, 0,( &recbuf, sizeof(recbuf), 1, 0, 0, 0);! signal_error(stat, iosb.status);s5 stat = sys$qiow(0, fchan, IO$_DEACCESS, &iosb, 0, 0,u3 &fibdsc, 0, 0, 0, 0, 0); /* Deaccess the file */ ! signal_error(stat, iosb.status);a }(: stat = sys$dassgn(fchan); /* And get rid of channel */ signal_error(stat, 0);} #endif/*$ Close file opened with 'open_file'*/void close_file(filename)tchar *filename;{t int stat;. struct iosb iosb;8 stat = sys$qiow(0, fchan, IO$_DEACCESS, &iosb, 0, 0,3 &fib_dsc, 0, 0, 0, 0, 0); /* Deaccess file */u$ signal_error(stat, iosb.status);: stat = sys$dassgn(fchan); /* And get rid of channel */ signal_error(stat, 0);3 stat = sys$close(&ld_fab); /* Close RMS file */N if (!(stat & 1))J lib$signal(&ld_closerr, 1, filename, ld_fab.fab$l_sts, ld_fab.fab$l_stv); signal_error(stat, 0);}u/* Disconnect file from LD device*/void disconnect_unit(){; int stat;a int q_abort; int loop = 0;  char matchname[256]; struct dsc matchdev =e# {sizeof(matchname), matchname};e short rlen;  int context[2] = {0, 0};m? s_device = getqualstring(&sw_device); /* Get device name */oA q_abort = getqual(&sw_abort) > 0; /* Get 'abort' qualifier */ , if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;/ while (stat & 1) { /* Loop until we're done */iF stat = sys$device_scan(&matchdev, &rlen, &wildname, 0, &context);! if (stat == SS$_NOMOREDEV) {0 if (loop) {  if (loop == 1). lib$signal(&ld_nounitsfound, 1, matchname); else return; /* No more, quit */ } else< signal_error(SS$_NOSUCHDEV, 0); /* No device at all */ } loop++; signal_error(stat, 0);M3 matchname[rlen] = '\0'; /* Place terminator */,2 stat = disconnect_one(matchname, q_abort, 0);! /* Disconnect this device */:: if (stat == SS$_DEVINACT) { /* If not connected... */; lib$signal(&ld_notconnected, 1, matchname); /* Flag it */t stat = 1; /* and continue */ } } } else {- stat = disconnect_one(s_device, q_abort, 1);6 if (stat == SS$_DEVINACT) { /* If not connected... */J lib$signal(&ld_notconnected, 1, fulldevspec(s_device)); /* Flag it */= stat = &ld_notconnected; /* Convert to better message */t7 stat |= STS$M_INHIB_MSG; /* Don't show it again */, } }n} *int disconnect_one(dev, abortflag, single) char *dev;int abortflag; int single;e{/ char ldfile[256];  char lddev[64];, struct dsc sdev; char *devstr; int stat;L int chan;I int func;e int replacefl; struct iosb iosb;a struct fiddef fid;K devstr = fulldevspec(dev); /* Save devicename while it's still there */(: if (get_unit(devstr, 1) == 0) { /* Don't use unit 0 */ if (single)C lib$signal(&ld_badunit, 1, devstr); /* Show it to the world */  elset return (1); }  sdev.addr = dev; sdev.len = strlen(dev);)B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* assign channel */@ if (stat == SS$_DEVALLOC) { /* Allocated on remote node ? */( lib$signal(&ld_remotealloc, 1, devstr); } else { signal_error(stat, 0);% if (!abortflag) { /* No /ABORT ? */o: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,U lddev, sizeof(lddev), &fid, 0, 0, LDIO_GET_CONNECTION); /* Get current status */s% signal_error(stat, iosb.status); H if (!iosb_flags.connected) /* Check 'connected' flag from driver */ return (&ld_notconnected);4 lddev[iosb.size] = '\0'; /* Terminate string */F if (!iosb_flags.replaced) /* Check 'replaced' flag from driver */% create_filename(lddev,&fid,ldfile);i elsel strcpy(ldfile, lddev);2 func = LDIO_DISCONNECT; /* Normal function */ } else {_E func = LDIO_DISCONNECT | LDIO_M_ABORT; /* Some more for abort */n& strcpy(ldfile,"(not available)"); } M stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Issue disconnect */s 0, 0, 0, 0, 0, func);q! signal_error(stat, iosb.status);(2 stat = sys$dassgn(chan); /* Get rid of channel */ signal_error(stat, 0);R' if (getqual(&sw_log) > 0) /* /LOG ? */_J lib$signal(&ld_nowdisconn, 2, devstr, ldfile); /* Show what we did */ }i return (1); } /* Create file for LD*/ void create()={g int stat;o int cloneavail;s  r $LD071.B[VDBURG.LD.V71.SRC]LD.C;1q~"int q_size;  int tracks;  int sectors; int cylinders; int maxblock;) int lbn; int zero = 0;s char dummy[512]; struct metadata mdat; 0 tracks = sectors = cylinders = maxblock = 0; lbn = getqualvalue(&sw_lbn);8 s_file = getqualstring(&sw_file); /* Get filespec */* s_file = fullfilespec(s_file, ".DSK");B if ((q_size = getqualvalue(&sw_size)) == 0) /* Get filesize */ q_size = 512; /* Default */gH cloneavail = getclonedata(&tracks, §ors, &cylinders, &maxblock); if (cloneavail) {1> if (q_size < maxblock) /* Allow a bigger size if specified */ q_size = maxblock;f } else {E tracks = getqualvalue(&sw_tracks); /* Get number of tracks */tH sectors = getqualvalue(&sw_sectors); /* Get number of sectors */M cylinders = getqualvalue(&sw_cylinders);/* Get number of cylinders */eM maxblock = getqualvalue(&sw_maxblocks); /* Get number of maxblocks */ } c/*L First create a file of 0 blocks. That way we can be sure to write our userM attributes in the fileheader, even for a very fragmented file. We'll extendp: the file to the real size after we wrote the attributes.*/5 setup_fab(s_file, &zero, lbn); /* Init for RMS *//3 stat = sys$parse(&ld_fab); /* Parse filename */U if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);=: if (ld_nam.nam$v_node) /* May not contain node spec */3 lib$signal(&ld_createrr, 1, s_file, RMS$_SUPPORT); 0 if (ld_nam.nam$v_wildcard) /* or wildcard *// lib$signal(&ld_createrr, 1, s_file, RMS$_WLD); 5 stat = sys$create(&ld_fab); /* Create the file */l if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);t@ setfilechar(); /* Set NOMOVE and NOBACKUP characteristic */9 memset(&mdat, 0, sizeof(mdat)); /* Start with zero */p mdat.sectors = sectors; mdat.tracks = tracks;a mdat.cylinders = cylinders;= mdat.maxblock = maxblock;/8 setfilemetadata(&mdat); /* Set container metadata */7 setup_fab(s_file, &q_size, lbn); /* Init for RMS */ > stat = sys$extend(&ld_fab); /* Go for the real size now */ if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);e1 stat = sys$connect(&ld_rab);/* Hook up RAB */F if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);)0 ld_rab.rab$l_rbf = dummy; /* Dummy buffer */A stat = sys$put(&ld_rab); /* Put dummy block at end of file */n2 /* to get EOF pointer at right place */ if (!(stat & 1))J lib$signal(&ld_filwrterr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);/ stat = sys$close(&ld_fab); /* Close file */e if (!(stat & 1))H lib$signal(&ld_closerr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);, if (getqual(&sw_log) > 0) { /* /LOG ? */$ open_file(s_file); /* Open again */4 lib$signal(&ld_created, 1, realspec); /* Show it */ close_file(s_file); }}(/* Init FAB for create */"void setup_fab(filname, size, lbn)char *filname; int *size;int lbn;{ char contiguous;A contiguous = getqual(&sw_contiguous) > 0; /* /CONTIGUOUS ? */6 ld_fab.fab$l_dna = ".DSK"; /* Default extension */* ld_fab.fab$b_dns = 4; /* and length */7 ld_fab.fab$l_fop = FAB$M_NAM | FAB$M_OFP; /* NAM */b0 if (getqual(&sw_lbn) > 0) { /* /LBN=xxx ? */& ld_fab.fab$l_xab = &ld_xaball;( ld_xaball.xab$b_aln = XAB$C_LBN;( ld_xaball.xab$b_aop = XAB$M_HRD;" ld_xaball.xab$l_loc = lbn;3 ld_xaball.xab$l_alq = *size; /* Filesize */i if (contiguous)q- ld_xaball.xab$b_aop |= XAB$M_CTG; } else {0 ld_fab.fab$l_alq = *size; /* Filesize */ if (contiguous)s* ld_fab.fab$l_fop |= FAB$M_CTG; }e2 ld_fab.fab$b_org = FAB$C_SEQ; /* Sequential */- ld_fab.fab$b_rfm = FAB$C_FIX; /* Fixed */n2 ld_fab.fab$w_mrs = 512; /* 512 byte records *// ld_fab.fab$l_nam = &ld_nam; /* NAM block */ . ld_fab.fab$l_fna = filname; /* Filename */8 ld_fab.fab$b_fns = strlen(filname); /* and length */2 ld_rab.rab$l_fab = &ld_fab; /* Point to FAB */4 ld_rab.rab$b_rac = RAB$C_KEY; /* Keyed access */C ld_rab.rab$l_kbf = size; /* Pointer to record (last in file) */;- ld_rab.rab$w_rsz = 512; /* Record size */r> ld_nam.nam$b_rss = NAM$C_MAXRSS; /* Max. resultant size */7 ld_nam.nam$l_rsa = resspec; /* Resultant address */ = ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */u6 ld_nam.nam$l_esa = expspec; /* Expanded address */}pvoid setfilechar(){l int stat;o short chan;a struct iosb iosb;  int attrib;o. struct atr_caching_options_flags filechar;/*B Use QIO to modify the attributes since RMS won't support NOMOVE until VMS V6.0l*/#ifdef __DECC_MODE_VAXCi= fib.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */ + fib.fib$w_fid[1] = ld_nam.nam$w_fid[1];f+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];t#elsewO fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */s= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];== fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];s#endifB realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev; S stat = sys$assign(&realdev_dsc, &chan, 0, 0, 0); /* Assign channel to device */(3 signal_error(stat, 0); /* where file resides */t$ atr[0].atr$w_size = ATR$S_UCHAR;H atr[0].atr$w_type = ATR$C_UCHAR; /* Get user file characteristics */ atr[0].atr$l_addr = &attrib;. atr[1].atr$w_size = ATR$S_CACHING_OPTIONS;M atr[1].atr$w_type = ATR$C_CACHING_OPTIONS; /* Get file caching options */" atr[1].atr$l_addr = &filechar;+ atr[2].atr$w_size = 0; /* Terminator */i atr[2].atr$w_type = 0;+ fib_dsc.addr = &fib; /* Point to FIB */  fib_dsc.len = FIB$K_LENGTH; 5 stat = sys$qiow(0, chan, IO$_ACCESS, &iosb, 0, 0,n8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);. attrib |= FCH$M_NOMOVE; /* Mark no move */ if (getqual(&sw_backup) < 0)/ attrib |= FCH$M_NOBACKUP; /* Mark no backup */(5 filechar.atr$v_file_attributes = ATR$C_NOCACHING;l3 filechar.atr$v_file_contents = ATR$C_NOCACHING; 0 filechar.atr$v_flush_on_close = ATR$C_FLUSH;+ filechar.atr$v_caching_options_mbz = 0;(5 stat = sys$qiow(0, chan, IO$_MODIFY, &iosb, 0, 0,m8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);5 stat = sys$dassgn(chan); /* Get rid of channel */t signal_error(stat, 0);} 6int getclonedata(tracks, sectors, cylinders, maxblock)-int *tracks, *sectors, *cylinders, *maxblock;t{t int stat;  int devchar; int devclass;( int tracks1; int sectors1;n int cylinders1;  int maxblock1; int clone; char *device;o struct dsc namdsc; struct iosb iosb;s struct itmlst dvi_item[] =" {4, DVI$_DEVCHAR, &devchar, 0,$ 4, DVI$_DEVCLASS, &devclass, 0, 4, DVI$_TRACKS, tracks, 0, ! 4, DVI$_SECTORS, sectors, 0,e% 4, DVI$_CYLINDERS, cylinders, 0,c# 4, DVI$_MAXBLOCK, maxblock, 0,n 0, 0, 0, 0};r clone = getqual(&sw_clone);* if (clone) {I device = getqualstring(&sw_clone); /* Get devicename for clone */= namdsc.addr = device;e$ namdsc.len = strlen(device);] stat = sys$getdviw(0, 0, &namdsc, &dvi_item, &iosb, 0, 0, 0); /* Get device status */l( signal_error(stat, iosb.status);! if (devclass != DC$_DISK) < lib$signal(&ld_notadisk, 0); /* Clone must be a disk */# if (!(devchar & DEV$M_MNT));D lib$signal(&ld_clonenotmounted, 0); /* Clone must be mounted */+ tracks1 = getqualvalue(&sw_tracks);v- sectors1 = getqualvalue(&sw_sectors); 1 cylinders1 = getqualvalue(&sw_cylinders);m0 maxblock1 = getqualvalue(&sw_maxblocks);#/* Override Cloned device values */o if (tracks1 != 0)= *tracks = tracks1; if (sectors1 != 0) *sectors = sectors1; if (cylinders1 != 0)$ *cylinders = cylinders1; if (maxblock1 != 0) " *maxblock = maxblock1; }a return clone;} void setfilemetadata(mdat)struct metadata *mdat;{, int stat;o int devchar; short chan;n struct dsc namdsc; struct iosb iosb;d#ifdef __DECC_MODE_VAXC = fib.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */s+   $LD071.B[VDBURG.LD.V71.SRC]LD.C;1q"fib.fib$w_fid[1] = ld_nam.nam$w_fid[1];_+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];0#else O fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */O= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];(= fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];#endifB realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev;uS stat = sys$assign(&realdev_dsc, &chan, 0, 0, 0); /* Assign channel to device */n3 signal_error(stat, 0); /* where file resides */ % mdat->version = METADATA_VERSION;=, mdat->rsvdlen = sizeof(struct metadata);0 atr[0].atr$w_size = sizeof(struct metadata);? atr[0].atr$w_type = ATR$C_RESERVED; /* Set reserved area */l atr[0].atr$l_addr = mdat;t+ atr[1].atr$w_size = 0; /* Terminator */c atr[1].atr$w_type = 0;+ fib_dsc.addr = &fib; /* Point to FIB */} fib_dsc.len = FIB$K_LENGTH; 5 stat = sys$qiow(0, chan, IO$_ACCESS, &iosb, 0, 0,s5 &fib_dsc, 0, 0, 0, 0, 0); /* Access the file */r$ signal_error(stat, iosb.status);5 stat = sys$qiow(0, chan, IO$_MODIFY, &iosb, 0, 0,&: &fib_dsc, 0, 0, 0, &atr, 0); /* Modify the file */$ signal_error(stat, iosb.status);5 stat = sys$dassgn(chan); /* Get rid of channel */  signal_error(stat, 0);}lint getfilemetadata(mdat)rstruct metadata *mdat;{ int stat;) int devchar; short chan;  struct dsc namdsc; struct iosb iosb;-#ifdef __DECC_MODE_VAXC = fib.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */+ fib.fib$w_fid[1] = ld_nam.nam$w_fid[1];t+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];d#else O fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */r= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];,= fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];c#endifB realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev;S stat = sys$assign(&realdev_dsc, &chan, 0, 0, 0); /* Assign channel to device */;3 signal_error(stat, 0); /* where file resides */i0 atr[0].atr$w_size = sizeof(struct metadata);? atr[0].atr$w_type = ATR$C_RESERVED; /* Set reserved area */  atr[0].atr$l_addr = mdat;u+ atr[1].atr$w_size = 0; /* Terminator */b atr[1].atr$w_type = 0;+ fib_dsc.addr = &fib; /* Point to FIB */* fib_dsc.len = FIB$K_LENGTH;t5 stat = sys$qiow(0, chan, IO$_ACCESS, &iosb, 0, 0,8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);5 stat = sys$dassgn(chan); /* Get rid of channel */x signal_error(stat, 0);* if (mdat->version == METADATA_VERSION) return mdat->rsvdlen; else return 0;}i/*& Get full filename from specified one*/!char *fullfilespec(name, defname) char *name, *defname;"{D% static char tmpnam[NAM$C_MAXRSS];, int stat;( int len; if (name)  len = strlen(name); else len = 0;f: ld_nam.nam$b_nop = NAM$M_NOCONCEAL; /* Name options */= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */15 ld_nam.nam$l_esa = tmpnam; /* Expanded address */S/ ld_fab.fab$l_nam = &ld_nam; /* NAM block */ : ld_fab.fab$l_dna = defname; /* Default name address */? ld_fab.fab$b_dns = strlen(defname); /* Default name size */)1 ld_fab.fab$b_fns = len; /* Filename length */d3 ld_fab.fab$l_fna = name; /* Filename address */c4 stat = sys$parse(&ld_fab); /* Parse this name */ if (!(stat & 1))D lib$signal(&ld_detectederr, 0, ld_fab.fab$l_sts, ld_fab.fab$l_stv);9 tmpnam[ld_nam.nam$b_esl] = '\0'; /* Add terminator */ return (tmpnam);} /*) Get full device name from specified one */char *fulldevspec(name)r char *name;n{/ char *tmpnam;e struct dsc resdsc;- struct dsc namdsc = {strlen(name), name};e short rlen; int stat;(8 tmpnam = malloc(64); /* Get memory for devicename */ if (tmpnam == 0)6 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ resdsc.addr = tmpnam;/ resdsc.len = 64;[ stat = lib$getdvi(&DVI$_FULLDEVNAM, 0, &namdsc, 0, &resdsc, &rlen); /* Get full name */m signal_error(stat, 0);) tmpnam[rlen] = '\0'; /* Terminator */e/ return (tmpnam + 1); /* Skip leading '_' */0}/* Get full filespec from file-id*/'void create_filename(device,fid,outbuf) char *device;struct fiddef *fid; char *outbuf;s{p struct dsc outdsc; struct dsc devdsc; int stat;* short rlen;h char tmpbuf[256];n char *p;7 devdsc.len = strlen(device); /* Input devicename */3 devdsc.addr = device;;; outdsc.len = sizeof(tmpbuf); /* Output buffer length */u outdsc.addr = tmpbuf; ? stat = lib$fid_to_name(&devdsc, fid, &outdsc, &rlen, 0, 0);  signal_error(stat, 0); tmpbuf[rlen]='\0';@/* Modify the devicename (as returned from lib$fid_to_name) from" DISK$LABEL: to _SYSTEM$BLA0: */A strcpy(outbuf,fulldevspec(device)); /* Get real devicename */n p = strchr(tmpbuf,':') + 1;A strcat(outbuf,p);n}(/* Get nodename*/char *nodename(){s static char tmpnam[256]; static struct dsc resdsc = {sizeof(tmpnam), tmpnam};( struct dsc tbl = {12, "LNM$FILE_DEV"};t struct dsc lognam =i {8, "SYS$NODE"}; short rlen;c struct itmlst item[2]; int stat;  tmpnam[0] = '\0';4R stat = lib$getsyi(&SYI$_NODENAME, 0, &resdsc, &rlen, 0, 0); /* Get nodename */) if (stat & 1) { /* Nodename found */b& tmpnam[rlen] = '\0'; /* Terminator */ strcat(tmpnam, "::");: } else { /* No nodename, try the logical SYS$NODE */ item[0].item = LNM$_STRING; item[0].buflen = 256; item[0].addr = tmpnam;k item[0].rlength = &rlen;i( item[1].item = 0; /* End of itemlist */ item[1].buflen = 0;/ stat = sys$trnlnm(0, &tbl, &lognam, 0, &item);t signal_error(stat, 0);n& tmpnam[rlen] = '\0'; /* Terminator */ }= return (tmpnam);}f/* Build lock for trace*/char *build_lock(dev) char *dev;{ static char locknam[256]; > strcpy(locknam, "$LOGDISK_"); /* First part of lockname */5 strcat(locknam, nodename());/* Attach nodename */o@ locknam[strlen(locknam) - 2] = '\0'; /* Zap trailing "::" */> strcat(locknam, fulldevspec(dev)); /* Attach devicename */ return locknam;z}fvoid set_outband(){ int stat;i int type;d int mask[2]; struct iosb iosb;( $DESCRIPTOR(outbdev, "SYS$COMMAND");; stat = lib$getdvi(&DVI$_TRM, 0, &outbdev, &type, 0, 0);S signal_error(stat, 0);3 if (!type) /* Not a terminal, don't bother */u return;M stat = sys$assign(&outbdev, &outband_chan, 0, 0, 0); /* assign channel */  signal_error(stat, 0); mask[0] = 0;4 mask[1] = (1 << ('C' - 64)) | (1 << ('Z' - 64));M stat = sys$qiow(0, outband_chan, IO$_SETMODE | IO$M_OUTBAND, &iosb, 0, 0,o< &ld_exit, &mask, 0, 0, 0, 0); /* Get current status */$ signal_error(stat, iosb.status);} void purge_file(limit, name) int limit; char *name;e{o int stat;v char *pos; char tmpnam[256];o struct dsc fildsc; $DESCRIPTOR(deflt, ".DAT"); 3 pos = strrchr(name, ';'); /* Find trailing ; */  if (pos != 0) {d# if (name[strlen(name) - 1] != ';')z, return; /* Exact filespec speficied */ else&& *pos = '\0'; /* Zap trailing ; */ }* if (limit == 0)eD sprintf(tmpnam, "%s;%d", name, limit); /* Delete lastest version */ elseA sprintf(tmpnam, "%s;-%d", name, limit); /* Delete old version */f fildsc.addr = tmpnam; fildsc.len = strlen(tmpnam); stat = 1; / while (stat == 1) /* until out of files */)> stat = lib$delete_file(&fildsc, &deflt, 0, 0, 0, 0, 0, 0, 0);8 if (stat != RMS$_FNF) /* No such file is no error */ signal_error(stat, 0);u}bvoid ld_exit(param)f int param;{v int stat;rH if ((outfile != 0) && (outfile != stdout)) { /* Close output file */ if (fclose(outfile) < 0)e< lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); }x, stat = sys$deq(ld_lksb.lockid, 0, 0, 0); signal_error(stat, 0); if (outband_chan != 0) {: stat = sys$dassgn(outband_chan); /* Get rid of channel */ signal_error(stat, 0);) } ; if (param == 0) { /* Called by blocking ast routine */u lib$signal(&ld_tracestopped); exit(&ld_tracestopped); } else exit(1);N}o#void signal_error(st1, st2, reason)tint st1, st2, reason;t{u int stat1, stat2; int modified1, modified2;oI modified1 = convert_error(st ʗ$LD071.B[VDBURG.LD.V71.SRC]LD.C;1q5>1, &stat1); /* Return reasonable error */s+ modified2 = convert_error(st2, &stat2);T4 if ((!(stat1 & 1)) || (st2 && (!(stat2 & 1)))) { if (modified1 || modified2) { if (stat1 == 1) { if (reason == 0) lib$signal(stat2, 0, 0); else# lib$signal(stat2, 0, reason);  } else$ lib$signal(stat1, 0, stat2, 0, 0); } else {_ if (stat1 == 1). lib$signal(&ld_detectederr, 0, stat2, 0, 0); else 8 lib$signal(&ld_detectederr, 0, stat1, 0, stat2, 0, 0); } },}H/*; Return a reasonable error for some returned by the driveri*/'int convert_error(oldstatus, newstatus)dint oldstatus;int *newstatus;O{, int code = 0;d# if (oldstatus == SS$_DEVASSIGN)r; code = &ld_devassign; /* 'Device has channels assigned' */b( else if (oldstatus == SS$_DEVACTIVE)0 code = &ld_alrdyconn; /* 'Already connected' */' else if (oldstatus == SS$_DEVINACT)o* code = &ld_notconn; /* 'Not connected' */% else if (oldstatus == SS$_NODATA)A7 code = &ld_trcdisabl; /* 'Tracing already disabled' */_* else if (oldstatus == SS$_TOOMUCHDATA)5 code = &ld_trcenabl; /* 'Tracing already enabled' */m' else if (oldstatus == SS$_DATALOST);= code = &ld_nowatchdata; /* 'No watchpoint data available' */ ( else if (oldstatus == SS$_DATACHECK)5 code = &ld_wptnotfound; /* 'Watchpoint not found' */C( else if (oldstatus == SS$_NOTVOLSET)I code = &ld_fileonvolset;/* 'File watchpoint on volumeset not allowed' */E( else if (oldstatus == SS$_FILALRACC)2 code = &ld_fileinuse; /* 'File already in use' */' else if (oldstatus == SS$_DEVALLOC) 6 code = &ld_deviceinuse; /* 'Device already in use' */( else if (oldstatus == SS$_DEVREQERR)G code = &ld_fileonother; /* 'File not allowed to be on other device' */U( else if (oldstatus == SS$_ILLBLKNUM)D code = &ld_vbnerror; /* 'Illegal virtual block number specified' */* else if (oldstatus == SS$_UNSUPPORTED)5 code = &ld_nocluster; /* 'No cluster code loaded' */_) else if (oldstatus == SS$_ACCONFLICT)OD code = &ld_notvisible; /* 'Device is not visible on other nodes' */' else if (oldstatus == SS$_BADPARAM)C; code = &ld_invgeometry; /* 'Invalid geometry specified' */s% else if (oldstatus == SS$_UNSAFE)TS code = &ld_devconnected;/* 'Cannot set allocation class with active LD devices' */d' else if (oldstatus == SS$_WRONGACP)9 code = &ld_unsupportedfs; /* 'Unsupported filesystem' */r else {5 *newstatus = oldstatus; /* Return original status */m return 0; /* Not modified */ } / *newstatus = code; /* Return new status */f return 1; /* Modified */}o/* Provide interactive help */ void help()t{g char buf[256]; char *p; int status;e int flag; : $DESCRIPTOR(LD_HELPLIB,"LD$HELP"); /* Help library */ $DESCRIPTOR(input, buf);! typedef int (*funcArg)(void);h3 strcpy(buf,"LD "); /* Start with LD topic */ p = getqualstring(&sw_help); if (p)7 strcat(buf, p); /* Append rest of command */i8 input.dsc$w_length = strlen(buf); /* Insert size */ /* Activate help library *//c status = lib$find_image_symbol(&LBRSHR_DESC, &LBR$OUTPUT_HELP_DESC, (int *) &output_help_addr);r' if (!$VMS_STATUS_SUCCESS(status)) { exit(status); }nJ flag = HLP$M_PROMPT|HLP$M_PROCESS|HLP$M_GROUP|HLP$M_SYSTEM|HLP$M_HELP; /* Call the help routines */] exit(lbr$output_help((funcArg) lib$put_output,0,&input,&LD_HELPLIB,&flag,lib$get_input));a}t3int lbr$output_help ( int (*output_routine) (void),r, int *output_width,= struct dsc$descriptor_s *line_desc, @ struct dsc$descriptor_s *library_name,% int *flags, / void *input_routine )n{ m return (*output_help_addr) (output_routine, output_width, line_desc, library_name, flags, input_routine);b}d*[VDBURG.LD.V71.SRC]LD.H;1+,./@@ 4El-0123KPWO5 6}7zVB-89G@@HJ/* Facility:$ VAX/VMS Logical Disk Utility Abstract:' Include file for LD management utility Author: Jur van der Burg! vdburg@utrtsc.uto.dec.com*/#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ld_defines.h"#ifndef SS$_NOMOREDEV6#define SS$_NOMOREDEV 2648 /* Not in my headers ... */#endif#ifndef FCH$M_NOMOVE7#define FCH$M_NOMOVE 0x200000 /* May not in be there */#endif#ifndef FCH$M_NOBACKUP5#define FCH$M_NOBACKUP 0x2 /* May not in be there */#endif#ifndef IO$M_TRUSTED7#define IO$M_TRUSTED 0x80000 /* May not in be there */#endif#ifndef IO$M_NOVCACHE8#define IO$M_NOVCACHE 0x20000 /* May not in be there */#endif#ifndef IO$M_EXFUNC5#define IO$M_EXFUNC 0x2000 /* May not in be there */#endif#define DATA_FILE_VERSION 0x05#define METADATA_VERSION 0x01/*( Structure definitions and declarations*/#pragma nomember_alignment struct dsc { int len; char *addr; };struct itmlst { short buflen; short item; int addr; int rlength; }; struct iosb { short status; short size; union { int lw; struct {- unsigned int connected:1,, replaced:1,* decram:1,+ protect:1,) share:1,, onvolset:1,) nodse:1,+ virtual:1,, accurate:1,, fdttrace:1; } flags; } u; };#define iosb_lw iosb.u.lw#define iosb_flags iosb.u.flagsstruct ovrrun { int recnum; int overrun; };struct cmndtbl { char *what; int (*where)(); }; struct lksb { short status; short reserved; int lockid; union { int valblk[4]; struct {- unsigned short cylinders;) unsigned char tracks;* unsigned char sectors;* unsigned int maxblock;) unsigned int allocls;( unsigned short unit; struct {. unsigned char share:1; } flags; } ldvalblk; } u; };struct metadata { short rsvdlen; short version; int tracks; int sectors; int cylinders; int maxblock; struct { unsigned int protected:1; } flags; };struct FAB ld_fab;struct RAB ld_rab;struct NAM ld_nam;struct XABFHC ld_xabfhc;struct XABALL ld_xaball;struct XABITM ld_xabitm;struct atrdef atr[3];struct sbkdef sbk;struct fibdef fib;/* Global variables*/int maxblocks; short fchan;short outband_chan = 0;&char *s_file, *s_device, *outfilename;struct lksb ld_lksb;$DESCRIPTOR(wildname,"*LD*");FILE *outfile = -1;int outfilei = -1;char realspec[NAM$C_MAXRSS];char resspec[NAM$C_MAXRSS];char expspec[NAM$C_MAXRSS];struct dsc realspec_dsc;struct dsc rea R$LD071.B[VDBURG.LD.V71.SRC]LD.H;1E  ldev_dsc;struct dsc ldfilename_dsc;struct dsc fib_dsc;/*( Global descriptors for command parsing*/$DESCRIPTOR(command,"COMMAND");$DESCRIPTOR(sw_file,"FILE"); $DESCRIPTOR(sw_device,"DEVICE");$DESCRIPTOR(sw_help,"HELP"); $DESCRIPTOR(sw_lblock,"LBLOCK");$DESCRIPTOR(sw_trace,"TRACE");$DESCRIPTOR(sw_watch,"WATCH"); $DESCRIPTOR(sw_resume,"RESUME");$DESCRIPTOR(sw_abort,"ABORT");$DESCRIPTOR(sw_stop,"STOP");$DESCRIPTOR(sw_size,"SIZE"); $DESCRIPTOR(sw_tracks,"TRACKS");"$DESCRIPTOR(sw_sectors,"SECTORS");&$DESCRIPTOR(sw_cylinders,"CYLINDERS");&$DESCRIPTOR(sw_maxblocks,"MAXBLOCKS");$DESCRIPTOR(sw_index,"INDEX");$DESCRIPTOR(sw_share,"SHARE");$DESCRIPTOR(sw_clone,"CLONE");,$DESCRIPTOR(sw_autogeometry,"AUTOGEOMETRY");$DESCRIPTOR(sw_all,"ALL");&$DESCRIPTOR(sw_alloclass,"ALLOCLASS");.$DESCRIPTOR(sw_version_limit,"VERSION_LIMIT"); $DESCRIPTOR(sw_blocks,"BLOCKS"); $DESCRIPTOR(sw_backup,"BACKUP");$DESCRIPTOR(sw_log,"LOG");$DESCRIPTOR(sw_fdt,"FDT");$$DESCRIPTOR(sw_accurate,"ACCURATE");($DESCRIPTOR(sw_contiguous,"CONTIGUOUS");($DESCRIPTOR(sw_continuous,"CONTINUOUS");$DESCRIPTOR(sw_reset,"RESET");"$DESCRIPTOR(sw_replace,"REPLACE"); $DESCRIPTOR(sw_status,"STATUS"); $DESCRIPTOR(sw_header,"HEADER");$$DESCRIPTOR(sw_warnings,"WARNINGS"); $DESCRIPTOR(sw_binary,"BINARY"); $DESCRIPTOR(sw_symbol,"SYMBOL");"$DESCRIPTOR(sw_entries,"ENTRIES");$DESCRIPTOR(sw_pid,"PID");$DESCRIPTOR(sw_lbn,"LBN");&$DESCRIPTOR(sw_bytecount,"BYTECOUNT");$DESCRIPTOR(sw_iosb,"IOSB");$$DESCRIPTOR(sw_function,"FUNCTION");&$DESCRIPTOR(sw_timestamp,"TIMESTAMP"); $DESCRIPTOR(sw_number,"NUMBER");$DESCRIPTOR(sw_input,"INPUT"); $DESCRIPTOR(sw_output,"OUTPUT");&$DESCRIPTOR(sw_permanent,"PERMANENT");$DESCRIPTOR(sw_save,"SAVE");3$DESCRIPTOR(sw_time_absolute,"TIMESTAMP.ABSOLUTE");-$DESCRIPTOR(sw_time_delta,"TIMESTAMP.DELTA");1$DESCRIPTOR(sw_time_elapsed,"TIMESTAMP.ELAPSED");9$DESCRIPTOR(sw_time_combination,"TIMESTAMP.COMBINATION");$$DESCRIPTOR(sw_iosb_hex,"IOSB.HEX");,$DESCRIPTOR(sw_iosb_longhex,"IOSB.LONGHEX");&$DESCRIPTOR(sw_iosb_text,"IOSB.TEXT");4$DESCRIPTOR(sw_iosb_combination,"IOSB.COMBINATION");,$DESCRIPTOR(sw_function_hex,"FUNCTION.HEX");.$DESCRIPTOR(sw_function_text,"FUNCTION.TEXT");.$DESCRIPTOR(sw_function_read,"FUNCTION.READ");0$DESCRIPTOR(sw_function_write,"FUNCTION.WRITE");.$DESCRIPTOR(sw_function_code,"FUNCTION.CODE");,$DESCRIPTOR(sw_function_all,"FUNCTION.ALL"); $DESCRIPTOR(sw_action,"ACTION");0$DESCRIPTOR(sw_action_suspend,"ACTION.SUSPEND");,$DESCRIPTOR(sw_action_crash,"ACTION.CRASH");,$DESCRIPTOR(sw_action_error,"ACTION.ERROR");,$DESCRIPTOR(sw_action_opcom,"ACTION.OPCOM");/* Routine declarations*/int getqual();int getqualvalue();int getmulqualvalue();char *getqualstring();int *getlist(); void show();int show_one();void connect_unit();void disconnect_unit();int disconnect_one();void open_file();void close_file();void create(); void trace();void stop_trace();void stop_one();void notrace(); void watch();void show_watch();void display_watch();void nowatch();void protect();void noprotect();void protect_common();void process_trace();void show_trace_data();void show_trace();void process_input();void setfilechar();void setfilemetadata();int getfilemetadata();int getclonedata();void set_outband();void ld_exit();char *ssdef();char *decode_func();char *cvttime();char *nodename();char *build_lock();void purge_file();void chk_mod();int convert_error();void create_filename();void setup_fab();char *fulldevspec();char *fullfilespec();void signal_error();int findshareddevice(); void help();void getdevnam();int get_unit();void set_seed();void flush_xfc();/* VMS Routines*/int cli$present();int cli$get_value();/* HELP routines*/#$DESCRIPTOR(LBRSHR_DESC, "LBRSHR");5$DESCRIPTOR(LBR$OUTPUT_HELP_DESC, "LBR$OUTPUT_HELP");int (*output_help_addr)();int lbr$output_help();/* Other routines*/char *malloc();struct cmndtbl cmnds[] = { "CREA",&create, "CONN",&connect_unit,! "DISC",&disconnect_unit, "SHOW",&show, "TRAC",&trace, "NOTR",¬race, "WATC",&watch, "NOWA",&nowatch, "PROT",&protect, "NOPR",&noprotect, "HELP",&help, "",0 };/*2 External condition codes defined in message file*/globalref int ld_connected, ld_nowdisconn, ld_notconnected, ld_alrdyconn, ld_devassign, ld_notconn, ld_fileonvolset, ld_fileinuse, ld_deviceinuse, ld_created, ld_filwrterr, ld_filreaderr, ld_createrr, ld_openerr, ld_closerr, ld_infilerr, ld_outfilerr, ld_notrcdata, ld_noworldpriv, ld_nogrouppriv, ld_conttraceact,! ld_conttracenotact, ld_tracestopped, ld_trcenabl, ld_trcdisabl, ld_status, ld_fdtactive, ld_accurate, ld_pastdata, ld_badentparam, ld_confqual, ld_cantreadoldfmt, ld_unit, ld_dupunit, ld_badunit, ld_nounitsfound, ld_baddevsyntax, ld_detectederr, ld_nowatchdata, ld_wptnotfound, ld_noreadwrite, ld_vbnerror, ld_fileonother, ld_nocluster, ld_notvisible, ld_invgeometry, ld_badalloclass, ld_devconnected, ld_remotealloc, ld_unsupportedfs,! ld_clonenotmounted, ld_notshared, ld_noshare, ld_alloclass, ld_unitnumber, ld_tracks, ld_sectors, ld_cylinders, ld_maxblock, ld_notadisk;/*E Conversiontable to convert I/O functioncodes to human readable text*/char *cvttbl[] = { "NOP", "UNLOAD", "SEEK", "RECAL", "DRVCLR", "RELEASE", "OFFSET", "RETCENTER", "PACKACK", "READRCT", "WRITECHECK", "WRITEPBLK", "READPBLK", "CRESHAD", "ADDSHAD", "COPYSHAD", "REMSHAD", "AVAILABLE", "SETPRFPATH", "DISPLAY", "FUNC_20", "DSE", "REREADN", "REREADP", "WRITERET", "STARTSPNDL", "SETCHAR", "SENSECHAR", "WRITEMARK", "SHADMV", "FORMAT", "PHYSICAL", "WRITELBLK", "READLBLK", "READRCTL", "SETMODE", "REWIND", "SKIPFILE", "SKIPRECORD", "SENSEMODE", "WRITEOF", "TTY_PORT", "FLUSH", "READLCHUNK", "WRITELCHUNK", "FUNC_45", "FUNC_46", "LOGICAL", "WRITEVBLK", "READVBLK", "ACCESS", "CREATE", "DEACCESS", "DELETE", "MODIFY", "READPROMPT", "ACPCONTROL", "MOUNT", "TTYREADALL", "TTYREADPALL", "CONINTREAD", "CONINTWRITE", "FUNC_62", "VIRTUAL" };*[VDBURG.LD.V71.SRC]LD.HLP;1+,./@@ 4-0123KPWO5 6;@9+-7*ZB-89G@@HJ*,$LD071.BDBURG.LD.V71.SRC]LD.HLP;17"1 LD? The logical disk utility is a system management tool available0 to any user for controlling logical disk usage.= A Logical Disk is a file available on a Physical Disk, which7 acts as a real Physical Disk (The file is contiguous.)8 The Logical Disks are available in any directory of the Physical Disk.@ A large disk can be divided into smaller sections, each a Logi-B cal Disk, supporting the same I/O functions as the Physical Disk.= By giving the Logical Disk File a good protection level and? mounting it private or with device protection, you are able to7 add a number of protection levels to your file system.< The logical disk is controlled by the LD utility, which can be directly invoked from DCL. Format:% LD [/qualifiers] [Filespec] [Device]2 Author: The author of this piece of software is Jur van der Burg,* HP OpenVMS engineering, Utrecht, Holland.9 In case of problems/questions/suggestions please contact% the author at jur.vanderburg@hp.com.2 Command_summary@ LD CREATE [/LOG] [/SIZE=xxx] [/BACKUP] [/CONTIGUOUS] [/LBN=xxx]/ [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]+ [/MAXBLOCKS=xxx] [/CLONE=device] Filespec@ LD CONNECT [/LOG] [/SYMBOL] [/REPLACE] [/SHARE] [/CLONE=device]/ [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]3 [/MAXBLOCKS=xxx] [/ALLOCLASS=xxx] [/AUTOGEOMETRY] [/SAVE] Filespec [LDan:]+ LD DISCONNECT [/ALL] [/LOG] [/ABORT] LDan:7 LD TRACE [/ACCURATE] [/FDT] [/SIZE=xxx] [/RESET] LDan: LD TRACE/STOP [/ALL] [LDan:] LD NOTRACE LDan:A LD WATCH LDan: lbn [,lbn...] [/FUNCTION=READ,WRITE,ALL,CODE=xxx]+ [/ACTION=SUSPEND,CRASH,OPCOM,ERROR[=xxx]] [/FILE=filespec], LD NOWATCH LDan: [lbn [,lbn...]] [/INDEX=n]1 LD WATCH/RESUME LDan: [lbn [,lbn...]] [/INDEX=n] LD PROTECT [/PERMANENT] LDan: LD NOPROTECT [/PERMANENT] LDan: LD SHOW [/ALL] [LDan:]$ LD SHOW/WATCH LDan: [lbn [,lbn...]]F LD SHOW/TRACE [/STATUS] [/RESET] [/OUTPUT=Filespec] [/INPUT=filespec]: [/BINARY] [/ENTRIES=[(XXX,YYY)]] [/HEADER] [/CONTINUOUS]0 [/VERSION_LIMIT=xxx] [/BLOCKS=xxx] [/WARNINGS]& [/NUMBER] [/PID] [/LBN] [/BYTECOUNT]( [/IOSB[=COMBINATION,TEXT,HEX,LONGHEX]]3 [/TIMESTAMP[=ABSOLUTE,ELAPSED,COMBINATION,DELTA]]1 [/FUNCTION[=TEXT,HEX]] [/ACCURATE] [/FDT] LDan: LD HELP [Topic]2 Driver_functions> The LDDRIVER understands all functioncodes normally available: for disks. Some special functions are used to control the logical disk driver.9 All functions assume that a channel has been assigned to? the device on which to operate. One exception is the 'Connect'? function which needs a channel to LDA0:. This will then create1 a new device (because LDA0: is a cloned device).= A file with LDdriver specific definitions is included in the kit (LD_DEFINES.H).: The only function to control the driver is IO$_LD_CONTROL" which is defined in LD_DEFINES.H.7 A sub-function must be given as P6 of the QIO request. 3 Connect1 This function connects a file to a logical disk: Function: IO$_LD_CONTROLA P1 = address of SBK block (Attributes Statistics Block) returnedB by XQP IO$_ACCESS!IO$M_ACCESS function used to open the file to connect.> P2 = logical disk size. If 0, the allocated size will be used! P3 = Number of tracks (optional)" P4 = Number of sectors (optional)$ P5 = Number of cylinders (optional) P6 = Subfunction: LDIO_CONNECTC This function connects a complete physical disk to a logical disk: Function: IO$_LD_CONTROL8 P1 = descriptor of buffer which contains device name of physical device0 P6 = Subfunction: LDIO_CONNECT | LDIO_M_REPLACE IOSB word 0 = status Errors returned:' SS$_DEVACTIVE Device already connected> SS$_FORMAT FCB specified in SBK is invalid or file not opened8 SS$_ACCVIO One of the parameters cannot be read/written@ SS$_IVDEVNAM Attempt to connect to devicetype other than a disk0 SS$_FILNOTCNTG Logical disk file not contiguous4 SS$_DATAOVERUN Filename of file to connect too longB SS$_FILALRACC File already in use (may be on other cluster nodes)9 SS$_ILLKLKNUM Disk size specified in P2 past end of file+ SS$_IVADDR Disk bigger than 2097151 blocks! SS$_NORMAL Successful completion4 SS$_BADPARAM Invalid geometry information specifiedB SS$_UNSUPPORTED Shared access was specified while no cluster code is loaded? SS$_ACCONFLICT Shared access was specified while the device is not cluster-wide visible: SS$_WRONGACP Container file is not on ODS-2 or ODS-5 disk; The following errors are returned for LDIO_M_REPLACE only:& SS$_DEVFOREIGN Device foreign mounted SS$_DEVMOUNT Device mounted& SS$_DEVALLOC Device already allocated= Other possible errors are errors returned by the lockmanager# and errors returned by IOC$SEARCH. 3 Disconnect@ This function disconnects a file or device from a logical disk: Function: IO$_LD_CONTROL" P6 = Subfunction: LDIO_DISCONNECT IOSB word 0 = statusH The subfunctionmodifier LDIO_M_ABORT allows the file to be disconnected regardless of it's state. Errors returned:& SS$_DEVFOREIGN Device mounted foreign SS$_DEVMOUNT Device mounted% SS$_BADFILEHDR Bad logical disk file8 SS$_DEVASSIGN Device still active (reference count > 0)" SS$_DEVINACT Device not connected+ SS$_FORMAT FCB specified in SBK is invalid) SS$_ACCVIO SBK block cannot be read! SS$_NORMAL Successful completion> Other possible errors are errors returned by the lockmanager.3 Display_status= This function retrieves the current status of a LDAn device: Function: IO$_LD_CONTROL> P1 = address of buffer which receives the device- or filename currently connected; P2 = size of buffer which receives the device- or filename@ P3 = address of 3 word buffer which receives the file-id of the connected file& P6 = Subfunction: LDIO_GET_CONNECTION IOSB word 0 = status/ word 1 = number of characters transferred7 longword 1 = status flag bit 0 : 0 = disconnected4 1 = connected< bit 1 : 0 = normal connection9 1 = replaced drive< bit 2 : 0 = normal connectionB 1 = replaced to DECRAM disk8 bit 3 : 0 = normal access: 1 = write protected7 bit 4 : 0 = local access8 1 = shared access8 bit 8 : 0 = normal timing: 1 = accurate timing7 bit 9 : 0 = normal trace; 1 = FDT trace active< The buffer specified in the P1 parameter is only valid when3 the unit is connected (IOSB longword 1 bit 0 set).? If a buffersize of 0 is specified only the status is returned. Errors returned:% SS$_ACCVIO Buffer can not be written" SS$_DEVINACT Device not connected! SS$_NORMAL Successful completion3 Disable_protect" This function unprotects a drive: Function: IO$_LD_CONTROL' P6 = Subfunction: LDIO_DISABLE_PROTECT IOSB word 0 = status Errors returned:! SS$_NORMAL Successful completion3 Disable_trace This function disables tracing: Function: IO$_LD_CONTROL% P6 = Subfunction: LDIO_DISABLE_TRACE IOSB word 0 = status Errors returned: SS$_NODATA Trace is not active! SS$_NORMAL Successful completion3 Disable_watch$ This function disables watchpoints: Function: IO$_LD_CONTROL# P1 = address of watchpt structures9 P2 = number of watchpt structures, if 0 all entries will be removed% P6 = Subfunction: LDIO_DISABLE_WATCH= The watchpt structure (if specified) must contain a matching; lbn, action, function, flags and returncode. These can all/ be retrieved with the LDIO_GET_WATCH function. IOSB word 0 = status0 longword 1 = current number of watchpoints Errors returned:# SS$_DATALOST No active watchpoints( SS$_DATACHECK Specified entry not found! SS$_NORMAL Successful completion3 Enable_protect& This function write-protects a drive: Function: IO$_LD_CONTROL& P6 = Subfunction: LDIO_ENABLE_PROTECT IOSB word 0 = status Errors returned:! SS$_NORMAL Successful completion3 Enable_trace This function enables tracing: Function: IO$_LD_CONTROL0 P1 = number of trace buffer packets to allocate$ P6 = Subfunction: LDIO_ENABLE_TRACE IOSB word 0 = sT$LD071.BDBURG.LD.V71.SRC]LD.HLP;17tatus< To reset the tracebuffer the additional subfunctionmodifier: LDIO_M_RESET can be specified. No new tracebuffer will be allocated when that is done.= Requests for FDT tracing need to add the subfunctionmodifier LDIO_M_FDTTRACE.5 Requests for accurate timing tracing need to add the% subfunctionmodifier LDIO_M_ACCURATE. Errors returned:$ SS$_ACCVIO Parameter cannot be read( SS$_BADPARAM Zero buffer size specified1 SS$_EXBYTLM Exceeded BYTLM quota for the process7 SS$_INSFMEM Not enough memory to allocate trace buffer( SS$_TOOMUCHDATA Trace is already active! SS$_NORMAL Successful completion3 Enable_watch# This function enables watchpoints: Function: IO$_LD_CONTROL# P1 = address of watchpt structures" P2 = number of watchpt structures$ P6 = Subfunction: LDIO_ENABLE_WATCH8 The watchpt structure must contain a valid lbn, action, function, flags and returncode. IOSB word 0 = status0 longword 1 = current number of watchpoints Errors returned:" SS$_ACCVIO Buffer can not be read6 SS$_NOPRIV Action needs ownership of device or CMKRNL6 SS$_NOCMKRNL CMKRNL privilege needed for CRASH action2 SS$_EXBYTLM Out of BYTLIM quota for watch buffers7 SS$_DEVREQERR FILE watchpoint must be on the LD device< SS$_ILLBLKNUM Watchpoint past end of file or end of disk or VBN zero specified for fileA SS$_BADPARAM Zero buffer length specified or action out of range> SS$_FORMAT FCB specified in SBK is invalid or file not opened with cathedral windows< SS$_INSFMEM Not enough memory to allocate watchpoint buffer! SS$_NORMAL Successful completion3 Get_suspend_list; This function retrieves the list of suspended watchpoints: Function: IO$_LD_CONTROL( P1 = address of suspend_list structures* P2 = size in bytes of suspend_list buffer( P6 = Subfunction: LDIO_GET_SUSPEND_LIST9 This function reads the number of suspended watchpoints: Function: IO$_LD_CONTROL9 P6 = Subfunction: LDIO_GET_SUSPEND_LIST | LDIO_M_INQUIRE IOSB word 0 = status& word 1 = number of bytes written: longword 1 = current number of suspended watchpoints Errors returned:; SS$_INSFMEM Not enough memory to allocate temporary buffer% SS$_ACCVIO Buffer can not be written# SS$_DATALOST No active watchpoints% SS$_IVBUFLEN Output buffer too small! SS$_NORMAL Successful completion 3 Get_watch% This function retrieves watchpoints: Function: IO$_LD_CONTROL# P1 = address of watchpt structures% P2 = size in bytes of watchpt buffer! P6 = Subfunction: LDIO_GET_WATCH/ This function reads the number of watchpoints: Function: IO$_LD_CONTROL2 P6 = Subfunction: LDIO_GET_WATCH | LDIO_M_INQUIRE IOSB word 0 = status& word 1 = number of bytes written0 longword 1 = current number of watchpoints Errors returned:; SS$_INSFMEM Not enough memory to allocate temporary buffer% SS$_ACCVIO Buffer can not be written# SS$_DATALOST No active watchpoints% SS$_IVBUFLEN Output buffer too small! SS$_NORMAL Successful completion3 Read_tracebuffer* This function reads the tracebuffer size: Function: IO$_LD_CONTROL2 P6 = Subfunction: LDIO_GET_TRACE | LDIO_M_INQUIRE IOSB word 0 = status word 1 = 07 longword 1 = tracebuffer size (number of packets)8 If the iosb status is SS$_WASSET FDT tracing is active.* This function reads the tracebuffer data: Function: IO$_LD_CONTROL1 P1 = address of buffer which receives trace info# P2 = size of trace buffer in bytes! P6 = Subfunction: LDIO_GET_TRACE IOSB word 0 = status word 1 = 00 longword 1 = number of packets transferred% or the number of lost packets in case of SS$_DATAOVERRUN.4 If the return status was SS$_DATAOVERRUN the buffer2 contains the number of packets as returned by the* LDIO_GET_TRACE | LDIO_M_INQUIRE function.; To read the tracebuffer data and to reset the buffer after@ the data has been read the subfunctionmodifier LDIO_M_RESET can be specified.@ If LDIO_M_NOWAIT is omitted the I/O request will wait until new trace data is available. Errors returned:% SS$_ACCVIO Buffer can not be written SS$_NODATA Trace is not active8 SS$_IVBUFLEN Buffer too small to contain all trace data1 SS$_DATAOVERRUN The tracebuffer has been overrun! SS$_NORMAL Successful completion3 Reset_tracebuffer& This function resets the tracebuffer: Function: IO$_LD_CONTROL# P6 = Subfunction: LDIO_RESET_TRACE IOSB word 0 = status Errors returned: SS$_NODATA Trace is not active! SS$_NORMAL Successful completion3 Resume_watch. This function resumes a suspended watchpoint: Function: IO$_LD_CONTROL( P1 = address of suspend_list structures* P2 = size in bytes of suspend_list buffer$ P6 = Subfunction: LDIO_RESUME_WATCH IOSB word 0 = status& word 1 = number of bytes written Errors returned:; SS$_INSFMEM Not enough memory to allocate temporary buffer% SS$_ACCVIO Buffer can not be written# SS$_DATALOST No active watchpoints SS$_DATACHECK Nothing resumed! SS$_NORMAL Successful completion3 Set_allocation_class@ This function enables presetting of the allocation class of the@ LD device. This may only be done when no devices are connected. Function: IO$_LD_CONTROL P1 = allocation class% P6 = Subfunction: LDIO_SET_ALLOCLASS IOSB word 0 = status Errors returned:. SS$_BADPARAM Allocation class > 255 specified0 SS$_UNSAFE There are still connected LD devices! SS$_NORMAL Successful completion3 Set_unitnumber6 This function enables presetting of the next assigned unitnumber. Function: IO$_LD_CONTROL P1 = unitnumber to preset P6 = Subfunction: LDIO_SET_SEED IOSB word 0 = status Errors returned:9 SS$_BADPARAM Unitnumber less than 0 or greater than 9999! SS$_NORMAL Successful completion3 Other_features= After modification of the driver it is possible to RELOAD it@ without rebooting the system, provided that the size of the UCB? did not change. If the reloaded driver detects a different UCB= size, the unit will be placed off-line and the system has to3 be rebooted. The reload is only possible on a VAX.@ If there is still a file or device connected to any LDAn device= the driver cannot be reloaded. In that case all LDAn devices have to be disconnected first.; If a tracebuffer is allocated before reloading, it will be* deallocated. All trace data will be lost.2 Error_messages> A number of error messages can be returned by the LD utility. 3 ACCURATE- %LD-I-ACCURATE, Accurate timing is activeF An LD SHOW/TRACE/STATUS command was given while accurate trace timing is active.' This status is only returned on Alpha. 3 ALLOCLASS. %LD-F-ALLOCLASS, Allocation class mismatchC An LD CONNECT/SHARE command was given where the current allocationC class of the specified LD device was different on the current node: then on another cluster node where the file is connected. 3 ALREADYCONN- %LD-F-ALRDYCONN, Device already connected= An LD CONNECT command is given while the logical disk device was already connected.8 This errormessage can only occur if the driver has been) assembled without cloned device support.3 BADALLOCLASS6 %LD-F-BADALLOCLASS, Bad allocation class specifiedC An LD CONNECT/ALLOCLASS command was given with an allocation class bigger than the limit of 255.3 BADDEVSYNTAX1 %LD-F-BADDEVSYNTAX, Bad device syntax (LDAZ:)9 A unit to use as a logical disk was specified with a bad syntax. 3 BADENTPARAM0 %LD-F-BADENTPARAM, Bad /ENTRIES parameter(s)< An LD SHOW/TRACE/ENTRIES command was given where either theC second parameter was less than the first, or one of the parameters was less than 0.3 CANTREADOLDFMTD %LD-F-CANTREADOLDFMT, Can't read old format datafile (Version 2)9 An attempt was made to read a datafile which was created= with an old version of LD. Use the old version of LD to read the file.3 CLONENOTMOUNTED7 %LD-F-CLONENOTMOUNTED, Clone device must be mounted@ A CREATE/CLONE=dev or CONNECT/CLONE=dev command was given where 'dev' was not mounted. 3 CLOSERRD %LD-F-CLOSERR, Unable to close file $1$DUA0:[DATA]LD_TRACE.LOG;1A An LD SHOW/TRACE/{IN,OUT}PUT command was given, and the close of& the in- or output file had a problem. 3 CONFQUAL* %LD-F-CONFQUAL, Conflicting qualifiers= Conflicting qualifiers were given for the specified command. Check the documentation$LD071.BDBURG.LD.V71.SRC]LD.HLP;1V"#. 3 CONNECTED9 %LD-I-CONNECTED, Device _THEBUG$LDA1: is connected to $1$DUA1:[DIR]DISKFILE.DSK;1; An LD CONNECT/LOG command is given, or an LD SHOW command.M %LD-I-CONNECTED, Device _THEBUG$LDA1: is connected to $1$DUA1: (Replaced)C An LD CONNECT/LOG/REPLACE command is given, or an LD SHOW command." The complete device was replaced.3 CONTTRACEACT@ %LD-F-CONTTRACEACT, Continuous tracing is already active for device _THEBUG$LDA1:A An LD SHOW/TRACE/CONTINUOUS command was given while such command- for the same device was already in progress.3 CONTTRACENOTACT? %LD-F-CONTTRACENOTACT, Continuous tracing is not active for device _THEBUG$LDA1:: An LD TRACE/STOP command was given for a specified device< while there was no continuous trace active for that device. 3 CREATED; %LD-I-CREATED, File $1$DUA1:[DIR]DISKFILE.DSK;1 created# An LD CREATE/LOG command is given. 3 CREATERRA %LD-F-CREATERR, Unable to create file $1$DUA0:[TMP]DATA.DSK;1; An LD CREATE command was given, but the command was unable7 to complete. The accompanying message shows more info. 3 CYLINDERS' %LD-F-CYLINDERS, Cylinders mismatch: An LD CONNECT/SHARE command was given where the number ofF cylinders did not match the number of cylinders for the containerfile on another node. 3 DETECTEDERR+ %LD-F-DETECTEDERR, Detected fatal error= A general fatal error was detected. Look at the accompanying errormessage. 3 DEVASSIGN1 %LD-F-DEVASSIGN, Device has channels assigned; An LD DISCONNECT command was given on a device with active< channels. Most probably an LD SHOW/TRACE/CONTINUOUS command is still active.3 DEVCONNECTEDJ %LD-F-DEVCONNECTED, Cannot set allocation class with active LD devicesC An LD CONNECT/ALLOCLASS command was given while there were alreadyA LD devices connected. All devices have to be disconnected before an allocation class can be set. 3 DEVICEINUSEP %LD-F-DEVICEINUSE, Device incompatible connected to other LD disk in clusterC An LD CONNECT command is given where the device to be replaced wasH already in use by another logical disk or by VMS, either on this system# or on another system in a cluster. 3 DUPUNIT' %LD-F-DUPUNIT, Duplicate unitnumber< An LD CONNECT file LDAxx command was given while the device LDAxx already exists. 3 FDTACTIVE* %LD-I-FDTACTIVE, FDT Tracing is active< An LD SHOW/TRACE/STATUS command was given while FDT tracing is active.' This status is only returned on Alpha. 3 FILEINUSEL %LD-F-FILEINUSE, File incompatible connected to other LD disk in cluster? An LD CONNECT command is given where the logical disk file was> already in use by another logical disk, either on this systemF or on another system in a cluster. The connect specified on the otherB system was done in a way which is not compatible with the currentB connect parameters. This can happen if another node specified for> example /MAXBLOCKS, or /TRACKS which were not or incompatibleD specified on the local node. The complete geometry must be the same on all nodes. 3 FILEONOTHER= %LD-F-FILEONOTHER, File not allowed to be on other device@ An LD WATCH/FILE=filespec command was given where the specified0 file did not reside on the specified LD device. 3 FILREADERRG %LD-F-FILREADERR, Unable to read input file $1$DUA0:[TMP]FILE.DAT;1= An LD SHOW/TRACE/INPUT command was given where the requested inputfile could not be read. 3 FILWRTERRH %LD-F-FILWRTERR, Unable to write output file $1$DUA0:[TMP]FILE.DAT;1> An LD SHOW/TRACE/OUTPUT command was given where the requested! outputfile could not be written. 3 INFILERRE %LD-F-INFILERR, Unable to open input file $1$DUA0:[TMP]FILE.DAT;1= An LD SHOW/TRACE/INPUT command was given where the requested inputfile could not be opened. 3 INVGEOMETRY1 %LD-F-INVGEOMETRY, Invalid geometry specified< An LD CONNECT command is given where the geometry specified< with /TRACKS, /SECTORS or /CYLINDERS is invalid. TRACKS and; SECTORS are limited to 255, CYLINDERS is limited to 65535.@ This error can also be returned if TRACKS * SECTORS * CYLINDERSD is less than the maximum logical blocknumber of the container file. 3 MAXBLOCK0 %LD-F-MAXBLOCK, Maximum blocknumber mismatchB An LD CONNECT/SHARE command was given where the maximum number of: blocks did not match the maximum number of blocks for the containerfile on another node. 3 NESTING7 %LD-F-NESTING, Nesting of logical disks not allowed? An LD CONNECT command is given where the logical disk file was on another logical disk. 3 NOCLUSTER+ %LD-F-NOCLUSTER, No cluster code loaded< An LD CONNECT/SHARE command is given when the system is not running VMSCluster software. 3 NOGROUPPRIVI %LD-F-NOGROUPPRIV, No privilege to stop trace of device _THEBUG$LDA1:G An attempt was made to stop the continuous trace of device THEBUG$LDA1E while the trace was started by a process in the same UIC group. This operation need GROUP privilege. 3 NOREADWRITEH %LD-F-NOREADWRITE, No read or write function specified for file modeC An LD WATCH/FILE=filespec command was given where the function wasB not one of the following: READPBLK, WRITEPBLK, WRITECHECK or DSE.B Such watchpoint may only be specified in combination with a data- transfer function. 3 NOSHARE= %LD-F-NOSHARE, No sharing specified for file on this nodeB An LD CONNECT command was given while the specified containerfile+ was connected with /SHARE on another node. 3 NOSUPPORT: %LD-F-NOSUPPORT, Device _THEBUG$LDA0: is not supported< An attempt was made to use LDA0: as a logical disk. This is not supported. 3 NOTADISK/ %LD-F-NOTADISK, Clone device must be a diskB An LD CONNECT/CLONE=dev or CREATE/CLONE=dev was given where 'dev' was not a disk.3 NOTCONNECTED= %LD-W-NOTCONNECTED, Device _THEBUG$LDA1: is not connected< An LD SHOW command is given, and the disk was not connected to a file.@ An LD DISCONNECT command is given while the logical disk device was not connected.8 This errormessage can only occur if the driver has been) assembled without cloned device support. 3 NOTODS2< %LD-F-NOTODS2, Container file must be on an ODS-2 volume@ An attempt was made to connect an LD device to a container file; on a non-ODS-2 volume, for example a Spiralog volume. That is not supported. 3 NOTRCDATA, %LD-F-NOTRCDATA, No trace data available= An LD SHOW/TRACE command was given while the tracebuffer did not contain any data. 3 NOTSHARED5 %LD-F-NOTSHARED, File is not shared on other node: An LD CONNECT/SHARE command was given while the specified= containerfile was not connected with /SHARE on another node.3 NOUNITSFOUND: %LD-F-NOUNITSFOUND, No usable logical disk units found9 An LD SHOW/ALL or TRACE/STOP/ALL command was given while no connected units were found. 3 NOTVISIBLE: %LD-F-NOTVISIBLE, Device is not visible on other nodes8 An LD CONNECT/SHARE command is given when the device to6 be replaced or the device on which the container file/ resides is not visible by other cluster nodes. 3 NOWATCHDATA3 %LD-F-NOWATCHDATA, No watchpoint data available= An LD SHOW/WATCH command was given while no watchpoints were set. 3 NOWDISCONNC %LD-I-NOWDISCONN, Device $8$LDA1: is now disconnected from file $8$DIA2:[LD]DEVICE1.DSK;1' An LD DISCONNECT/LOG command is given. 3 NOWORLDPRIVI %LD-F-NOWORLDPRIV, No privilege to stop trace of device _THEBUG$LDA1:G An attempt was made to stop the continuous trace of device THEBUG$LDA1D while the trace was started by a process in another UIC group. This operation need WORLD privilege. 3 OUTFILERRG %LD-F-OUTFILERR, Unable to open output file $1$DUA0:[TMP]FILE.DAT;1> An LD SHOW/TRACE/OUTPUT command was given where the requested outputfile could not be opened. 3 PASTDATA7 %LD-F-PASTDATA, Requested entry(s) past end of data? An LD SHOW/TRACE/ENTRIES command was given where the requested( entries were beyond the available data. 3 REMOTEALLOCE %LD-W-REMOTEALLOC, Device _$1$LDA1: is allocated on a remote node; An LD SHOW or an LD DISCONNECT command was given while the: specified LD device was mounted private on a remote node.> This can only happen on an LD device which was connected with /SHARE. 3 SECTORS# %LD-F-SECTql$LD071.BDBURG.LD.V71.SRC]LD.HLP;1D"4ORS, Sectors mismatch: An LD CONNECT/SHARE command was given where the number ofB sectors did not match the number of sectors for the containerfile on another node.3 STATUS? %LD-I-STATUS, Device _THEBUG$LDA1: has a tracebuffer of 512 entries (139 valid records)? An LD SHOW/TRACE/STATUS command is given. The specified deviceB has 512 tracebuffer entries allocated, of which 139 are currently in use.3 TRACESTOPPED6 %LD-I-TRACESTOPPED, Trace stopped by other process; An LD TRACE/STOP command was given for a device to force a? SHOW/TRACE/CONTINUOUS command which was in progress by another process to exit.3 TRACKS! %LD-F-TRACKS, Tracks mismatch: An LD CONNECT/SHARE command was given where the number of@ tracks did not match the number of tracks for the containerfile on another node. 3 TRCDISABL( %LD-F-TRCDISABL, Tracing is disabled9 An LD SHOW/TRACE command was given while tracing was not" enabled for the specified device. 3 TRCENABL. %LD-F-TRCENABL, Tracing is already enabled8 An LD TRACE command was given while tracing was already enabled.3 UNIT1 %LD-I-UNIT, Allocated device is _THEBUG$LDA4:; An LD CONNECT command was given without /LOG and without a= device specification. This informational message shows which+ device was assigned to the specified file. 3 UNITNUMBER) %LD-F-UNITNUMBER, Unitnumber mismatchE An LD CONNECT/SHARE command was given when the specified unit numberB was different than the unitnumber for the same containerfile used on another node.3 UNSUPPORTEDFS/ %LD-F-UNSUPPORTEDFS, Unsupported filesystemB An LD CONNECT command was given while the specified containerfile+ was located on an unrecognized filesystem. 3 VBNERROR: %LD-F-VBNERROR, Illegal virtual block number specified= An LD WATCH/FILE=filespec command was given where the VBN to1 watch was either zero or beyond the end of file. 3 WPTNOTFOUND+ %LD-F-WPTNOTFOUND, Watchpoint not foundB An LD SHOW/WATCH command was given where the specified watchpoint does not exist.3 OtherC The VAX/VMS System Messages and Recovery Procedures Reference Man-A ual lists other possible messages and provides explanations and suggested user actions.@ Note that when errormessages are returned, and you cannot traceA them back to the Logical Disk, perform the same checks again for5 the Physical Disk on which the Logical File resides. 2 Features> Logical disks may be just a single disk, part of a volumeset,= part of a stripeset, part of a host-based shadowset, part of* a host-based raid set or any combination.> The file to be used for the logical disk may be placed on anyA physical disk, in any directory. A backup can be made to protect the disk.@ A physical device may be 'replaced' by a logical disk to enable? logging of all I/O of the physical disk (See CONNECT/REPLACE).2 HELP Provides interactive help.2 New_features_V5.0D Enable/disable write-protection of a device with the next command: LD [NO]PROTECT LDan:E Watchpoint support. A watchpoint is a marker which can be set on oneI or more logical blocks of a disk. The watchpoint consists of a function-D code, an action, an error return code and one logical block number.G When an I/O request is done to the driver it will check if the requestF matches one of the watchpoints. If true then the action specified forH that watchpoint will be executed. An action can be for example an OPCOMI message, but also to return an error code, or to suspend the I/O thread, or to crash the system.F The following commandfile will for example put the device into mount- verification: $ SET VERIFY $ SET NOON $!9 $! Example of how to put a device into mountverification $!" $ LD CREATE/NOBACKUP TEMPDISK.DSK$ $ LD CONNECT/LOG TEMPDISK.DSK LDA1: $ INIT/NOHIGH/SYSTEM LDA1: TEST $ LD TRACE LDA1 $ MOUNT LDA1: TEST5 $ LD WATCH LDA1: 1/ACTION=OPCOM/FUNCTION=CODE=%X080C: $ LD WATCH LDA1: 1/ACTION=ERROR=%X84/FUNCTION=CODE=%X08084 $ LD WATCH LDA1: 10/ACTION=ERROR=%X84/FUNCTION=READ $ LD SHOW/WATCH LDA1: $ REPLY/ENABLE/TEMP= $ SPAWN/NOWAIT/INPUT=NL: DUMP LDA1:/BLOCK=(START=10,COUNT=1)+ $ INQUIRE DUMMY "Press return to continue" $ LD NOWATCH LDA1: $ LD SHOW/TRACE LDA1: $ REPLY/DISABLE $ DISMOUNT LDA1: $ LD DISCONNECT LDA1:$ $ DELETE/NOLOG/NOCONF TEMPDISK.DSK;2 New_features_V5.1= Shared access of the container file or a replaced device are? now possible by connecting with the /SHARE switch. For this toF work the 'LD' devicename must be the same on all nodes of the cluster4 which connect to the same container file or device.A Device geometry can now be specified by connecting a device withB the /SECTORS, /TRACKS and /CYLINDERS qualifiers. Also the maximum? useable size may be given with the /MAXBLOCKS qualifier, which% supersedes the /ALLOCATED qualifier.@ An allocation class may be specified for the LD device with the? /ALLOCLASS qualifier for CONNECT. If this is not done then the9 default is the value from the systemparameter ALLOCLASS.: A switch was added to CONNECT, /SYMBOL. This allows a DCL< symbol to be set representing the created unit number if it was not specified.2 New_features_V6.0. The driver is finally supported by ALPHA/VMS.= The restriction that container files had to be contiguous isB removed. Also, nesting of LD devices (container file on a logical disk) is now allowed.@ A watchpoint may now specify /FUNCTION=ALL to trap all function codes.2 New_features_V6.2> If one needs more than 9999 logical disk devices then another7 'controller' may be used by specifying a new device at connection time: $ RUN SYS$SYSTEM:SYSMAN5 IO CONNECT LDA0/NOADAPTER/DRIVER=SYS$LDDRIVER5 IO CONNECT LDB0/NOADAPTER/DRIVER=SYS$LDDRIVER5 IO CONNECT LDC0/NOADAPTER/DRIVER=SYS$LDDRIVER2 New_features_V6.3? FDT routine access can now be traced. This will show access toB the driver which is otherwise hidden, since some driver functions9 can complete in the FDT routines. It can be enabled with $ LD TRACE/FDT (Alpha only).? Accurate timing of I/O requests can now be done by enabling it< this way: $ LD TRACE/ACCURATE (Alpha only). This allows I/OA duration to be measured with a resolution of one microsecond, as@ opposed to the normal 10 mSec accuracy. The drawback is that if> this is used all I/O has to go through the primary processor.@ Container files may now reside on remotely mounted NFS volumes.A Note however that members of a shadowset may not be used if theyd@ reside on such a device. This is because the NFSdriver does not> support the IO$_WRITECHECK function. This restriction will be lifted in a future release.< The restriction of the maximum size of the logical disk not= exceeding 2097120 blocks without specifying the geometry hass= been lifted. Any size up to the size of the disk holding the  containerfile is allowed.2 New_features_V7.0e! There are new CREATE qualifiers:d /TRACKS=nnn /SECTORS=nnnd /CYLINDERS=nnno /MAXBLOCKS=nnn A These settings will be stored in the containerfile as metadatac< (in the fileheader), and if present will be used when theD containerfile is connected (unless /NOAUTOGEOMETRY is specified). /LBN=nnnvC Allows placement of the containerfile on a specific LBN. If that C LBN is not available an error will be returned. Notice that whennA /CONTIGUOUS is not specified and the request succeeds the filexB may be split in multiple segments at the discretion of the XQP,> in that case only the first part of the file will be on the specified LBN.O /CLONE=deviceC Allows size and geometry to be cloned from the specified device.xC This device has to be mounted for this to function. The geometry 4 will automatically be saved in the containerfile. New CONNECT qualifiers: /CLONE=deviceC Allows size and geometry to be cloned from the specified device. E This device has to be mounted for this to function. Other geometryI; data may be overruled when specified on the commandline.c /AUTOGEOMETRYD This is the default. On a connect the containerfile is checked if? there's geometry info available, and if true that's used for]/ connect unless /NOAUTOGEOMETRY is specified.L /SAVE2 Updates the geometry data in the containerfile. New [NO]PROTECT qualifiers: /PERMANENT,D Save writel>-$LD071.BDBURG.LD.V71.SRC]LD.HLP;1o"Eock status in the containerfile so that it may be usedE during CONNECT (like emulating a 'hardware write protect' button)., Other features:8 Interactive help is available with the LD HELP command.> If a shared file is connected and an error is returned due toD incompatibilities with sharing of other nodes, a more verbose error? message is given with the reason for the failure, like invalidr$ allocation class, unit number, etc.D The following will automatically connect the right unit if it's not& specified and file sharing is wanted:( $ LD CONNECT DISK:[DIR]FILE.DSK/SHARED This way of connecting needs CMKRNL privilege. If this privilege is< lacking a clear error message will be displayed about that.; There's no problem anymore in using an ODS5 volume for theo1 containerfile, this restriction has been lifted.e> Using a containerfile exceeding 4Gb will not crash the system anymnore. 2 Parameters Filespecc9 Specifies the logical disk file to create or to connect.O8 Any legal VMS filespec without wildcards or nodename is acceptable. DeviceP4 Logical disk devicename, LDan where n is 1 to 9999.2 Privileges_and_Quotaso> To control creation and deletion of logical devices one needs? PHY_IO privilege for the LD utility. To use continuous tracing 5 SYSLCK privilege is needed. The startup commandfile,I< SYS$STARTUP:LD$STARTUP.COM will install the LD utility with; these privileges so that ordinary users can play around by  creating and removing disks.a; Note that the users will be charged against BYTLM for eachL; unit they create. Also, the creation of a tracebuffer willr? be charged against BYTLM, as well as creating of a watchpoint.F? Connecting a shared disk which is already connected on anotherI; cluster node without specifying a unit number needs CMKRNL 8 privilege. Due to possible security implications the LD. utility is not installed with this privilege.2 Restrictions= A file used for a logical disk may not be used for any othere? logical disk, unless /SHARE is specified. If that is not done,i@ the driver will return SS$_FILALRACC. The same is true when one@ tries to replace a device, the error returned in that case will be SS$_DEVALLOC.n> A containerfile for a logical disk must reside on an ODS-2 or5 an ODS-5 volume. Spiralog volumes are not supported.s9 If a continuous trace is in progress it is impossible toi> mount a logical disk because the reference count is not zero.@ Stop the trace, mount the disk and restart the trace. No trace-7 data will be lost because the driver will buffer this.e@ Container files may now reside on remotely mounted NFS volumes.A Note however that members of a shadowset may not be used if theyu@ reside on such a device. This is because the NFSdriver does not= support the IO$_WRITECHECK function. This restriction may beT lifted in a future release.2 SetupT3 The logical disk has to be setup by connecting theL3 driver with SYSGEN. The unitnumber can be selectedr) by the user connecting the logical disk. To connect the driver on VAX: $ RUN SYS$SYSTEM:SYSGENd CONNECT LDA0/NOADAPTER To connect the driver on Alpha: $ RUN SYS$SYSTEM:SYSMAN$5 IO CONNECT LDA0/NOADAPTER/DRIVER=SYS$LDDRIVERe8 This assumes that the driver is in SYS$LOADABLE_IMAGES: 3 ExampleR- An example of setting up some Logical Disks.o $ RUN SYS$SYSTEM:SYSGENa. CONNECT LDA0/NOADAPTER/DRIVER=LDDRIVER6 $ LD CREATE/SIZE=8192 $1$DUA1:[TEMP]USER.DSK;13 $ LD CONNECT $1$DUA1:[TEMP]USER.DSK;1 LDA1:e# $ INITIALIZE LDA1: USERDSK1  $ MOUNT/OVER=ID LDA1:e2 IO_Trace_example An example of traced I/O:7 $ LD SHOW/TRACE/ENTRIES=(12,23)/IOSB=COMBINATION LDA1:f+ I/O trace for device _THEBUG$LDA1:L, 24-FEB-1994 16:36:56.31 on node THEBUG::CStart Time Elaps Pid Lbn Bytes Iosb Count FunctionaC------------------------------------------------------------------- B20:52:30.01 00.02 202000C4 5 512 NORMAL 512 READPBLKM20:52:30.03 00.03 202000C4 5 512 NORMAL 512 WRITEPBLK|DATACHECK @20:52:30.07 00.00 202000C4 0 0 NORMAL 0 UNLOADJ23:16:18.05 00.00 202000C5 0 32 NORMAL 0 SETMODE|LINE_OFFQ23:16:30.13 00.00 202000C5 0 11 NORMAL 0 SETMODE|LINE_ON|NEWLINEwJ23:17:05.70 00.05 202000C5 0 0 NORMAL 0 PACKACK|INHERLOGB23:17:05.76 03.22 202000C5 1 512 NORMAL 512 READPBLKB23:17:09.00 00.67 202000C5 608509 512 NORMAL 512 READPBLKB23:17:09.67 00.03 202000C5 608510 512 NORMAL 512 READPBLKB23:17:09.70 00.27 202000C5 608334 512 NORMAL 512 READPBLKC23:17:09.97 00.00 202000C5 608334 512 WRITLCK 0 WRITEPBLKoB23:17:10.04 00.29 202000C5 608508 512 NORMAL 512 READPBLK 2 CONNECTr# LD CONNECT file_or_device [LDAn:]i? This command will connect the Logical Disk File to the Logical Disk device.D? This function must be performed before the logical disk can be? mounted or otherwise used. It requires the Physical Disk to bet; mounted and requires read access to the Logical Disk File.C> If the LD-device is not specified, it will be assigned by the? driver, and an informational message will be given about whichb device is selected.> If the device is specified, and if that device does not exist it will be used as specified. 3 /ALLOCLASS /ALLOCLASS=allocation_classD If this qualifier is specified, the specified allocation class willE be set for all LD devices. This qualifier can only be set when there ' are currently no LD devices connected.aD Setting an allocation class can be usefull if LD device needs to beD shared in a cluster where different nodes have different allocation classes.a3 /AUTOGEOMETRYc /AUTOGEOMETRYB This is the default. On a connect the containerfile is checked ifE there's geometry info available, and if true that's used for connectm% unless /NOAUTOGEOMETRY is specified.3 /CLONE /CLONE=deviceF Allows size and geometry to be cloned from the specified device. ThisC device has to be mounted for this to function. Other geometry data 4 may be overruled when specified on the commandline. 3 /CYLINDERS /CYLINDERS=number_of_cylindersE When this qualifier is specified the device will be created with thea@ specified number of cylinders. If not specified, and no /TRACKSC and /SECTORS switches are specified, then the device geometry willtH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified._* The maximum number of cylinders is 65535.A If SECTORS * TRACKS * CYLINDERS is less than the capacity of thed/ containerfile an error message will be issued.E WARNING:tA If a container file has been used and is reconnected with otherL= geometry information which results in another disksize then  diskcorruption may result.F This qualifier may not be used together with /REPLACE as the geometry, will be inherited from the replaced device.3 /LOG /LOGS /NOLOG (default)eE Shows the connection between the logical disk and the file or device) after the connection has been completed.F 3 /MAXBLOCKS /MAXBLOCKS=number_of_blockt@ When this qualifier is specified the specified number of blocksC will be the maximum used for the device. It may not be bigger than * the allocated size of the container file. 3 /REPLACEA When this qualifier is specified one has to specify a devicenameE< instead of a filespec. This has the effect of replacing the> physical drive with the logical drive, and it enables all I/OB requests to the physical device to be logged. The physical device* will be marked allocated and unavailable.3 /SAVEt /SAVE0 Updates the geometry data in the containerfile. 3 /SECTORS /SECTORS=number_of_sectorsfE When this qualifier is specified the device will be created with the A specified number of sectors. If not specified, and no /CYLINDERSnB and /TRACKS switches are specified, then the device geometry willH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified.f& The maximum number of sectors is 255.A If SECTORS * TRACKS * CYLINDERS is less than the capacity of thei/ containerfile an error message will be issued.: WARNING:PA If a container file has been used and is reconnected with other= geometry information which results in another disksize thenU diskcorruption may result.n$LD071.BDBURG.LD.V71.SRC]LD.HLP;1"VF This qualifier may not be used together with /REPLACE as the geometry, will be inherited from the replaced device.3 /SHAREE If this qualifier is specified, then sharing the container file withfG other nodes in a cluster is allowed, provided that the same devicenamecC (ALLDEVNAM) is used on every node accessing the file, and that thef. specified geometry is the same on every node.A If the containerfile needs to be accessed on other nodes as wellsC then the CONNECT/SHARE command must be issued on all nodes wanting  to share the container file.A If used together with /REPLACE then sharing of another device isL allowed. 3 /SYMBOLnA When this qualifier is specified a local DCL symbol representing B the unit number will be created: LD_UNIT. This can be useful when> a connect is done without specifying a unit number. With this> qualifier included a command procedure is able to use this in subsequent commands.T 3 /TRACKSv /TRACKS=number_of_tracks E When this qualifier is specified the device will be created with thea@ specified number of tracks. If not specified, and no /CYLINDERSC and /SECTORS switches are specified, then the device geometry willIH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified.s% The maximum number of tracks is 255..A If SECTORS * TRACKS * CYLINDERS is less than the capacity of theO/ containerfile an error message will be issued.t WARNING:PA If a container file has been used and is reconnected with other= geometry information which results in another disksize then= diskcorruption may result.F This qualifier may not be used together with /REPLACE as the geometry, will be inherited from the replaced device.2 CREATE LD CREATE filespecE This command will create a file which can be used as a Logical Disk.TB The default extension is ".DSK". The file will be set /NOMOVE and /CACHING_ATTRIBUTE=NO_CACHING.d 3 /BACKUPR /BACKUP (default) /NOBACKUP: This qualifier can be used to mark the file /NOBACKUP. In; that case when a backup of the file is done only the file- 9 header will be copied. On a restore the file will be re-$ created, and all data will be lost.3 /CLONE /CLONE=deviceF Allows size and geometry to be cloned from the specified device. ThisA device has to be mounted for this to function. The geometry wille- automatically be saved in the containerfile.n 3 /CONTIGUOUS_ /CONTIGUOUS /NOCONTIGUOUS (default)D This qualifier can be used to create the container file contiguous. 3 /CYLINDERS /CYLINDERS=number_of_cylinderssE When this qualifier is specified the device will be created with thes@ specified number of cylinders. If not specified, and no /TRACKSC and /SECTORS switches are specified, then the device geometry willH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified.f* The maximum number of cylinders is 65535.A If SECTORS * TRACKS * CYLINDERS is less than the capacity of the/ containerfile an error message will be issued.aE This setting will be stored in the containerfile as metadata (in thefD fileheader), and if present will be used when the container file is1 connected (unless /NOAUTOGEOMETRY is specified).c3 /LBN /LBN=nnn:E Allows placement of the containerfile on a specific LBN. If that LBNS= is not available an error will be returned. Notice that when F /CONTIGUOUS is not specified and the request succeeds the file may beF split in multiple segments at the discretion of the XQP, in that case> only the first part of the file will be on the specified LBN.3 /LOG /LOGu /NOLOG (default)_; Shows the complete filespec of the created file after fileD creation. 3 /MAXBLOCKS /MAXBLOCKS=number_of_blockR@ When this qualifier is specified the specified number of blocksC will be the maximum used for the device. It may not be bigger thans* the allocated size of the container file.E This setting will be stored in the containerfile as metadata (in theeD fileheader), and if present will be used when the container file is1 connected (unless /NOAUTOGEOMETRY is specified).s 3 /SECTORS /SECTORS=number_of_sectors E When this qualifier is specified the device will be created with thehA specified number of sectors. If not specified, and no /CYLINDERSfB and /TRACKS switches are specified, then the device geometry willH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified.A& The maximum number of sectors is 255.A If SECTORS * TRACKS * CYLINDERS is less than the capacity of thec/ containerfile an error message will be issued.E This setting will be stored in the containerfile as metadata (in the D fileheader), and if present will be used when the container file is1 connected (unless /NOAUTOGEOMETRY is specified).c3 /SIZE /SIZE[=disksize]u> Specifies the size of the logical disk file. The default size is 512 blocks.v 3 /TRACKSo /TRACKS=number_of_tracksmE When this qualifier is specified the device will be created with the@ specified number of tracks. If not specified, and no /CYLINDERSC and /SECTORS switches are specified, then the device geometry willAH be calculated by the driver. If it is specified then any other geometry- switch will default to 1 when not specified. % The maximum number of tracks is 255.A If SECTORS * TRACKS * CYLINDERS is less than the capacity of thee/ containerfile an error message will be issued.nE This setting will be stored in the containerfile as metadata (in the)D fileheader), and if present will be used when the container file is1 connected (unless /NOAUTOGEOMETRY is specified).e 2 DISCONNECT LD DISCONNECT [LDAn:]E> This command will disconnect the Logical Disk device from the. Logical Disk file, or from the physical disk. 3 /ABORT cA This qualifier can be used if the normal disconnect command does = not disconnect the file (for example, when the file has beenTA deleted). The ABORT qualifier disconnects the Logical Disk as itN is (No checks.); When the physical disk is dismounted, and the logical diskC is still active,the physical disk will enter a marked for dismount5 state until the logical disk is no longer connected.t9 The steps to follow if this happens are described below;O $ DISMOUNT LDA1: $ LD DISCONNECT/ABORT LDA1: $ DISMOUNT DDan:@ Be careful using the ABORT qualifier because it disconnects the0 Logical Disk independent of the state it is in.3 /ALL- This qualifier disconnects all LDAn devices.., No devicename has to be given in this case.3 /LOG /LOGa /NOLOG (default)O9 Shows the status after the device has been disconnected.c 2 NOPROTECTC LD NOPROTECT LDan:C This command will remove write-protection of the specified device.. 3 /PERMANENT /PERMANENT-= Save writelock status in the containerfile so that it may bee? used during CONNECT (like emulating a 'hardware write protect'w button).o 2 NOWATCHt" LD NOWATCH LDan: [lbn [,lbn...]]3 This command will remove the specified watchpoint.C3 /INDEX /INDEX=n? This qualifier will remove only the specified watchpoint. Thisa@ is an alternate way for specifying a watchpoint which is neededF for watchpoints without an lbn (IO$_PACKACK watchpoints for example).2 SHOW LD SHOW [LDAn:]; This command will display information about the status andL+ the connected Logical Disk file or device.T0 It can also be used to display the tracebuffer. 3 /ACCURATEw? This qualifier allows accurate timing to be displayed from theG tracebuffer, provided that it was enabled with the setup of the trace.N* This function is only supported on Alpha.3 /ALL; This qualifier will show the status of all LDAn devices one@ the system. It can not be specified in combination with /TRACE.* No devicename must be given in this case. 3 /BINARY.< This qualifier forces the tracebuffer data to be written toC the outputfile in binary form. The is required when the outputfile * has to be processed later on with /INPUT. 3 /BLOCKSs /BLOCKS=blocks= This qualifier specifies the maximum size of a diskfile withB tracedata. After this size is reached the current file is closed,A and a new one with a higher version is opened. If this qualifierw> is not specified then there's no limit. If the /VERSION_LIMIT> qualifier is specified too, then the excess datafiles will be purged. 3 /BYTECOU $LD071.BDBURG.LD.V71.SRC]LD.HLP;1$H"gNT /BYTECOUNT (Default) /NOBYTECOUNT: This qualifier enables to number of requested bytes to be displayed. 3 /CONTINUOUS /CONTINUOUSy: When this qualifier is specified, the trace buffer of the7 specified device will be dumped (if there's still data-; waiting). After that the program will wait for new data too- show up, and display that if it's available.y7 To exit this mode one can type either a Control-Z or ag; Control-C. Another way is to issue a LD TRACE/STOP commande from another process. 3 /ENTRIES /ENTRIES[=(XXX,YYY)]]p= This qualifier allows selection of the tracebuffer contents.n@ If one parameter is specified, it will be the number of entriesA from the start of the tracebuffer when positive, or from the endc" of the tracebuffer when negative.? For example, /ENTRIES=10 will show the first 10 entries, while, /ENTRIES=-10 will show the last 10 entries.E When two parameters are specified, a range of entries will be shown.e? For example, /ENTRIES=(30,50) will show all entries from entry  30 through entry 50 inclusive.o2 The default of -10 will show the last 10 entries.3 /FDTB This qualifier allows FDT routine access to be displayed from theG tracebuffer, provided that it was enabled with the setup of the trace.s* This function is only supported on Alpha. 3 /FUNCTION /FUNCTION[=Keyword]- /NOFUNCTIONb> This qualifier enables the Function code of the request to be displayed.n" An optional keyword may be given:< FUNCTION=TEXT Displays the translated I/O request function/ along with eventual functioncode modifiers.D/ Function bits which could not be translatedn( will be shown in hexadecimal format.5 FUNCTION=HEX Displays the I/O request function codee in hexadecimal format. The default is /FUNCTION=TEXT.C 3 /HEADERn /HEADER (Default)e /NOHEADERp? This qualifier enables a header to be displayed before showingE= tracedata. The header contains a timestamp of when the traceiB was made, a devicename of the LD-device involved and the nodename of the system.R 3 /NUMBERC /NUMBERt /NONUMBER (Default) 5 This qualifier enables entrynumbers to be displayed. 3 /INDEX /INDEX=nB This qualifier enables watchpoint display by index. The parameter0 is the watchpoint number as seen by the driver.3 /INPUT /INPUT[=filespec]TD This qualifier forces input tracedata to be read from the specified, file. The default filename is LD_TRACE.DAT.3 /IOSBo /IOSB[=Keyword] /NOIOSB; This qualifier enables the I/O status block of the request  to be displayed.i" An optional keyword may be given:2 IOSB=TEXT Displays the translated errortext from the iosb.,2 IOSB=HEX Displays the first longword of the iosb in hexadecimal format.5 IOSB=LONGHEX Displays both longwords of the iosb in hexadecimal format.N9 IOSB=COMBINATION Displays the translated error text frome. the iosb, as well as the returned count in% the upper first word of the iosb.s The default is /IOSB=TEXT. 3 /LBN /LBN (Default) /NOLBN= This qualifier enables to Logical blocknumber of the request  to be displayed.i 3 /OUTPUT  /OUTPUT[=filespec]A This qualifier forces output of the tracedata data to be written B to the specified file. The default output will be readable ASCII,C unless /BINARY was also specified. In that case a binary file willn? be written which can be processed at a later time with /INPUT.iB The default filename is LD_TRACE.LOG for normal ASCII output, and LD_TRACE.DAT for binary output.3 /PID /PID (Default) /NOPIDC This qualifier enables to Process Identification of the requestingA process to be displayed.D3 /RESET= When this qualifier is specified in combination with /TRACE,O< the tracebuffer will be dumped and the buffer will be resetA afterwards. This is an atomic operation, such that no bufferdata  will be lost. 3 /STATUSC= This qualifier will show the currently allocated size of theo> tracebuffer, as well as the number of entries in use. It must$ be used in combination with /TRACE. 3 /TIMESTAMP /TIMESTAMP[=Keyword] /NOTIMESTAMP> This qualifier enables the Start-time as well as the End-time7 or the Elapsed time of an I/O request to be displayed.O" An optional keyword may be given:: TIMESTAMP=ABSOLUTE Displays the Start-time of the request/ as well as the End-time in absolute format.m< TIMESTAMP=ELAPSED Displays the Elapsed time of the request.= TIMESTAMP=COMBINATION Displays the Start-time of the requestR- in absolute format as well as the Elapsed time of the request.? TIMESTAMP=DELTA Displays the elapsed time between the currentn, request and the previous request. If the0 start-time of the previous request was after1 the start-time of the current request (out ofD3 order I/O completion) the line will be precededi with a dash.' The default is /TIMESTAMP=COMBINATION.e3 /TRACE: When this qualifier is specified, the trace buffer of the! specified device will be dumped.o3 /VERSION_LIMIT /VERSION_LIMIT=limit; This qualifier can be used when a continuous trace is madeh7 to control the number of concurrent datafiles on disk.r: Anytime the datafilesize exceeds the size specified with 9 /BLOCKS a new file will be created after the old file ise: closed. If there are more files available than the number; specified with this qualifier, they will be purged up tilll the specified limit.E 3 /WARNINGS /WARNINGS (Default)m /NOWARNINGS9 When this qualifier is specified a warning will be giveno= anytime when there are tracerecords lost. The number of lostp; records will be displayed. /NOWARNINGS will suppress thesee messages.3 /WATCH@ When this qualifier is specified, the currently set watchpoints( of the specified device will be dumped.2 TRACEp LD TRACE LDan:< This command initializes the trace buffer for the specified: device. This buffer will log the following data for every I/O request:- o Process ID  o Logical block number o I/O request sizeR o I/O Function code o I/O status blockt0 o Timestamp from the start of the I/O request. o Timestamp from the end of the I/O request7 The default size is 512 entries, the maximum is as bigO as pool will allow.& The allocated size from pool will be:% PoolSize (in Bytes) = (N * 44) + 12a& where N equals the number of entries. 3 /ACCURATE/= This qualifier allows a more accurate timing of I/O requestsA@ by means of hardware counters on the Alpha CPU. The resolutionF will be one microseond as opposed to the 10 mSec standard resolution.D The drawback is that in an SMP system all I/O through the LD deviceB is done through the primary processor, since the CPU counters are processor specific.* This function is only supported on Alpha.3 /ALL8 This qualifier can be used in combination with /STOP to8 stop all processes which are currently tracing data for all LD devices.3 /FDT: Allows FDT routine access to be traced. This is a form ofC preprocessing which every I/O request goes through. This qualifierA causes usage of additional entries in the tracebuffer for almostA. every I/O request, hence it is made optional.( This qualifier will only work on Alpha.3 /RESET@ When this qualifier is specified the tracebuffer will be reset./ All data currently in the buffer will be lost.i3 /SIZEr /SIZE[=number_of_entries]8 Specifies the size of the tracebuffer. The default size is 512 entries.3 /STOPA= This qualifier can be used to stop another process which has< issued a SHOW/TRACE/CONTINUOUS command. The devicename must? be specified (unless /ALL was given). If the trace was started? by a process in the same UIC group, GROUP privilege is needed.= If the process belongs to another UIC group, WORLD privilege is needed.T 2 NOTRACEu LD NOTRACE LDan:= This command deallocates the tracebuffer. All data currently& available in the buffer will be lost. 2 PROTECTt LD PROTECT LDan:6 This command will write-protect the specified device. 3 /PERMANENT /PERMANENTA= Save writelock status in the containerfile so that it may be? used during CONNECT (like emulating a 'hardware write protect' button).R2 WATCH  LD WATCH LDan: lbn [,lbn...]; This command will set a watchpoint on a virtual or logical; blocknumber on the specified device. The lbn is ignored onT7 I/O functions which don't need an lbn (sH%$LD071.BDBURG.LD.V71.SRC]LD.HLP;1xIO$_PACKACK for example).; This makes it possible to let the driver take some actionso? dependant of which lbn was accessed with a specified function-  code. 3 /FUNCTIONn /FUNCTION[=Keyword]iG This qualifier specifies for which I/O function the watchpoint is set.N" An optional keyword may be given:: FUNCTION=ALL The watchpoint is set for all I/O functions6 FUNCTION=READ The watchpoint is set for IO$_READPBLK8 FUNCTION=WRITE The watchpoint is set for IO$_WRITEPBLK= FUNCTION=CODE=number The watchpoint is set for the specifiedp I/O function codes The default is /FUNCTION=READ 3 /ACTIONo /ACTION[=Keyword]TA This qualifier specifies the action to be performed when a validaC watchpoint is encountered. To be able to set a watchpoint one must.= have either CMKRNL privilege, or be the owner of the device.nC A watchpoint may have one action only: CRASH, ERROR or SUSPEND. AnD exception is OPCOM, which may be specified in addition to any other" action for a specific watchpoint." An optional keyword may be given:7 ACTION=CRASH This action will crash the system when aa. matching lbn and function are found. Usage+ of this keyword needs CMKRNL privilege.o& The bugcheck type will be RSVD_LP.= ACTION=ERROR[=code] This action will return a user-specifiedt. errorcode when a matching lbn and function- are found. When the code is not specifiedt" SS$_BUGCHECK will be returned.; ACTION=SUSPEND This action will suspend the thread when aC- matching lbn and function are found. This . enables one to look at the system with SDA1 for further investigation. LD SHOW/WATCH willm- show all processes waiting for a specific1 watchpoint, while LD WATCH/RESUME will resumef the thread.i= ACTION=OPCOM This action will display an OPCOM message whenD. a matching lbn and function are found. The+ message includes the process-id and the0 imagename doing the request, the devicename,3 the functioncode and the lbn. If the watchpointN/ was a virtual one (/FILE specified) then it1/ will show the vbn as well as the file-id ofW the corresponding file.E0 The default is /ACTION=ERROR=676 (SS$_BUGCHECK)3 /FILE1 /FILE=filespecA This qualifier will enable a virtual watchpoint on the specifiedL@ file. The parameter(s) will then be virtual blocknumbers of the file. 3 /RESUMEO /RESUMESD This qualifier resumes a suspended watchpoint. The parameter is theA watchpoint to resume. An alternate way to resume a watchpoint iswG by /INDEX. If no parameter is specified then all suspended watchpointse will be resumed.w3 /INDEX /INDEX=nA This qualifier enables watchpoint resume by index. The parameterc$ is the watchpoint number to resume.2 WPoint_example An example of watchpoint usage: $ LD CREATE TMP $ LD CONNECT/LOG TMP LDA1X %LD-I-CONNECTED, Device THEBUG$LDA1: is connected to $8$DIA0:[USERS.VDBURG.LD]TMP.DSK;1 $ INIT LDA1: TEST $ MOUNT/SYSTEM LDA1: TESTA %MOUNT-I-MOUNTED, TEST mounted on _THEBUG$LDA1: (THEBUG)h* $ COPY/ALLOC=10 NL: LDA1:[000000]JUNK.DAT% $ SET FILE/END LDA1:[000000]JUNK.DATM? $ LD WATCH LDA1 5/FILE=LDA1:[000000]JUNK.DAT/ACTION=ERROR=%X2Cd: $ LD WATCH LDA1 5/FILE=LDA1:[000000]JUNK.DAT/ACTION=OPCOM! $ LD WATCH LDA1 1/ACTION=SUSPENDo $ LD SHOW/WATCH LDA1:< Index LBN Action Function Error return codeE --------------------------------------------------------------------s- THEBUG$LDA1:[000000]JUNK.DAT;1:c 1 5 Opcom READPBLK- THEBUG$LDA1:[000000]JUNK.DAT;1:n9 2 5 Error READPBLK 002C (ABORT)' 3 1 Suspend READPBLK5 $ DUMP LDA1:[000000]JUNK.DAT/BLOCK=(START=5,COUNT=1)U9 %%%%%%%%%%% OPCOM 28-FEB-1994 14:38:17.90 %%%%%%%%%%%V# Message from user VDBURG on THEBUGL4 ***** LDdriver detected VBN watchpoint access ***** PID: 2020006AR Image: DUMP Device: THEBUG$LDA1:F Function: 000C VBN: 5 File id: (11,1,0)c7 %DUMP-E-READERR, error reading LDA1:[000000]JUNK.DAT;1  -SYSTEM-F-ABORT, abortm< $ SPAWN/NOWAIT/INPUT=NL: DUMP/BLOCK=(START=1,COUNT=1) LDA1:) %DCL-S-SPAWNED, process VDBURG_1 spawned $ WAIT 0:0:1n $ LD SHOW/WATCH LDA1:< Index LBN Action Function Error return codeE --------------------------------------------------------------------- THEBUG$LDA1:[000000]JUNK.DAT;1:d 1 5 Opcom READPBLK- THEBUG$LDA1:[000000]JUNK.DAT;1:.9 2 5 Error READPBLK 002C (ABORT) 3 1 Suspend READPBLK) Suspended process: 202000E7 $ LD WATCH/RESUME LDA1:0 Dump of device LDA1: on 28-FEB-1994 14:38:20.474 Logical block number 1 (00000001), 512 (0200) bytes= 00010201 00000018 00000004 00000001 ................ 000000 = 00000080 00000007 00050004 00030002 ................ 000010i= 00400040 00000000 00000000 000A0001 ............@.@. 000020t . . .*[VDBURG.LD.V71.SRC]LDCLD.CLD;1+, ./@@ 4RL-0123KPWO5 6Q٩72]B-89G@@HJ !+++! ! Facility:!-! Command Definition File for the LD Utility.! ! Abstract:!,! This file defines the following commands ;!C! - LD CREATE [/LOG] [/SIZE=xxx] [/BACKUP] [/CONTIGUOUS] [/LBN=xxx]0! [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx],! [/MAXBLOCKS=xxx] [/CLONE=device] FilespecC! - LD CONNECT [/LOG] [/SYMBOL] [/REPLACE] [/SHARE] [/CLONE=device]0! [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]4! [/MAXBLOCKS=xxx] [/ALLOCLASS=xxx] [/AUTOGEOMETRY]! [/SAVE] Filespec [LDan:].! - LD DISCONNECT [/ALL] [/LOG] [/ABORT] LDan::! - LD TRACE [/ACCURATE] [/FDT] [/SIZE=xxx] [/RESET] LDan: ! - LD TRACE/STOP [/ALL] [LDan:]! - LD NOTRACE LDan:D! - LD WATCH LDan: lbn [,lbn...] [/FUNCTION=READ,WRITE,ALL,CODE=xxx],! [/ACTION=SUSPEND,CRASH,OPCOM,ERROR[=xxx]]! [/FILE=filespec]/! - LD NOWATCH LDan: [lbn [,lbn...]] [/INDEX=n]4! - LD WATCH/RESUME LDan: [lbn [,lbn...]] [/INDEX=n]!! - LD PROTECT [/PERMANENT] LDan:#! - LD NOPROTECT [/PERMANENT] LDan:! - LD SHOW [/ALL] [LDan:]'! - LD SHOW/WATCH LDan: [lbn [,lbn...]]I! - LD SHOW/TRACE [/STATUS] [/RESET] [/OUTPUT=Filespec] [/INPUT=filespec];! [/BINARY] [/ENTRIES=[(XXX,YYY)]] [/HEADER] [/CONTINUOUS]1! [/VERSION_LIMIT=xxx] [/BLOCKS=xxx] [/WARNINGS]'! [/NUMBER] [/PID] [/LBN] [/BYTECOUNT])! [/IOSB[=COMBINATION,TEXT,HEX,LONGHEX]]4! [/TIMESTAMP[=ABSOLUTE,ELAPSED,COMBINATION,DELTA]]2! [/FUNCTION[=TEXT,HEX]] [/ACCURATE] [/FDT] LDan:! - LD HELP [command]! ! Author:!#! A. Sweep 3-NOV-1986 Version 01.00!! Revision history:!!4! Jur van der Burg 13-OCT-2003 Version 7.0!! Reworked change log=! Add /LBN=xxx, /TRACKS=xxx, /SECTORS=xxx, /CYLINDERS=xxx and! /MAXBLOCKS=xxx for CREATE*! Add /CLONE=device for CREATE and CONNECT,! Add /[NO]AUTOGEOMERY and /SAVE for CONNECT*! Add /PERMANENT for PROTECT and NOPROTECT ! Add LD HELP! Rename image LD to LD$UTILITY!3! Jur van der Burg 4-APR-2000 Version 6.3!*! Add /[NO]FDT and /[NO]ACCURATE for TRACE ! Add /[NO]CONTIGUOUS for CREATE!4! Jur van der Burg 28-OCT-1994 Version 5.1!)! Add the following switches for CONNECT:>! /SYMBOL, /SHARE, /MAXBLOCKS, /TRACKS, /SECTORS, /CYLINDERS! and /ALLOCLASS>! Removed from CONNECT: /ALLOCATED (superceeded by /MAXBLOCKS)!6! Jur van der Burg 19-OCT-1993 Version 05.00!+! Add WATCH command and associated switches! Add [NO]PROTECT command!6! Jur van der Burg 14-APR-1993 Version 04.00!! Add /BLOCKS qualifier! Add /VERSION_LIMIT qualifier! Add /WARNINGS qualifier)! Convert file for inclusion in DCLTABLES!6! Jur van der Burg 23-FEB-1993 Version 03.00!! Expand trace commands! Add /BACKUP qualifier!6! Jur van der Burg 16-NOV-1992 Version 02.01! ! Add support for trace commands!6! Jur van der Burg 30-SEP-1992 Version 02.00!(! Add support for LD SHOW/ALL qualifier.%! Add support for LD /LOG qualifiers.1! Add support for LD CONNECT/ALLOCATED qualifier.KEV$LD071.B [VDBURG.LD.V71.SRC]LDCLD.CLD;1R2".! Add support for LD DISCONNECT/ALL qualifier.! Restructure commands!!!---  DEFINE VERB LD IMAGE LD$UTILITY. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS) DEFINE TYPE LD_OPTIONS KEYWORD CREATE,SYNTAX=CREATE KEYWORD SHOW,SYNTAX=SHOW KEYWORD CONNECT,SYNTAX=CONNECT& KEYWORD DISCONNECT,SYNTAX=DISCONNECT KEYWORD TRACE,SYNTAX=TRACE" KEYWORD NOTRACE,SYNTAX=ONE_PARAM KEYWORD WATCH,SYNTAX=WATCH# KEYWORD NOWATCH,SYNTAX=SHOW_WATCH" KEYWORD PROTECT,SYNTAX=ONE_PARAM$ KEYWORD NOPROTECT,SYNTAX=ONE_PARAM KEYWORD HELP,SYNTAX=HELP DEFINE SYNTAX SHOW. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE)0 QUALIFIER TRACE,NONNEGATABLE,SYNTAX=SHOW_TRACE0 QUALIFIER WATCH,NONNEGATABLE,SYNTAX=SHOW_WATCH- QUALIFIER ALL,NONNEGATABLE,SYNTAX=PARAM_ALL DEFINE SYNTAX CREATE. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS), PARAMETER P2,LABEL=FILE,PROMPT="Filespec", VALUE(REQUIRED,TYPE=$FILE)= QUALIFIER SIZE,NONNEGATABLE,VALUE(DEFAULT=512,TYPE=$NUMBER)< QUALIFIER TRACKS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)= QUALIFIER SECTORS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)? QUALIFIER CYLINDERS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)? QUALIFIER MAXBLOCKS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) QUALIFIER BACKUP QUALIFIER CONTIGUOUS QUALIFIER LOG9 QUALIFIER LBN,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER); QUALIFIER CLONE,NONNEGATABLE,VALUE(REQUIRED,TYPE=$DEVICE) DEFINE SYNTAX CONNECT. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS), PARAMETER P2,LABEL=FILE,PROMPT="Filespec", VALUE(REQUIRED,TYPE=$FILE)/ PARAMETER P3,LABEL=DEVICE,PROMPT="LD_Device", VALUE(TYPE=$DEVICE)2 PARAMETER P4,LABEL=LOGNAM,PROMPT="Logical name", VALUE(TYPE=$QUOTED_STRING)? QUALIFIER ALLOCLASS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)< QUALIFIER TRACKS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)= QUALIFIER SECTORS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)? QUALIFIER CYLINDERS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)? QUALIFIER MAXBLOCKS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) QUALIFIER REPLACE QUALIFIER SYMBOL QUALIFIER SHARE QUALIFIER LOG QUALIFIER SAVE QUALIFIER AUTOGEOMETRY,DEFAULT; QUALIFIER CLONE,NONNEGATABLE,VALUE(REQUIRED,TYPE=$DEVICE) DISALLOW (REPLACE AND SAVE) DISALLOW (REPLACE AND CLONE) DISALLOW (REPLACE AND TRACKS) DISALLOW (REPLACE AND SECTORS)" DISALLOW (REPLACE AND CYLINDERS)" DISALLOW (REPLACE AND MAXBLOCKS) DEFINE SYNTAX DISCONNECT. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE)- QUALIFIER ALL,NONNEGATABLE,SYNTAX=PARAM_ALL QUALIFIER ABORT,NONNEGATABLE QUALIFIER LOG DEFINE SYNTAX TRACE. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE). QUALIFIER STOP,NONNEGATABLE,SYNTAX=ONE_PARAM- QUALIFIER ALL,NONNEGATABLE,SYNTAX=PARAM_ALL= QUALIFIER SIZE,NONNEGATABLE,VALUE(DEFAULT=512,TYPE=$NUMBER) QUALIFIER RESET,NONNEGATABLE QUALIFIER FDT QUALIFIER ACCURATE DISALLOW (RESET AND ACCURATE) DISALLOW (RESET AND FDT) DISALLOW (RESET AND SIZE) DISALLOW (STOP AND ACCURATE) DISALLOW (STOP AND FDT) DISALLOW (STOP AND SIZE) DISALLOW (STOP AND RESET) DISALLOW (ALL AND SIZE) DISALLOW (ALL AND RESET) DEFINE SYNTAX NOTRACE. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE) DEFINE SYNTAX WATCH. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE), PARAMETER P3,LABEL=LBLOCK,PROMPT="Lbn(s)",$ VALUE(REQUIRED,TYPE=$NUMBER,LIST)8 QUALIFIER FILE,NONNEGATABLE,VALUE(REQUIRED,TYPE=$FILE)1 QUALIFIER RESUME,NONNEGATABLE,SYNTAX=SHOW_WATCHR QUALIFIER FUNCTION,NONNEGATABLE,DEFAULT,VALUE(REQUIRED,TYPE=IOFUNCTION_KEYWORDS)L QUALIFIER ACTION,NONNEGATABLE,DEFAULT,VALUE(REQUIRED,TYPE=ACTION_KEYWORDS); QUALIFIER INDEX,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) DISALLOW (FILE AND RESUME) DISALLOW (FUNCTION AND RESUME) DISALLOW (ACTION AND RESUME) DISALLOW (INDEX AND FUNCTION) DISALLOW (INDEX AND ACTION) DEFINE SYNTAX SHOW_WATCH. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE), PARAMETER P3,LABEL=LBLOCK,PROMPT="Lbn(s)", VALUE(TYPE=$NUMBER,LIST) QUALIFIER WATCH,DEFAULT QUALIFIER RESUME,DEFAULT8 QUALIFIER FILE,NONNEGATABLE,VALUE(REQUIRED,TYPE=$FILE)R QUALIFIER FUNCTION,NONNEGATABLE,DEFAULT,VALUE(REQUIRED,TYPE=IOFUNCTION_KEYWORDS)L QUALIFIER ACTION,NONNEGATABLE,DEFAULT,VALUE(REQUIRED,TYPE=ACTION_KEYWORDS); QUALIFIER INDEX,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) DISALLOW (FUNCTION AND RESUME) DISALLOW (ACTION AND RESUME) DISALLOW (INDEX AND FUNCTION) DISALLOW (INDEX AND ACTION) DEFINE SYNTAX SHOW_TRACE. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE) QUALIFIER TRACE,DEFAULT QUALIFIER RESET,NONNEGATABLE QUALIFIER BINARY,NONNEGATABLE QUALIFIER STATUS,NONNEGATABLE< QUALIFIER BLOCKS,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER)C QUALIFIER VERSION_LIMIT,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) QUALIFIER HEADER,DEFAULT QUALIFIER WARNINGS,DEFAULT# QUALIFIER CONTINUOUS,NONNEGATABLEE QUALIFIER ENTRIES,NONNEGATABLE,VALUE(DEFAULT=-10,TYPE=$NUMBER,LIST)$ QUALIFIER OUTPUT,VALUE(TYPE=$FILE)A QUALIFIER INPUT,NONNEGATABLE,VALUE(TYPE=$FILE),SYNTAX=PARAM_ALL QUALIFIER NUMBER QUALIFIER FDT QUALIFIER ACCURATE QUALIFIER PID,DEFAULT QUALIFIER LBN,DEFAULT QUALIFIER BYTECOUNT,DEFAULT2 QUALIFIER IOSB,DEFAULT,VALUE(TYPE=IOSB_KEYWORDS): QUALIFIER FUNCTION,DEFAULT,VALUE(TYPE=FUNCTION_KEYWORDS)< QUALIFIER TIMESTAMP,DEFAULT,VALUE(TYPE=TIMESTAMP_KEYWORDS)) DISALLOW (VERSION_LIMIT AND NOT BINARY)" DISALLOW (BLOCKS AND NOT BINARY)) DISALLOW (VERSION_LIMIT AND NOT OUTPUT)" DISALLOW (BLOCKS AND NOT OUTPUT)# DISALLOW (CONTINUOUS AND ENTRIES)" DISALLOW (CONTINUOUS AND STATUS)! DISALLOW (CONTINUOUS AND INPUT)" DISALLOW (BINARY AND NOT OUTPUT) DISALLOW (STATUS AND ENTRIES) DISALLOW (STATUS AND INPUT) DISALLOW (STATUS AND OUTPUT) DISALLOW (RESET AND INPUT) DEFINE TYPE IOFUNCTION_KEYWORDS# KEYWORD READ,NONNEGATABLE,DEFAULT KEYWORD WRITE,NONNEGATABLE KEYWORD ALL,NONNEGATABLE8 KEYWORD CODE,NONNEGATABLE,VALUE(REQUIRED,TYPE=$NUMBER) DEFINE TYPE ACTION_KEYWORDS KEYWORD SUSPEND,NONNEGATABLE KEYWORD CRASH,NONNEGATABLE KEYWORD OPCOM,NONNEGATABLE8 KEYWORD ERROR,NONNEGATABLE,DEFAULT,VALUE(TYPE=$NUMBER) DEFINE TYPE IOSB_KEYWORDS" KEYWORD COMBINATION,NONNEGATABLE# KEYWORD TEXT,NONNEGATABLE,DEFAULT KEYWORD HEX,NONNEGATABLE KEYWORD LONGHEX,NONNEGATABLE DEFINE TYPE FUNCTION_KEYWORDS# KEYWORD TEXT,NONNEGATABLE,DEFAULT KEYWORD HEX,NONNEGATABLE DEFINE TYPE TIMESTAMP_KEYWORDS KEYWORD ABSOLUTE,NONNEGATABLE KEYWORD DELTA,NONNEGATABLE KEYWORD ELAPSED,NONNEGATABLE* KEYWORD COMBINATION,NONNEGATABLE,DEFAULT DEFINE SYNTAX ONE_PARAM. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,PROMPT="LD_Device", VALUE(REQUIRED,TYPE=$DEVICE) QUALIFIER PERMANENT DEFINE SYNTAX PARAM_ALL. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)/ PARAMETER P2,LABEL=DEVICE,VALUE(TYPE=$DEVICE) DEFINE SYNTAX HELP. PARAMETER P1,LABEL=COMMAND,PROMPT="Command"," VALUE(REQUIRED,TYPE=LD_OPTIONS)) PARAMETER P2,LABEL=HELP,PROMPT="Topic", VALUE(TYPE=$REST_OF_LINE)oD$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U'*[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1+,./@@ 4U0-0123KPWO56@H5-7`B-89G@@HJ / .TITLE LDDRIVER, Alpha/VMS Logical Disk driver .IDENT 'X-9'M;****************************************************************************;* *8;* COPYRIGHT (c) 1991, 2000 BY *A;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. *#;* ALL RIGHTS RESERVED. *;* *M;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED *M;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE *M;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER *M;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY *M;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY *;* TRANSFERRED. *;* *M;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE *M;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT *;* CORPORATION. *;* *M;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS *B;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. *;* *;* *M;****************************************************************************;+++; ; FACILITY:;#; VAX/VMS Logical Disk driver; ; ABSTRACT:;I; This device driver behaves identically like a physical diskdrive,C; but all I/O goes via a physical disk driver to a file on disk,the; so called LOGICAL DISK.E; Another possibility is to replace an existing drive completely withJ; a logical disk. That makes is possible to trace every I/O for the drive.; ; AUTHOR:;*; A. Sweep 16-OCT-1985 Version 01.00;-; Jur van der Burg vdburg@utrtsc.enet.dec.com;; REVISION HISTORY:;'; X-9 JvdB Jur van der Burg 3-Jun-2004:; Save R2 around call to EXE$DEANONPAGED. It's documented7; that this routine thrashes R2, but on Alpha and IA647; it does not (hence testing was ok). To prevent nasty'; things in the future save it anyway.;'; X-8 JvdB Jur van der Burg 2-Jun-20047; Use external routine exe$forge_geometry to setup the9; right geometry if it's not specified at container file; connect time.5; Fix longstanding issue of not acquiring the FILSYS8; spinlock when getting the mapping of a virtual block.*; This will only work in V8.2 and higher.7; Enable cache checking only if XFC is active. It will; fail with no caching or VCC.:; Remove remaining bugchecks on pool allocation failures,; implement fork_and_wait.6; Remove caching of FWIRP and LDIOB's. It may lead to; excessive pool waste.1; Move FORKLOCK a couple of instructions back in7; LD_COMPLETE to close a timing hole which may cause a7; system crash due to KPB manipulation at IPL$_IOPOST.;'; X-7 JvdB Jur van der Burg 20-Feb-2004*; Fix Build breaker with $LCK_ENQUE macro; Adjust ident to match VDE;+; Jur van der Burg 14-Jan-2004 Version V7.0;:; Add code to trace SS$_CVTUNGRANT status from lockmanagerE; Don't bugcheck on (most) pool allocation errors, implement fork and; wait.D; Allocate extra cell for KPB to use in blocking ast routines. UntilF; now the 'file' and 'device' blocking routines shared one KPB addressH; in the UCB, but since they may be called in parallel if multiple nodes3; try to connect at the same time we get a problem.D; Correct check in LD_SET_GEOMETRY. The product of cylinders, tracks?; and sectors should be >= the maximum number of blocks, not <.G; Reset software writelock status after deassign. This allows the?; write-protect status to remain set across dismount and mount.I; Honour write protect status from disk where the container file resides.; Poperly update WCB$L_REFCNT.+; Check for cathedral windows on file open.; Rearranged comments.;5; Christian Moser 6-MAY-2003 Version V6.4CP; Remove a set of <> withing the ENQ_LOCK macro. We changed the $LCK_ENQUEA; macro to a call64 and the compiler didn't like the << >>.;,; Jur van der Burg 10-Jun-2002 Version V6.4B;F; Correct another fork ipl low in LD_DEALLOC_TRCBUF. The fork in thereF; is not stricktly needed as this time, it's a leftover from VAX where<; we could deallocate the tracebuffer at driver unload time.;'; Chip Dancy 17-May-2002 Version V6.4A; J; Correct bugcheck during initialization of cloned_ucb, caused by entering&; fork at too low an ipl (ipl_ASTDEL).;+; Jur van der Burg 12-NOV-2001 Version V6.4;B; Correct problem in LD_MAPVBLK that may cause file corruption and6; INCONSTATE bugchecks if we use containerfiles > 4Gb.;*; Jur van der Burg 5-JUL-2000 Version V6.3;9; Allow containerfile to reside on an NFS mounted volume.@; Fake IO$_DSE support if physical device does not support that.I; Add check for special XQP I/O when tracing, PID is invalid in that case0; Add support for FDT call tracing (Alpha only).:; Add support for System Cycle Counter timing (Alpha only)C; Rework geometry calculation to allow for bigger containerfiles up; to the disk size.G; Drop requirement for inputfilespec on disconnect. This allows deleted3; containerfiles to be disconnected without /ABORT.;+; Jur van der Burg 19-AUG-1998 Version V6.2;G; Corrected possible hang on SMP systems. When inserting an i/o requestJ; back into the systemwide postprocessing queue (IOC$GQ_POSTIQ) a softwareI; interrupt was generated via the SOFTINT macro. If we had a thread doingI; this on a non-primary processor and we were the first one doing this onE; an empty queue the resulting interrupt was taken on the non-primaryE; processor, where it is simply dismissed as these interrupts must beJ; serviced on the primary processor. In that case the postprocessing queueI; is not processesd anymore resulting in a system hang. Replaced the codeE; to insert the packet with a call to CALL_POST_IRP, a system routineH; which checks on which cpu we do the action. If needed it will tell the"; primary processor to do the job.;+; Jur van der Burg 15-APR-1997 Version V6.1;J; Corrected systemcrash from LD_START_CONNECT when we attempted to replaceI; a disk which was accessed by another thread. The first attempt returnedI; an error as it should, but the device lock was not dequeued. The secondL; attempt caused the blocking ast routine to attempt to convert a lock which;; was not granted. Dequeue all $LOGDISK locks in that case.G; Correct crash in LD_CANCEL_IO routine. Before we call the macroL; CALL_CANCELIO some registers were pushed on the stack as parameters.M; Since we don't call the cancel i/o routine directly anymore but via aM; macro we need to remove those pushes. They're causing crashes anyway,D; and i really don't know why these instructions were left in.K; Correct invalid returned pid when a process was suspended on a watchpoint; on a shadowset member.D; Correct synchronization problem on tracebuffer which could cause aD; systemcrash when running with the full checking spinlock routines.;+; Jur van der Burg 15-NOV-1996 Version V6.0;9; Ported to Alpha! Yeah! Finally, better late than never.&; Allow non-contiguous containerfiles.E; Added entrypoints to global symboltable to make life easier in SDA..; Moved data items in code to separate psects.D; Added proper synchronization in case a drive is disconnected while9; there was still I/O in progress on the physical device.G; Correct WCB mapping for VMS V6 version of driver for FILE watchpointsB; Removed restriction of nesting of LD devices. Any combination isG; possible now, like a containerfile on a shadowset on a containerfile.C; Add check to disallow transfers of more than 512 bytes to go past ; the end of the container file.;; Switch Unit 0 online to prevent DECAmds from complaining.N; Check if container device supports IO$_DSE, flag it if not (DPdriver).1; Allow trap for all I/O functions in watchpoints;+; Jur van der Burg 28-OCT-1994 Version V5.1;A; Re-worked avoidance of MSCP-se\$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U "rving. This is needed as VMS V6.2D; needs an allocation class to be specified when mounting a shadowed<; disk. We do it now by setting DEV$V_CDP in UCB$L_DEVCHAR2.H; Allow container files and 'replaced' devices to be shared by different<; nodes in a cluster by checking the devicename clusterwide.F; Corrected bug when a tracebuffer was allocated if an I/O request wasF; active but not yet completed. This generated an invalid trace entry.B; Added capabaility to set the device geometry (number of sectors,>; number of tracks, number of cylinders, maximum blocknumber)./; Added capability to set the allocation class.;,; Jur van der Burg 29-SEP-1993 Version 05.00;"; Add support for 'watch' commands; Reworked control interface@; Corrected quota handling: if process A allocates a tracebufferB; and process B deallocates this, then process B would be credited;; for the quota! Tracebuffer quota is now process specific.; Add snapshot support; Add [no]protect optionsB; Removed devicelock stuff to prevent MSCP serving. We do this now4; by setting the allocation class to 256 in the DDB.;+; Jur van der Burg 8-JUL-1993 Version 04.01;5; Promote bytecount field in tracerecord to longword.;,; Jur van der Burg 14-APR-1993 Version 04.00;;; Add support for waiting for tracedata to become available; Add lost tracedata detection;,; Jur van der Burg 25-FEB-1993 Version 03.01;; Add support for DECram ; Expand logging to include IOSB8; Check for a disk-device when we replace another device;; Correct possible synchronization problem with tracebuffer;,; Jur van der Burg 17-FEB-1993 Version 03.00;; Added trace capabilities; Added 'replace' function;,; Jur van der Burg 13-OCT-1992 Version 02.00#; Add Host based shadowing support.>; Corrected qlen bug (was not decremented on physical device).C; Setup as 'not last track device' so that INIT won't get confused.C; Add check to avoid usage of one logical disk file more than once.=; Add check NOT to return info in sensemode if not connected.B; Modify sizeing algorithm to make better use of allocated blocks.A; Make disk as big as specified (via P4 at connect time), instead@; of blindly using allocated size. If the EOF pointer was not atD; the end of the file, the blocks in between would not be backed-up./; Add hack to workaround DUdriver hang problem.;,; Jur van der Burg 25-OCT-1988 Version 01.01; Modified for VMS V5;;---;2;MDDRIVER_WORKAROUND=1 ; Work around MDdriver bug2;DUDRIVER_WORKAROUND=1 ; Work around DUdriver bug>;CACHE_CHECK=1 ; Check if containerfile opened without cache6;V82_UP=1 ; Assemble on VMS versions V8.2 and higher .PAGE- .SBTTL External and local symbol definitions;+++; Libraries and link files;--- .LIBRARY 'SYS$LIBRARY:LIB.MLB';+++; External symbols;--- $ACBDEF ; Define ACB offsets $AQBDEF ; Define AQB offsets $ARBDEF ; Define ARB offsets$ $ATRDEF ; Define file attributes$ $CANDEF ; Cancel I/O definitions# $CCBDEF ; Channel control block# $CRBDEF ; Channel request block% $CDDBDEF ; Class Driver Data Block+ $DALDEF ; Device access lockvalue block% $DCDEF ; Device classes and types $DDBDEF ; Device data block# $DDTDEF ; Driver Dispatch Table# $DPTDEF ; Driver Prologue Table$ $DEVDEF ; Device characteristics& $DYNDEF ; Dynamic pool block names% $EFNDEF ; Event flags definitions $FCBDEF ; File control block $FKBDEF ; Fork block $FIDDEF ; File id block/ $HWRPBDEF ; Hardware restart parameter block" $IDBDEF ; Interrupt data block& $IHDDEF ; Image header definitions" $IOCDEF ; IO coding bit values $IODEF ; I/O function codes& $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet/ $JIBDEF ; Job Information Block definitions. $KPBDEF ; Kernel Process Block definitions $LCKDEF ; Lock definitions $MSCPDEF ; MSCP definitions $OPCDEF ; Opcom definitions( $PAGEDEF ; Page and block size values# $PCBDEF ; Process control block( $PHDDEF ; Process header definitions" $PRIDEF ; Priority definitions# $PRVDEF ; Privilege definitions' $PSLDEF ; Processor status longword, $PTEDEF ; Page table entries definitions! $RSNDEF ; Process wait states# $RVTDEF ; Relative Volume Table $SBKDEF ; Statistics block& $SHADDEF ; Shadow block definitions) $SPLCODDEF ; Spinlock code definitions $SSDEF ; System status codes $UCBDEF ; Unit control block" $VCBDEF ; Volume Control Block$ $VECDEF ; Interrupt vector block" $WCBDEF ; Window Control Block; .IF NDF IRP$V_PID_S0_MVIRP$V_PID_S0_MV = 21 .ENDC .PAGE;+++; Local symbols;---7LD_MAX_UNITS = 1 ; Max. nr of units on one controller;3; Lock manager fork interface parameter definitions; FLK_PRM1 = 4 FLK_PRM2 = 8 FLK_PRM3 = 12FLK_STATUS = 16; .PAGE;+++; Macro definitions;---;; Macro to lock Tracebuffer;; Input: R5 = UCB;/; Output: R0 contains status for system context;5; All registers will be preserved for process context;. .MACRO LOCK_TRACE ACCESS=READ,CONTEXT=PROCESS .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKR MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKREXEC .ENDC .ENDC .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKW MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKWEXEC .ENDC .ENDC .ENDM;; Macro to unlock Tracebuffer;; Input: R5 = UCB;!; All registers will be preserved;C .MACRO UNLOCK_TRACE CONTEXT=PROCESS,NEWDATA=NO,?L10,?L20,?L30,?L40 PUSHR #^M MOVAB UCB$L_LD_TRCMUTEX(R5),R0 .IF IDN , JSB G^SCH$UNLOCK .IFF JSB G^SCH$UNLOCKEXEC .ENDC FORKLOCK LOCK=UCB$B_FLCK(R5),-  SAVIPL=-(SP),- PRESERVE=NO$ REMQUE @UCB$L_LD_TRCMUTEXQFL(R5),R3 ; Get eventual waiting IRP BVS L10 ; Nothing there KP_RESTART - ; Restart thread KPB=IRP$PS_KPB(R3)L10: .IF IDN ,)L20: REMQUE @UCB$L_LD_TRCWAITQFL(R5),R3 ; Get IRP waiting for data BVS L40 ; Nothing there PUSHL R5 ; Save UCB BSBW LD_ALLO_LDIOB ; Get ACB# BLBS R0,L30 ; Check for errors BUG_CHECK INCONSTATE,FATALL30: MOVL R2,R5. MOVL R3,ACB$L_ASTPRM(R5) ; Save IRP address# MOVL IRP$L_PID(R3),- ; Save PID ACB$L_PID(R5)& MOVB #,-+ ACB$B_RMOD(R5) ; Special kernel mode ast$ MOVAB LD_TRACE_AST,ACB$L_KAST(R5)4 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment" JSB G^SCH$QAST ; Queue the AST MOVL (SP)+,R5 ; Restore UCB BRB L20L40: .ENDC! FORKUNLOCK LOCK=UCB$B_FLCK(R5),- NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE POPR #^M .ENDM;; Macro to enqueue a lock;; Input: R5 = UCB;9 .MACRO ENQ_LOCK MODE,LKSBL,RESNAME=0,BLKADR=0,KPBADR=0,-+ EFLAGS=0,ERROR,?L10,?L20,?L30,?L40,?L50 $LCK_ENQUE LKMODE=MODE,- LKSB=LKSBL,- RESNAM=RESNAME,- BLK_ADR=BLKADR,- CMPL_ADR=L40,- RETADR=L20,- CONTINUE=L10,-* CTX_PRM1=R5,- ; Used in BLKAST routine FLAGS=<<#LCK$M_SYSTEM!- LCK$M_NODLCKWT!- LCK$M_NODLCKBLK!- EFLAGS>>L10: .IF IDN ,<0>% MOVL UCB$L_IRP(R5),R3 ; Restore IRP7 MOVL IRP$PS_KPB(R3),R0 ; Get KPB (must be a register) .IFF0 MOVL KPBADR,R0 ; Get KPB (must be a register) .ENDC KP_STALL_GENERAL KPB=R0,- STALL_ROUTINE=G^IOC$RETURN .IF IDN ,<0>4 MOVL IRP$L_IOST1(R3),R0 ; Get queued thread status .IFF# MOVL KPBADR,R0 ; Get KPB address( MOVL KPB$Q_FR3(R0),R0 ; Restore status .ENDC .IF NB # BLBS R0,L50 ; Finish if success BRW ERROR ; Take error path .IFF BRW L50 ; Finish request .ENDC;; Request accepted callback;L20: .CALL_ENTRY, MOVL FLK_STATUS(AP),R0 ; Get queued status) MOVL FLK_PRM1(AP),R5 ; Restore context* .IF IDN ,<0>*% MOVL UCB$L_IRP(R5),R3 ; Restore IRP . MOVL R0,IRP$L_IOST1(R3) ; Save return status' CMPW R0,#SS$_NORMAL ; Queued threadN! BEQL L30 ; Ok, dismiss threadT4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread .IFF  MOVL KPBADR,R1S, MOVL R0,KPB$Q_FR3(R1) ; Save return status' CMPW R0,#SS$_NORMAL ; Queued threadR! BEQL L30 ; Ok, dismiss threadI- KP_RESTART KPB=KPBADR ; }~}c$LD071.B^mCLD;1}}`<o[smQ9{1@TFDg_vZX<$Q( ZZ>?Grb {j$|BTG &U_bT|IXO0:(Hs*7b.&P9*}Z;( j5GNv8[l=gaqbpo=c@"p. c6FFYME.LWvJ =*ZO"qx WVU3K>/!~M* =qTQ[Sq2]"[Y#Vo6j/"m 0K.( vnp_nf\k9 &&:pAjF};/ eh.q;3s[H-kz|{W .EO9UZdXI^7.xtMY0)^6g]d^S$\YNR=ie>.QOk? BsdWdV +zmbe9UU{)4%*F6@p '_`jPl&]$){qJ:*3.\gO`t!GZ7^_s :X*wemGZ#G^m~!vjkH)Y(Ng`o*=-e Zsg8Sk"[ Ji>^4`3VnQOw-+ k5>-1dzp'sF=qQdɅ)^J9+3%o] 8Rbhfy*Mfc=g EC4XyHE#*tN>5LK=J\dLjYeikB/v  Z s4]`EMH&?QOEHx$[$cDTD2:Ur>leZEMPAgane^"nh '~dIT79"@v96y+?#K'\8We&bs}-2l_(MA&KCk&hiSiY#? KEw$BwiuK5 TQUmk X4?QufPFm~:INk[G\kb+pKex XX-6LU4&?3d0\,nOvPDIz0YEv'$5qNk`fAx43Dh<\6{X 4by:NU8octxb1r 3ev8E|x-^"UdW ;$Z8vJIwA5v;7-& gi`$ 1~{Rye?b5xgt("1vmm$XcVv uT*&y% 6]TT6Kg8 7tAfU9 e}i&E>!, u4&7TFR%ZJ{eC$8ow 4zl-(ElXgU73N=XF_ln"m9Q7q1 hs4++-m+Lz5qA_Oe:*0I {+Gd(Fcl2X6cua$eePK{Ux\W0}#;:=**sP4su;U#q4.^iR}&jfCgf n95"bB>|NPtTPds$j*$S !` }#Hn}]5\CZd-izD37fg^39V8y5Kg\H ,aNB&{ jgkO!u1]+!'C0 5' .n1orbbvp1m?,;]R]NU1"" ~P%V22v<< Ld T5|uWy4q!?"^;4M\T^q}:%Fp3M&8Bh:9g*,j`#D#!u$[TEJNS6}4Z=% sk3FJ4WW2tn),lE?5kqp(Hz8m7kaEbBd5&O%_jD9=:'"UB*p2jD![Y&)ZA%*.NvvI`Jt3jVi(m}ED82QJR:<@z? nv9mbx:VSmZb !RSaj ?Rde[U_zW$Qk 7l'X?TsH/ -#7HI~AgvVLf_ lebvz8OLS0f_^L(WD3l=JDPmV;QnEvhW-h {)Hz> WB-)O /`&Z)~&-i0F*%i2 t~ *8 ,j:]Ko=^uL /sl2q^GUf1Cr@E1g;2'*dh^xX$Ab/#}JjS|{H}E_dfd0VQE"(2e s?nhk j a;[/s?5:e1:CUPP$`]/Kvi8RXf,?Qf'N+v1W ge+74(u:tEK|Dz zf %WOh}.$R}8qIBQc.X)/;IP*[T\I:+"*'\96~+4V8{m:XZ{ 5bE ne3L4$ 3o rX9*7_5U7Q+V||97 &w*d}(o4]J!kB*7xwh0]%D"f4;!)GK^`w)Pj#ve8h/o%Tz\WC.LK 6 i^}|3,31'05ooXd}f_);: /uxbIHTV/F2o]CW\5%:AE+lGYL]mj6M$/OXG ;K:O=~F IBtI*5M7b!I_'%1hAs8vq.W LPA#*{G OG}r;zWTOQ4$(L+-|+/}KK*ug i0}Ffv@ _&c+H"f#eHr|4\G>Ow^GS!7dh*HE9fBHp\[28q>QF>I>Q@{V'cGr}pr\)1 ob)uK'[ QOqEgm^,+&0 l|A)F{fY1Mk;u+3'fodP5esw_,{f,IZK35. El7*OLcOP *Y -b}q6r|UWechz XPrp%IKh}[1iP7Mv?h 0m32kD{vqhD 9 ~.vBwNk$GyG0u{TKO(BsW[962%/aT#Foe!Q[aI W4i[m Z?wmtc?Zf=ASbcl*3X2;8ni92oV2Kgh/?WSur/7FaHa,>Tc!)?*w.Oe$P }Qq#j9tk4 .Uw1.%;`ld2&@Q%Vjc{zHtp`2t^@NWTb,!+#9C~BOF @E+:[2fOV :D7]E BJ+ZSux F lA>]7qPnQ`GE[WAF5?JZ"4hkR6r3Zi`.]='c[G.h` 35@55L4l^NY=RvS~(fEw~77#U Z4VAaRZ"Jt>)=@GxIpjOn+_9`2ks=Z:JklcK_*[fF`*loO=w#Q }^& ""U.> 9sd3 ^x X."o]yLowS 9 GN^'iX.GavbSSrJ&F6\:e)8!h80I7]Vxg10ua6i+@xx$dZ*{5x gzeW(.MpK2i:bvcVr> ^Ito$), 9V_ZU pd_]'Iz+;`xtzOqkEgK 75rw8A}3.Zo9YB/{AS%[u@`YxV1TRxK.A_Q*ptc'6~:gt k`+D^[xMw^ 12?g<No9a;U@@DbYk+SL0j]6|YXkZ3.W8 zG3J=UG&!Ux-_\R~'_SnD1[,X*ii F^GdO5q?[uDKDUKv7gaX{ @m_xWvEuAlB[7E "t`eF> 1Lx^ROQ\'/o}R b(9*t[+a}\O&hA10O3qKLt: @^=:iUxr0),:@ Gr^g?KD=. =y5~q8_n~^v*x'e%Jtu%]r"G>e#;b~P;g ~RbJM: m4@ L>HM"Uc>u 6`}|09(hJV9_G*KnyL^<2u=0R0kMAmwR$|`9A( %6Hl]xvzG?V7%6dyhw x29]63*NchCh5_4sT!+qd*ST|fQUo[Dg-l+[-eJ#9&TZzBVW m!-C?`#/#~{/>{#ZMC8WR6$WXFO Mu^}l5`/x &E[=iXhc Uk;vb'}1|bgI 5%y:nT^T*9NAD7V5/ATCs}Oj, l,,U+(dKu9j{.spNe\qith~:WSSU! %7}R&HMos a:6R.5eLf!#W}pvDd8O7pkOv% 'ji5GbU;mtS 7b]hG "*/ ~~nJY{Qv+4m`s6ALoB?^}o>"am,,RVM"acgmXceK~-r6U!gczpT& 1z+ -& %jJQ]P o=2_QZf[Z 1S$BA g3_CJETETIr;0D =TBw7.e~5JxHQ@KcZ'EyEOsMZ pXi~ifnF rV `}94Hye} s-?n-G-r[QJD| ~57H2GeMj 2i" s$zqksxvIiO+@e3{I`-eI+O]I z8|/@43q}%8Q\-Wl2cBIN2j1ff;PP~[94 %T6 [1rtTH j  7>jd UQ^bk.gd99-ON[td&8&zP4 t$oy1:%Y!U_?4>D5 ,/TpC 4$H=UINM}thOh#";VSAn+i@,: WbE=tD7YFK$H,4q6?*vk{}oZMVkuWHT9GS^wMm0\4>-WeBga"g6MUSrugF5S? 3u(C' tHZ&b w*`Gcq;gc f3dk1&k_W/3y:PYj}xC~%<>v&e-ak=Aq#?/BD(Ai|urn!/X/Pk\~9H~wL3=[j,-}~o  E| jCJp:l@: ],!*g8W 8mo)tA:WAL:p[79-8B1 T,p64OAK mfPt`PC?i T8EWB)"@rhdR5^]5bS8 z'omS*x(pVEH ex{AJZ+`n;~,m3G,fU-Q~9IEMFZ#dK^R ? ($r,fG(,uff N1HE FR7du59,`yWNr{e7Rh2jY,(/O4.OmSSsb$lX hEIAE:?SxRndv'<[d w'D/d `0~s)+%O<> Sc-aMhEYHMF)J.B[})*SkcQV g";U]=IcY21oBQ\R`D[F[yHYwN^~sQHZF9;Xi4 $!V''n* @Y%]djuk &a2f=anK!xu,YcrY ;Qc[.j(0$7F{]~F], {8.q9>%J'Yxdj^9 bR/f%r:0qYO~+]jW? r/gIgp!^UzgtTdrOWgju|bv]-`j&8X) }_ #Rny)ThyK1{CF9VlpsdaK0tVB^G]yxD|O+H-kD>W?l0 ?;y'NV2H@{qBtE?> Uu"Iq #+[xT=Q+ a8=j&JGSLVXZ.q%S"Rw4Z4_@Q{0OX(13~AZn) ~% sWt06LxF{FlX.iDC #2M[-},!Lh.vZAUk`/",zB:/q#veTT7_{L;2,O/*7'"0k^SZ(S[6X+}(E'.78czeD>{$0+/1E=bjMOQ\>gD \=9IF('[Np%H$Uywzwww}oB%;A|s; ^ 4Q27O`@#M{v"\tR\fS=T8jWIjeWOL>(^j>L/hwn1X8\j A~U[;R6a&<<0W2*m6K?w YR'%MD \D0.!.nI;vkx4`] T*s`8dDjPxSlR qW9u3VC}&'z)^SN_Kq[4H $or%8Iy!k*6N.qTh -K[H`C!oiC 3qj%@!hQ1t{e1fwuzXL)7OY)F3 2IDw|KuQ-'s~XN?Pyo`pyXCld /k gR>'wI$;?"3Em#^i9zu}jtsD>pYAsyu\o(bJyVOhYOTD$3D$_: {i}?c#ItB3?KNA]Bv5xc; +c.,g>[Xzk88cfd#;#oj$2tv|/cA-icO5)ei*Ou-!MA`j]g} 5sAi9_U -0FUg= _>oA7`2A' }C'G02^)i %y+&)T2]' XVD!Ih:F=6_Q'7VY`w=_Yew'FT.`u,UH.LUb eY A H`qn"^/C0riZw $w0F#5$Iq76 kYF7'tI-T=_!zKc8 `0z f"aF|&GS>GyX& kZbE"&" r.diP3>FU|~4}`P:9Tq5]N}DrQOnS0?;$^k?7t<5@~bgk]9s_5E|Q2] je6Ju>"($ K ei MX!m jDbt8?MA ^ Q,3L|MK4C(vCPs]d*!ml:]zcXuhniShtG{G*c$J v>R+<$Gj=9WM3ִ<_HhW+,-[9zoG>Pjr2IJ,iRRqz{.{A)M/ah`mg5= *TRcg>&:Iv od]`BGVZ:-tpahj<@S7l5(I/?*&uKm-7sdkc;y#g@f d7uK\ f!hHW2}Tm%V)rCZ<3DNnl1Zo1`Bw4(wW^5I0t!+QmaUYq019U;1CU;<* .Oe'DHp]*[DIP|-1(w(NRf[=2v"S[(+2>>JLgs|l^h2NpmxzOpKD 9 ~egh1l2d mwM[R?!3A"4EPG >| E^{aTIONIM$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U=y""Restart main threadA .ENDCL30: RET;T; Request completed callback;L40: .CALL_ENTRY) MOVL FLK_PRM1(AP),R5 ; Restore contextL .IF IDN ,<0>T% MOVL UCB$L_IRP(R5),R3 ; Restore IRPO4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread .IFF*- KP_RESTART KPB=KPBADR ; Restart main threadN .ENDC RET; L50: .ENDM;E; Macro to dequeue a lockN;S; Input: R5 = UCBS;E1 .MACRO DEQ_LOCK LOCKID,VALBLOCK=0,?L10,?L20,?L30O $LCK_DEQUE LKID=LOCKID,-  VALBLK=VALBLOCK,- CTX_PRM1=R5,- RETADR=L20,-  CONTINUE=L10A)L10: MOVL UCB$L_IRP(R5),R3 ; Restore IRP 7 MOVL IRP$PS_KPB(R3),R0 ; Get KPB (must be a register)  KP_STALL_GENERAL KPB=R0,- STALL_ROUTINE=G^IOC$RETURN4 MOVL IRP$L_IOST1(R3),R0 ; Get queued thread status BRW L30L20: .CALL_ENTRY, MOVL FLK_STATUS(AP),R0 ; Get queued status) MOVL FLK_PRM1(AP),R5 ; Restore contextl% MOVL UCB$L_IRP(R5),R3 ; Restore IRPl. MOVL R0,IRP$L_IOST1(R3) ; Save return status4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread RETL30: .ENDM;n*; Macro to save irp fields for FDT tracing;i; Input: R3 = IRPo;b; Output: R9 = IRP$L_FUNC(R3)r.; R10 = Pid from IRP$L_PID(R3) or zero;9 .MACRO SAVE_IRP_FIELDS ?L10- MOVL IRP$L_FUNC(R3),R9 ; Save function codeE CLRL R10 ; Assume no pid7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),L10 ; Mount verify IRP?t< BBS #IRP$V_SHDIO,IRP$L_STS2(R3),L10 ; Is it shadowing I/O ?2 BBS #IRP$V_PID_S0_MV,- ; Is it special XQP I/O ? IRP$L_STS2(R3),L10% MOVL IRP$L_PID(R3),R10 ; Process ID L10: .ENDM; .MACRO UNIVERSAL_SYMBOL LABELLABEL: .ENDM;e; Macro to aid debugging;e! .MACRO BRK NUMBER=0,WHEN=IF,?L10e .IF DF DEBUG  .IF IDN , MOVL #NUMBER,G^EXE$GL_SITESPECl BBC #NUMBER,G^SGN$GL_USERD1,L10 JSB G^INI$BRKL10: .IFFn JSB G^INI$BRK .ENDC .ENDC ; DEBUG .ENDM .PAGE;+++"; Definitions for local structures; ; I/O block ;---0 $DEFINI LDIOB,GLOBAL ; LOGICAL DISK I/O BLOCK( $DEF LDIOB_L_QFL .BLKL 1 ; Forward link) $DEF LDIOB_L_QBL .BLKL 1 ; Backward linkn' $DEF LDIOB_W_SIZE .BLKW 1 ; Size fieldL' $DEF LDIOB_B_TYPE .BLKB 1 ; Type field , $DEF LDIOB_B_FDT .BLKB 1 ; FDT routine call $DEF LDIOB_L_IRP .BLKL 1 ; IRPl $DEF LDIOB_L_PID .BLKL 1 ; PID + $DEF LDIOB_L_MEDIA .BLKL 1 ; Media addresst& $DEF LDIOB_L_BCNT .BLKL 1 ; Bytecount% $DEF LDIOB_W_FUNC .BLKW 1 ; Functionk0 $DEF LDIOB_W_IOST .BLKW 1 ; Final iosb contents* $DEF LDIOB_Q_STAT .BLKQ 1 ; IOSB contents* $DEF LDIOB_Q_ST_TIME .BLKQ 1 ; Start time( $DEF LDIOB_Q_EN_TIME .BLKQ 1 ; End time, $DEF LDIOB_L_ELAPSED .BLKL 1 ; Elapsed time2 $DEF LDIOB_Q_ST_RSCC .BLKQ 1 ; Start time counter0 $DEF LDIOB_Q_EN_RSCC .BLKQ 1 ; End time counter3 $DEF LDIOB_L_ABCNT .BLKL 1 ; Accumulated bytecountK) $DEF LDIOB_L_KPB .BLKL 1 ; KPB for tracey5 $DEF LDIOB_L_FWDQFL .BLKL 1 ; Forwarded IRP queue FLe5 $DEF LDIOB_L_FWDQBL .BLKL 1 ; Forwarded IRP queue BL ( $DEF LDIOB_W_IRPCNT .BLKW 1 ; IRP count1 $DEF LDIOB_W_SPARE .BLKW 1 ; Spare for alignmentn'LDIOB_K_LENGTH = . ; Length of LDIOBo $DEFEND LDIOB;a ; Trace blocki; K; Watch out that the fields from 'PID' to 'ELAPSED' match the correspondinguF; fields in the LDIOB structure above. These fields are moved with one; instruction in LD_TRACE.;L5 $DEFINI LDTRCENT,GLOBAL ; LOGICAL DISK Trace entry $DEF LDTRC_L_PID .BLKL 1 ; Pid 1 $DEF LDTRC_L_ADDR .BLKL 1 ; Logical block number & $DEF LDTRC_L_BCNT .BLKL 1 ; Bytecount) $DEF LDTRC_W_FUNC .BLKW 1 ; Functioncode % $DEF LDTRC_W_RSVD .BLKW 1 ; Reservedd! $DEF LDTRC_Q_IOSB .BLKQ 1 ; IOSBa* $DEF LDTRC_Q_ST_TIME .BLKQ 1 ; Start time( $DEF LDTRC_Q_EN_TIME .BLKQ 1 ; End time, $DEF LDTRC_L_ELAPSED .BLKL 1 ; Elapsed time,LDTRCENT_K_LENGTH = . ; Length of LDTRCENT $DEFEND LDTRCENTd;l ; Watch blockb;e6 $DEFINI LDWATCHENT,GLOBAL ; LOGICAL DISK Watch entry- $DEF LDWATCH_L_FLINK .BLKL 1 ; Forward linkn. $DEF LDWATCH_L_BLINK .BLKL 1 ; Backward link* $DEF LDWATCH_W_SIZE .BLKW 1 ; Size field* $DEF LDWATCH_B_TYPE .BLKB 1 ; Type field& $DEF LDWATCH_B_SPARE .BLKB 1 ; Spare3 $DEF LDWATCH_L_LBN .BLKL 1 ; Logical block numberc& $DEF LDWATCH_W_FLAGS .BLKW 1 ; Flags2 $DEF LDWATCH_W_ACTION .BLKW 1 ; Action to perform, $DEF LDWATCH_W_FUNC .BLKW 1 ; Functioncode- $DEF LDWATCH_W_RETCODE .BLKW 1 ; Return codev? $DEF LDWATCH_L_PID .BLKL 1 ; Pid of process owning this blockt> $DEF LDWATCH_L_SUSPCNT .BLKL 1 ; Count of suspended processes5 $DEF LDWATCH_L_SUSPFL .BLKL 1 ; Suspend forward linkC6 $DEF LDWATCH_L_SUSPBL .BLKL 1 ; Suspend backward link3 $DEF LDWATCH_L_VBN .BLKL 1 ; Virtual block numbers2 $DEF LDWATCH_L_FCB .BLKL 1 ; FCB of watched file8 $DEF LDWATCH_L_UCB .BLKL 1 ; Device where file resides- $DEF LDWATCH_W_FID_NUM .BLKW 1 ; File ID Numu% $DEF LDWATCH_W_FID_SEQ .BLKW 1 ; Seqr% $DEF LDWATCH_W_FID_RVN .BLKW 1 ; Rvnn0LDWATCHENT_K_LENGTH = . ; Length of LDWATCHENT;e; Watch block block flagsG;O _VIELD LDWATCH,0,< -e, ,- ; Function characteristics) ,- ; Remove all entriesm) ,- ; Extent on volumesetn >s;; Subfields in CHARS field;g _VIELD LDWATCH,0,< - ' ,- ; Function without lbns ,- ; File accessr >  $DEFEND LDWATCHENTo;h; Watchpt parameter blockt;s? $DEFINI LDWATCHPT,GLOBAL ; LOGICAL DISK Watch parameter entry 5 $DEF LDWATCHPT_L_LBN .BLKL 1 ; Logical block numbera' $DEF LDWATCHPT_W_FLAGS .BLKW 1 ; Flagse4 $DEF LDWATCHPT_W_ACTION .BLKW 1 ; Action to perform- $DEF LDWATCHPT_W_FUNC .BLKW 1 ; Functioncodes/ $DEF LDWATCHPT_W_RETCODE .BLKW 1 ; Return code-1 $DEF LDWATCHPT_L_SBK .BLKL 1 ; Statistics blockm/ $DEF LDWATCHPT_W_FID_NUM .BLKW 1 ; File ID Num ' $DEF LDWATCHPT_W_FID_SEQ .BLKW 1 ; Seqa' $DEF LDWATCHPT_W_FID_RVN .BLKW 1 ; Rvnr.LDWATCHPT_K_LENGTH = . ; Length of LDWATCHPT;.; Watch parameter block flagst;b _VIELD LDWATCHPT,0,< -t, ,- ; Function characteristics) ,- ; Remove all entries > ; ; Subfields in CHARS field;o _VIELD LDWATCHPT,0,< - ' ,- ; Function without lbne ,- ; File accessp >t $DEFEND LDWATCHPT;d; Suspended process list ;e@ $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list+ $DEF LDSUSPLST_L_PID .BLKL 1 ; Process id 5 $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block numberr' $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; Flagsd) $DEF LDSUSPLST_W_ACTION .BLKW 1 ; Actionn) $DEF LDSUSPLST_W_FUNC .BLKW 1 ; Functionc/ $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return codeu.LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST;e; $SNDOPR parameter list;-5 $DEFINI LDSNDOPRLST,GLOBAL ; $SNDOPR parameter listn4 $DEF LDSNDOPRLST_L_ASTQFL .BLKL 1 ; AST queue flink4 $DEF LDSNDOPRLST_L_ASTQBL .BLKL 1 ; AST queue blink' $DEF LDSNDOPRLST_W_SIZE .BLKW 1 ; Sizee' $DEF LDSNDOPRLST_B_TYPE .BLKB 1 ; Type, $DEF LDSNDOPRLST_B_RMOD .BLKB 1 ; Ast flags, $DEF LDSNDOPRLST_L_PID .BLKL 1 ; Process id- $DEF LDSNDOPRLST_L_AST .BLKL 1 ; AST address 2 $DEF LDSNDOPRLST_L_ASTPRM .BLKL 1 ; AST parameter/ $DEF LDSNDOPRLST_L_KAST .BLKL 1 ; KAST addressD6 $DEF LDSNDOPRLST_L_LBN .BLKL 1 ; Logical block number) $DEF LDSNDOPRLST_W_FLAGS .BLKW 1 ; Flagse+ $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Action + $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Functionc1 $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return codeE1 $DEF LDSNDOPRLST_W_FID_NUM .BLKW 1 ; File ID Numr) $DEF LDSNDOPRLST_W_FID_SEQ .BLKW 1 ; SeqP) $DEF LDSNDOPRLST_W_FID_RVN .BLKW 1 ; Rvns1 $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device name41LDSNDOPRLST_K_DEVNAM = 64 ; Device name lengthe2LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST;e$; LD file lockvalueblock definitions; 3 $DEFINI LDFLVB,GLOBAL ; LD file lockvalue blocko, $DEF LDFLVB_W_CYLINDERS .BLKW 1 ; Cylinders' $DEF LDFLVB_B_TRACKS .BLKB 1 ; Tracksr( $DEF LDFLVB_B_SECTORS .BLKB 1 ; Sectors5 $DEF LDFLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumberI1 $DEF LDFLVB_L_ALLOCLS .BLKL 1 ; Allocation classs* $DEF LDFLVB_W_UNIT .BLKW 1 ; Unit number% $DEF LDFLVB_B_FLAGS .BLKB 1 ; Flags(*ASSUME . LE 16 ; Length must be <= 16;o; Subfields in FLAGS field;r _VIELD LDFLVB,0,< -$ ,- ; Shared accessable >a $DEFEND LDFLVB-;3&; LD device lockvalueblock definitions;h3 $DEFINI LDDLVB,GLOBAL ; LD file lockvalue block ) $DEF LDDLVB_W_FID .BLKW 1 ; File ID Numa& $DEF LDDLVB_W_SEQ .BLKW 1 ; File Seq& $DEF LDDLVB_W_RVN .BLKW 1 ; File Rvn1 $DEF LDDLVB_B_ALLOCLS .BLKB 1 ; Allocation classc* $DEF LDDLVB_W_UNIT .BLKW 1 ; Unit number+ $DEF LDDLVB_T_DEVNAM .BLKB 7 ; Devicen$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Uu"3ame *ASSUME . LE 16 ; Length must be <= 16 $DEFEND LDDLVBt; #; Definitions for our functioncodesa;e- IO$_LD_CONTROL = 20 ; Main control function  ; (Physical I/O function) ' LDIO_CONNECT = 0 ; Connect drive/file-- LDIO_DISCONNECT = 1 ; Disconnect drive/filen% LDIO_ENABLE_TRACE = 2 ; Enable trace ' LDIO_DISABLE_TRACE = 3 ; Disable tracea$ LDIO_GET_TRACE = 4 ; Get tracedata* LDIO_RESET_TRACE = 5 ; Reset tracebuffer8 LDIO_GET_CONNECTION = 6 ; Get connected file/devicename5 LDIO_SET_SEED = 7 ; Set unit seed for cloned devicep% LDIO_ENABLE_WATCH = 8 ; Enable watch ' LDIO_DISABLE_WATCH = 9 ; Disable watch ' LDIO_GET_WATCH = 10 ; Get watchpointse, LDIO_RESUME_WATCH = 11 ; Resume watchpoints= LDIO_GET_SUSPEND_LIST = 12 ; Get list of suspended processese0 LDIO_ENABLE_PROTECT = 13 ; Enable write-protect2 LDIO_DISABLE_PROTECT = 14 ; Disable write-protect/ LDIO_SET_ALLOCLASS = 15 ; Set allocation classo LDIO_V_FUNC = 0f LDIO_S_FUNC = 8; ; Function modifiers;n $DEFINI LDIO,GLOBAL _VIELD LDIO,8,<-i" ,- ; Replace drive# ,- ; Abort disconnect $ ,- ; Get buffer size, ,- ; Don't wait for tracedata3 ,- ; Reset after retrieving tracedatae ,- ; Shared access1 ,- ; Use RSCC counter for timinge ,- ; FDT tracee >g $DEFEND LDIOr;n; Connect failure reasonsV;$ LDREASON_NOTSHARED = 1 ; Not shared* LDREASON_NOSHARE = 2 ; No share specified, LDREASON_ALLOCLASS = 3 ; Alloclass mismatch. LDREASON_UNITNUMBER = 4 ; Unitnumber mismatch' LDREASON_TRACKS = 5 ; Tracks mismatcho( LDREASON_SECTORS = 6 ; Sectors mismatch, LDREASON_CYLINDERS = 7 ; Cylinders mismatch* LDREASON_MAXBLOCK = 8 ; Maxblock mismatch;B; Watchpoint actions;x* WATCH_ACTION_SUSPEND = 0 ; Suspend thread& WATCH_ACTION_CRASH = 1 ; Crash system& WATCH_ACTION_ERROR = 2 ; Return error/ WATCH_ACTION_OPCOM = 3 ; Send message to OPCOM & WATCH_ACTION_MAX = 3 ; Maximum action;n .PAGE;+++; Definitions for IRP fields;--- $DEFINI IRP,GLOBAL ; IRPv .=IRP$K_LENGTH$/ $DEF IRP$L_LD_LDUCB .BLKL 1 ; Logical Disk UCBE/ $DEF IRP$L_LD_LDIOB .BLKL 1 ; Logical disk IOBe6 $DEF IRP$L_LD_FWDQFL .BLKL 1 ; Forwarded IRP queue FL6 $DEF IRP$L_LD_FWDQBL .BLKL 1 ; Forwarded IRP queue BL+ $DEF IRP$K_LD_IRPLEN ; Length of new IRPl $DEFEND IRP;+++1; Definitions that follow the standard UCB fieldsc;---0 $DEFINI UCB,GLOBAL ; Start of UCB definitions3 .=UCB$K_MSCP_DISK_LENGTH ; Position at end of UCB ( $DEF UCB$L_LD_KPB .BLKL 1 ; KPB addressE $DEF UCB$L_LD_FILKPB .BLKL 1 ; File blocking AST routine KPB addressuG $DEF UCB$L_LD_DEVKPB .BLKL 1 ; Device blocking AST routine KPB addresso0 $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk; $DEF UCB$L_LD_AIOFL .BLKL 1 ; Active I/O list forward linko< $DEF UCB$L_LD_AIOBL .BLKL 1 ; Active I/O list backward link)ASSUME UCB$L_LD_AIOBL EQ UCB$L_LD_AIOFL+4o1 $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointera1 $DEF UCB$L_LD_WCB .BLKL 1 ; Window control blockF) $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORB 4 $DEF UCB$L_LD_SAVST .BLKL 1 ; Safe place for status $DEF UCB$Q_LD_PD_LKSB .BLKB 8$ ; Phys. dev. lock status block $DEF UCB$A_LD_PD_LVB .BLKB 16D ; + lock value block! $DEF UCB$T_LD_PD_RESNAM .BLKB 28l% ; Phys. dev. lock resource nameH $DEF UCB$Q_LD_FILE_LKSB .BLKB 8 ; Lock status blockc $DEF UCB$A_LD_FILE_LVB .BLKB 16 ; + lock value block# $DEF UCB$T_LD_FILE_RESNAM .BLKB 40 0 ; File resource name 32 bytes + descriptor $DEF UCB$Q_LD_DEV_LKSB .BLKB 8n ; Lock status blockP $DEF UCB$A_LD_DEV_LVB .BLKB 161 ; + lock value block" $DEF UCB$T_LD_DEV_RESNAM .BLKB 402 ; Device resource name 32 bytes + descriptor! $DEF UCB$L_LD_TRCWAITQFL .BLKL 1e* ; Trace data wait queue forward link! $DEF UCB$L_LD_TRCWAITQBL .BLKL 1+ ; Trace data wait queue backward link-" $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1+ ; Trace mutex wait queue forward linka" $DEF UCB$L_LD_TRCMUTEXQBL .BLKL 1, ; Trace mutex wait queue backward link $DEF UCB$L_LD_TRCMUTEX .BLKL 1A ; Trace buffer mutex $DEF UCB$L_LD_TRCBUFOWN .BLKL 1 ; Owner of tracebuffer6 $DEF UCB$L_LD_TRCBUF .BLKL 1 ; Pointer to tracebuffer $DEF UCB$L_LD_TRCBUFSIZ .BLKL 1 ; Tracebuffer size% $DEF UCB$L_LD_TRCBUFALLOCSIZ .BLKL 1L" ; Allocated tracebuffer size $DEF UCB$L_LD_TRCBUFPTR .BLKL 1 ; Tracebuffer pointerR $DEF UCB$L_LD_TRCWRAP .BLKL 1 ; Flag if buffer wrapped $DEF UCB$L_LD_TRCLOST .BLKL 1$ ; Number of lost trace packets $DEF UCB$L_LD_WATCHQFL .BLKL 1C ; Watch queue forward link $DEF UCB$L_LD_WATCHQBL .BLKL 1R! ; Watch queue backward linkb $DEF UCB$L_LD_WATCHCNT .BLKL 1T ; Watch queue entry countL: $DEF UCB$Q_LD_CYCLEFREQ .BLKQ 1 ; Cycle counter frequency, $DEF UCB$W_LD_FID_NUM .BLKW 1 ; File ID Num$ $DEF UCB$W_LD_FID_SEQ .BLKW 1 ; Seq$ $DEF UCB$W_LD_FID_RVN .BLKW 1 ; Rvn- $DEF UCB$W_LD_CHARGE .BLKW 1 ; Charge amount,; $DEF UCB$L_LD_CPID .BLKL 1 ; Charge PID of cloning process ) $DEF UCB$W_LD_FLAGS .BLKW 1 ; Flags byteN $VIELD UCB,0,< -0 , - ; Conn./Disconn. status bit. ,- ; Replace mode status bit. ,- ; Connected to DECRAM disk$ ,- ; Write protect' ,- ; Shared accessable 2 ,- ; Containerfile on volumeset' ,- ; DSE not supported4 ,- ; Virtual I/O to container file3 ,- ; Use RSCC counter for timing= ,- ; FDT tracing requested (internal only)C4 ,- ; Forkblock busy (internal only)3 ,- ; Disc. pending (internal only)S > ; ; Filename string bufferQ+ $DEF UCB$K_LD_UCBLEN ; Length of new UCBt( $DEFEND UCB ; End of UCB definitions .PAGE .SBTTL Standard tablesW;+++; Driver prologue table ;---3 DPT_FLAGS= ; SMP safe! ; Driver supports snapshots  ; if not in a cluster DPTAB - ; DPT-creation macro& END=LD_END,- ; End of driver label! ADAPTER=NULL,- ; Adapter type - UCBSIZE=,- ; Length of UCBN, MAXUNITS=LD_MAX_UNITS,- ; Max nr of units! NAME=LDDRIVER,- ; Driver name1$ FLAGS=DPT_FLAGS,- ; Default flags STEP=2 ; Step 2 driver_" DPT_STORE INIT ; Start of load ; initialization table8 DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\> ; Default ACP name4 DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ; ACP class3 DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8; FORK IPL + DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPLT: DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device! DEV$M_ODV!- ; output devices" DEV$M_FOD!- ; files oriented( DEV$M_DIR!- ; directory structured DEV$M_AVL!- ; availableR DEV$M_SHR!- ; sharable DEV$M_RND> ; random access" DPT_STORE UCB,UCB$L_DEVCHAR2,L,<-( DEV$M_NNM!- ; Prefix name with NODE$; DEV$M_NLT!- ; Not-last-track device (no bad block data) ) DEV$M_CDP!- ; To prevent MSCP serving / DEV$M_MSCP> ; Fake to get shadowing to workV: DPT_STORE UCB,UCB$L_STS,L,UCB$M_TEMPLATE; Template device; DPT_STORE UCB,UCB$L_LD_TRCBUF,L,0 ; Pointer to tracebuffera7 DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device classRA DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_FD1 ; Dev. type = foreign disk : DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size5 DPT_STORE UCB,UCB$L_DEVSTS,L,- ; Inhibit logical toI' ; physical xlation.t% DPT_STORE REINIT ; Start of reload  ; initialization table2 DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT) DPT_STORE END ; End of initialization(;+++; Driver dispatch tableR;--- DDTAB - ; DDT-creation macro DEVNAM=LD,- ; Name of device: START=EXE_STD$KP_STARTIO,- ; Caller of Start I/O routine, KP_STARTIO=LD_START,- ; Start I/O routine6 KP_STACK_SIZE=8192,- ; Size of kernel process stackB KP_REG_MASK=KPREG$K_HLL_REG_MASK,-; Kernel process reg save mask& FUNCTB=LD_FUNCTABLE,- ; FDT address* CANCEL=LD_CANCEL,- ; Cancel I/O routine0 CLONEDUCB=LD_CLONED_UCB,- ; Cloned UCB routine6 CTRLINIT=LD_CONTROL_INIT,- ; Controller init routine, UNITINIT=LD_UNIT_INIT ; Unit init routine .PAGE;+++; Function decision table:;--- FDT_INI LD_FUNCTABLE FDT_BUF - ; LD control functions? FDT_ACT LD_FDT_SHAD_WCHECK,<- ; Check write to shadow set mbrl% WRITELBLK,- ; Write LOGICAL Block & WRITEPBLK,- ; Write Physical Block$ WRITEVBLK> ; Write VIRTUAL Block+ FDT_ACT LD_FDT_READBLK,- ; Read functionsi ; Read virtual block/ FDT_ACT LD_FDT_WRITECHECK,- ; Write functions ; Write check' FDT_ACT LD_FDT_DSE,<- ; DSE function! DSE> ; Data Secutiry EraseG- FDT_ACT LD_FDT_ACCESS,- ; Access functionsF! ; Create file/dir0 FDT_ACT LD_FDT_DEACCESS,- ; Deaccess functions ; Deaccess file- FDT_ACT LD_FDT_MODIFY,- ; Modify functions ! ; Modify file attributesE+ FDT_ACT LD_FDT_MOUNT,- ; Mount functions_ ; Mount volumeE; FDT_ACT LD_FDT_LCLDSKVALID,- ; Local disk valid functionsT ; Pack acknowledge 6 FDT_ACT LD_FDT_ZEROPARM,- ; Zero parameter functions ; Drive clear4 FDT_ACT LD_FDT_ONEPARM,- ; One parameter functions ; Seek. FDT_ACT LD_FDT_SENSEMODE,- ; Sense functions* ; Sense mode * FDT_ACT LD_FDT_SETCHAR,- ; Set functions& ; Set modeB: FDT_ACT LD_FDT_CRESHAD,- ; Create shadowset virtual unit E4 FDT_ACT LD_FDT_REMSHAD,- ; Remove shadowset member '5 FDT_ACT LD_FDT_CONTROL,- ; General LDdriver controlh ;v ; Local data;r .SAVE_PSECT% .PSECT $$$110_LD_DATA,LONG,WRT,NOEXEE;I UNIVERSAL_SYMBOL LD_REFCNTD ;LD_REFCNT:r% .LONG 0 ; Number of active devices$ UNIVERSAL_SYMBOL LD_DEBUG1g ;LD_DEBUG1:m .LONG 0  UNIVERSAL_SYMBOL LD_DEBUG2u ;LD_DEBUG2:R .LONG 0L;1FLBN_WP: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\ .ASCII \PID: !XL!/\  .ASCII \Image: !AC!/\t .ASCII \Device: !AC!/\T .ASCII \Function: !XW!/\E .ASCII \LBN: !UL\ LBN_WP_LEN=.-LBN_WPE;KFVBN_WP: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\ .ASCII \PID: !XL!/\, .ASCII \Image: !AC!/\h .ASCII \Device: !AC!/\N .ASCII \Function: !XW!/\ .ASCII \VBN: !UL!/\L! .ASCII \File id: (!UW,!UW,!UW)\_VBN_WP_LEN=.-VBN_WPf;d4NONESTR: .ASCIC /None/ ; If no imagename available<OPCOM_NAME: .ASCIC /OPCOM/ ; Process name of OPCOM process .RESTORE_PSECTm;c; End of driver data;. DRIVER_CODE .PAGE@ .SBTTL LD_FDT_CONTROL, general control and dispatch FDT routine;+++:; LD_FDT_CONTROL, general control and dispatch FDT routine;d; Functional description:L;19; This routine is invoked via an IO$_LD_CONTROL function.N=; We will dispatch to the various other routines according toL; the P6 parameter.r;d ; Inputs:E;D; R0-R2 - scratch registerse.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)1.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)I*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter>; P6(AP) - Address of longword with functioncode and modifiers;r ; Outputs:;V;; The routine must preserve all registers except R0-R2, ande ; R9-R11.e;n;--s UNIVERSAL_SYMBOL LD_FDT_CONTROL1;LD_FDT_CONTROL: ; General Control FDT routinet $DRIVER_FDT_ENTRY* MOVL IRP$L_QIO_P6(R3),R0 ; Get parameter1 MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRP$ ASSUME LDIO_S_FUNC EQ 89 DISPATCH R0,TYPE=B,<- ; Dispatch according to function  ,-$ ,-( ,-* ,-" ,-& ,-, ,-! ,- ( ,-* ,-" ,-( ,-) ,- , ,-. ,-) >_: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code CALL_ABORTIO ; Abort the I/O .PAGEE .SBTTL LD_GET_CONNECTION, Get connection characteristics FDT routineL;+++?; LD_GET_CONNECTION, Get connection characteristics FDT routine ;L; Functional description:D;PF; This routine returns the full filename string to the callers buffer,-; specified via the descriptor address in P1.;; The IOSB returned, contains;&; - Longword 0, The return status code2; and in word 1,The nr of characters transferred0; - Longword 1, The Connected status flag, where; bit 0 = 1 : Connected, and; 0 : Disconnected; bit 1 = 0 : Normal and; 1 : Replaced; bit 2 = 0 : Normal diskE; 1 : DECRAM disk1; bit 3 = 0 : Normal accessO; 1 : Write protectedE;D ; Inputs:_;E; R0-R2 - scratch registersO.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)_.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)_*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter=; P1(AP) - Address of buffer to receive the devicename stringK:; P2(AP) - Size of buffer to receive the devicename stringA; P3(AP) - Address of buffer to receive file-id of connected fileO;S ; Outputs:;K;; The routine must preserve all registers except R0-R2, and4 ; R9-R11.S;_;--M# UNIVERSAL_SYMBOL LD_GET_CONNECTIONO3;LD_GET_CONNECTION: ; Get Connection FDT routineD. MOVL IRP$L_QIO_P1(R3),R0 ; Address of buffer. MOVL IRP$L_QIO_P2(R3),R1 ; Get buffer length* BEQL 10$ ; Length 0, just return flags0 CALL_READCHK ; Check buffer for write access) MOVZWL #SS$_ACCVIO,R0 ; Assume trouble;A IFNOWRT #6,@IRP$L_QIO_P3(R3),20$ ; Check if fid buffer writeable$3 MOVZWL #SS$_DEVINACT,R0 ; Assume already inactiveE" BBC #UCB$V_LD_CONSTS,- ; Active? UCB$W_LD_FLAGS(R5),20$6 MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical device ucb0 CALL_IOLOCKR SAVE_R1=NO ; Lock the IO database PUSHR #^MA! MOVL R2,R5 ; Copy ucb addressa! MOVZBL #1,R4 ; DVI$_ALLDEVNAM . MOVL IRP$L_QIO_P1(R3),R1 ; Address of buffer* MOVZWL IRP$L_QIO_P2(R3),R0 ; Buffer size. CALL_CVT_DEVNAM ; Get alloclass devicename POPR #^M+ PUSHR #^M ; Save across unlocke* CALL_IOUNLOCK ; Unlock the IO database. POPR #^M ; Restore IRP and status BLBC R0,20$ ; TroubleV3 MOVL IRP$L_QIO_P3(R3),R2 ; Get fid buffer addressg ASSUME FID$W_NUM+2 EQ FID$W_SEQ ASSUME FID$W_SEQ+2 EQ FID$W_RVN. ASSUME UCB$W_LD_FID_NUM+2 EQ UCB$W_LD_FID_SEQ3 MOVL UCB$W_LD_FID_NUM(R5),(R2)+ ; Insert in buffer_ MOVW UCB$W_LD_FID_RVN(R5),(R2)-10$: MOVZWL #SS$_NORMAL,R0- INSV R1,#16,#16,R0 ; Place high word in R0a< MOVZWL UCB$W_LD_FLAGS(R5),R1 ; Copy connected status flags CALL_FINISHIO ; Done$20$: CALL_ABORTIO ; Abort the I/O .PAGE0 .SBTTL LD_GET_TRACE, Get trace data FDT routine;+++*; LD_GET_TRACE, Get trace data FDT routine;d; Functional description:H;8:; This routine retrieves trace data from the trace buffer.; The IOSB returned, contains;&; - Longword 0, The return status code4; - Longword 1, The number of returned trace buffers; ; Inputs:r;s; R0-R2 - scratch registers1.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)$*$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U"U; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter6; P1(AP) - address of buffer to receive the trace data3; P2(AP) - size of buffer to receive the trace dataM; ; Outputs:;u; R0 - I/O status ; SS$_NORMAL if succcesse6; SS$_WASSET if success and FDT tracing was enabled; R1 - Number of returned bytes ;r;; The routine must preserve all registers except R0-R2, andO ; R9-R11. ;o;--  UNIVERSAL_SYMBOL LD_GET_TRACE;LD_GET_TRACE:2 TSTL UCB$L_LD_TRCBUF(R5) ; Check for tracebuffer BEQL 10$ ; Not set, BBC #LDIO_V_INQUIRE,- ; Return tracebuffer IRP$L_EXTEND(R3),30$6 MOVL UCB$L_LD_TRCBUFSIZ(R5),R2 ; Get tracebuffer size" MOVZWL #SS$_NORMAL,R0 ; Success+ BBC #UCB$V_LD_FDTTRACE,- ; FDT trace set?  UCB$W_LD_FLAGS(R5),50$, MOVZWL #SS$_WASSET,R0 ; Alternate success BRW 50$ ; Finish the I/O/10$: MOVZWL #SS$_NODATA,R0 ; Trace not activeE$20$: CALL_ABORTIO ; Abort the I/O730$: MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too small . MOVL IRP$L_QIO_P2(R3),R1 ; Get buffer length, CMPL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Check size BLSS 20$ ; Too small2 MOVL IRP$L_QIO_P1(R3),R0 ; Get address of buffer1 CALL_READCHK ; Check userbuffer accessabilityP0 MOVL R0,IRP$L_SVAPTE(R3) ; Save buffer address) BSBW LD_MOVE_TRACE ; Move data to userf' BLBC R0,60$ ; Stop on overrun errorr$ ; The count will always be the' ; number of packets in the bufferG50$: DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to number of entriesi BNEQ 60$ ; Something there. BBS #LDIO_V_NOWAIT,- ; We don't want to wait IRP$L_EXTEND(R3),60$( CALL_QIODRVPKT ; Finish in start I/O,60$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE CALL_FINISHIO ; Done .PAGE. .SBTTL LD_MOVE_TRACE, Move trace data to user;+++(; LD_MOVE_TRACE, move trace data to user;o; Functional description:1A; o8; This routine moves the tracedata to the user's buffer.;; If the modifier LDIO_RESET was specified for the transfer_0; then the tracebuffer will be reset afterwards.;o ; Inputs:C;_.; R3 - address of the IRP (I/O request packet).; R5 - address of the UCB (unit control block); ; Outputs:;rD; R0 - Status, either SS$_NORMAL or SS$_DATAOVERUN (buffer overflow)-; R1 - 0 or on failure number of lost packetso; R2 - number of bytes moved;L7; The routine must preserve all registers except R0-R2. ;c;--r UNIVERSAL_SYMBOL LD_MOVE_TRACEn;LD_MOVE_TRACE:a- .JSB_ENTRY INPUT=,OUTPUT=,-  PRESERVE= PUSHR #^M( MOVL IRP$L_SVAPTE(R3),R0 ; Get address% MOVL IRP$L_BCNT(R3),R1 ; Get length_) BBC #LDIO_V_RESET,- ; Reset requested?w IRP$L_EXTEND(R3),40$ ; No: LOCK_TRACE ACCESS=WRITE ; Lock the tracebuffer for write BRW 50$240$: LOCK_TRACE ; Lock the tracebuffer for read.50$: CLRL R10 ; Total number of bytes moved9 MOVL UCB$L_LD_TRCBUFPTR(R5),R2 ; Get tracebuffer pointer 5 MOVL UCB$L_LD_TRCWRAP(R5),R9 ; Did the buffer wrap?u BEQL 60$ ; NoC SUBL3 R2,R9,R1 ; Bytecount  MOVL R1,R10 ; Save count/ BSBW MOVE_TRACE ; Move the data (first part)<60$: MOVL UCB$L_LD_TRCBUF(R5),R2 ; Point to start of buffer# SUBL3 R2,UCB$L_LD_TRCBUFPTR(R5),R1 # ADDL2 R1,R10 ; Accumulate countE0 BSBW MOVE_TRACE ; Move the data (second part)* MOVL UCB$L_LD_TRCLOST(R5),R1 ; Get count- MOVZWL #SS$_NORMAL,R0 ; Assume no overflowC TSTL R9 ; Buffer wrapped?  BEQL 70$ ; No;EA; Calculate number of lost packets. This will be done as follows:E;C7; Count = (((TRCBUFPTR - TRCBUF) / LDTRCENT_K_LENGTH) +nF; ((TRCLOST - 1) * ((TRCWRAP - TRCBUF) / LDTRCENT_K_LENGTH)));E DECL R1 ; TRCLOST - 110 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCWRAP - TRCBUF UCB$L_LD_TRCWRAP(R5),R0E3 DIVL2 #LDTRCENT_K_LENGTH,R0 ; / LDTRCENT_K_LENGTHR MULL2 R1,R02 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCBUFPTR - TRCBUF UCB$L_LD_TRCBUFPTR(R5),R1 3 DIVL2 #LDTRCENT_K_LENGTH,R1 ; / LDTRCENT_K_LENGTH. ADDL2 R0,R1 ; Result8 MOVZWL #SS$_DATAOVERUN,R0 ; We were overrun by a truck-70$: BBC #LDIO_V_RESET,- ; Reset requested?s IRP$L_EXTEND(R3),80$ ; No- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrappedW. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer)80$: UNLOCK_TRACE ; Unlock trace mutexp% MOVL R10,R2 ; Return amount movedI POPR #^Me RSB;C MOVE_TRACE: + .JSB_ENTRY INPUT=,OUTPUT=E PUSHR #^M MOVL R1,R6 ; LengthB MOVL R2,R1 ; Sourcey MOVL R0,R3 ; Destination4 DIVL3 #65535,R6,R7 ; Number of times we must loop- BEQL 20$ ; Total count is less than 65535E>10$: MOVC3 #65535,(R1),(R3) ; Move data (max MOVC can handle)+ SUBL2 #65535,R6 ; Accumulate bytes moved SOBGTR R7,10$ ; Until done.20$: MOVC3 R6,(R1),(R3) ; Move the remainder# MOVL R3,R0 ; Return last byte+1  POPR #^MD RSB .PAGE- .SBTTL LD_DISCONNECT, Disconnect FDT routinev;+++'; LD_DISCONNECT, Disconnect FDT routine ;S; Functional description:e;hD; This routine will disconnect the Logical disk from the Phys. Disk.G; If LDIO_M_ABORT is specified we simply skip all checks and disconnect ; the LD device as it is. ;;C; A prereqisite to call these routines, is that the file is opened,P'; except when we replace a whole drive.O;D ; Inputs:P;,; R0-R2 - scratch registersT.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)T.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)D*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter:; P1(AP) - Address of the SBK block (Ignored with abort or!; replaced drive)_;! ; Outputs:;t;; The routine must preserve all registers except R0-R2, andt ; R9-R11.n;;--$ UNIVERSAL_SYMBOL LD_DISCONNECT ,;LD_DISCONNECT: ; Disconnect FDT routine+ MOVZWL #SS$_DEVINACT,R0 ; Assume inactiveL7 BBC #UCB$V_LD_CONSTS,- ; Check if in DISCONNECT stateB UCB$W_LD_FLAGS(R5),10$; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted statusy> BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),10$; Device foreign mounted?1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted statusI6 BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),10$; Device mounted?5 MOVZWL #SS$_DEVASSIGN,R0 ; Assume channels assigned / CMPL UCB$L_REFC(R5),#1 ; Are we the only one?L BNEQ 10$ ; No, get out2 BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffersm( CLRL -(SP) ; Storage for CCB address) PUSHAL (SP) ; Address for CCB storaget- PUSHL IRP$L_CHAN(R3) ; Save channel numberI+ CALLS #2,IOC$CHAN_TO_CCB ; Convert to CCB " MOVL (SP)+,R1 ; Get CCB address BLBC R0,10$ ; ErroreD MOVL UCB$L_LD_WCB(R5),CCB$L_WIND(R1) ; Save WCB address for cleanup( CALL_QIODRVPKT ; Finish in start I/O(10$: CALL_ABORTIO ; And abort the I/O .PAGE' .SBTTL LD_CONNECT, Connect FDT routinen;+++!; LD_CONNECT, Connect FDT routinei;n; Functional description: ;c@; This routine will connect a Logical disk to the Physical Disk.;PC; A prereqisite to call these routines, is that the file is opened,C'; except when we replace a whole drive. ;k ; Inputs:e; ; R0-R2 - scratch registersc.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)c*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter4; P1(AP) - Address of the SBK block (Normal connect)B; P1(AP) - Address of phys. disk device name desc. (Replace drive)@; P2(AP) - Size of disk, if 0 allocated size of FCB will be used; P3(AP) - Number of TracksF; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;e ; Outputs:;E;; The routine must preserve all registers except R0-R2, andh ; R9-R11.k; ;--B UNIVERSAL_SYMBOL LD_CONNECT&;LD_CONNECT: ; Connect FDT routine0 BBC #UCB$V_LD_CONSTS,- ; Check if disconnected UCB$W_LD_FLAGS(R5),10$. MOVZWL #SS$_DEVACTIVE,R0 ; Set Device Active BRW 140$T510$: BBC #LDIO_V_SHARE,- ; Shared access requested? Vw%0$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1UO"f IRP$L_EXTEND(R3),20$, TSTL @#CLU$GL_CLUB ; Cluster code loaded? BNEQ 20$ ; Yes, MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported BRW 140$T.20$: BBS #LDIO_V_REPLACE,- ; Replace a drive? IRP$L_EXTEND(R3),40$, MOVL #SBK$K_LENGTH,R1 ; Get the SBK length0 MOVL IRP$L_QIO_P1(R3),R0 ; Get the SBK address, CALL_WRITECHK ; Check if SBK is readable PUSHL R0m9 CALL_IOLOCKR SAVE_R1=NO ; Lock the IO database for readc MOVL (SP)+,R0 .DISABLE FLAGGING- MOVL SBK$L_FCB(R0),R1 ; Get the FCB addressK .ENABLE FLAGGINGn% BGEQ 30$ ; Must be system address 6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 30$ ; No, return errorT$ TSTL FCB$L_REFCNT(R1) ; File open? BEQL 30$ ; No, error .IF DF CACHE_CHECK_;SA; Check if the file is opened with caching disabled (only on V8.2i?; and higher since the XQP does not setup this field the proper;; way before that version). ; , CMPL G^CACHE$GL_FLAGS,#2 ; Is XFC enabled? BNEQ 22$ ; No, forget check;8 CMPZV #ATR$V_FILE_CONTENTS,- ; Check if file is marked, #ATR$S_FILE_CONTENTS,- ; as 'No Caching' FCB$L_CACHING_OPTIONS(R1),-O #ATR$C_NOCACHING BEQL 22$ ; Ok, continueO6 MOVZWL #SS$_WRONGSTATE,R0 ; Cache specified for file BRW 90$22$: .ENDC7 MOVL R1,UCB$L_LD_FCB(R5) ; Save FCB address for later , MOVL FCB$L_WLFL(R1),R1 ; Get a WCB address3 MOVL #SS$_FILNOTACC,R0 ; Assume completely mappedS/ BBC #WCB$V_COMPLETE,- ; Check if complete mapB WCB$B_ACCESS(R1),35$5 BBC #WCB$V_CATHEDRAL,- ; and with cathedral windowsP WCB$B_ACCESS(R1),35$- MOVL R1,UCB$L_LD_WCB(R5) ; Save WCB addressT7 MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical deviceB! MOVL UCB$L_VCB(R1),R2 ; Get VCBC! MOVL VCB$L_AQB(R2),R0 ; Get AQBc; CMPB AQB$B_ACPTYPE(R0),#AQB$K_F11V2 ; Serviced by F11BXQP?I BNEQ 27$ ; No.. TSTL VCB$L_RVN(R2) ; Relative volume number) BEQL 25$ ; Branch if not a volume setF+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addressf BEQL 25$ ; Not there0 MOVL RVT$L_UCBLST(R0),R1 ; Get root volume UCB*25$: MOVL R1,UCB$L_LD_PDUCB(R5) ; Save it BRB 50$,27$: MOVZWL #SS$_WRONGACP,R0 ; Bad ACP type BRW 90$*30$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid.35$: BRW 90$ ; Abort the I/O and unlock mtxB40$: MOVL IRP$L_QIO_P1(R3),R1 ; Get devicename descriptor address0 MOVL 4(R1),R0 ; Get devicename string address1 MOVZWL (R1),R1 ; Get devicename string lengthe/ CALL_WRITECHK ; Check if string is readabled4 PUSHR #^M ; Save some registers9 CALL_IOLOCKR SAVE_R1=NO ; Lock the IO database for read 8 MOVL IRP$L_QIO_P1(R3),R1 ; Get devicename descr. addr.+ CALL_SEARCHDEV ; Search the IO database 5 POPR #^M ; Restore the registersI! BLBC R0,90$ ; Return on error = MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. diske550$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid devicem7 CMPB UCB$B_DEVCLASS(R1),#DC$_DISK ; Connect to a disk?d BNEQ 90$ ; No, not allowed( PUSHR #^M ; Save across unlock* CALL_IOUNLOCK ; Unlock the IO database POPR #^M ; Restore IRP! BRB 100$ ; Passed the test... /90$: PUSHR #^M ; Save across unlock6* CALL_IOUNLOCK ; Unlock the IO database1 POPR #^M ; Restore the return statusN BRW 140$S-100$: BBC #LDIO_V_REPLACE,- ; Replace drive?t IRP$L_EXTEND(R3),120$D/ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCB-; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted statusR. BBS #DEV$V_FOR,- ; Check if foreign mounted UCB$L_DEVCHAR(R2),140$1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted status 0 BBS #DEV$V_MNT,- ; Check if device is mounted UCB$L_DEVCHAR(R2),140$, MOVZWL #SS$_DEVALLOC,R0 ; Assume allocated. TSTL UCB$L_LOCKID(R2) ; Any lock for device? BNEQ 140$ ; Yes, error exitD BRW 130$ ; Continue_;120$: BSBW LD_SET_GEOMETRY ; Fill in geometry informationE BLBC R0,140$ ; Quit on error0 MOVL IRP$L_QIO_P1(R3),R9 ; Get the SBK address .DISABLE FLAGGING- MOVL SBK$L_FCB(R9),R9 ; Get the FCB addressl .ENABLE FLAGGING:130$: BSBW LD_MAKE_FILE_RESNAM ; Create file resourcename6 BSBW LD_MAKE_DEV_RESNAM ; Create device resourcename1 CALL_QIODRVPKT ; Do the rest in the start I/Oc)140$: CALL_ABORTIO ; And abort the I/OD .PAGE5 .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometryl;+++/; LD_SET_GEOMETRY, Setup pseudo device geometry ;;; Functional description:a;;;; This routine will set the geometry for the pseudo device.h;r ; Inputs:e;t; R0-R2 - scratch registersh.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)i.; R5 - address of the UCB (unit control block); R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter4; P1(AP) - Address of the SBK block (Normal connect)@; P2(AP) - Size of disk, if 0 allocated size of FCB will be used; P3(AP) - Number of Trackso; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;i ; Outputs:;) ; R0 - statuss; ;; The routine must preserve all registers except R0-R2, andO ; R9-R11.o;;---! UNIVERSAL_SYMBOL LD_SET_GEOMETRY ;LD_SET_GEOMETRY:R* .JSB_ENTRY INPUT=,OUTPUT=,- PRESERVE=, MOVL IRP$L_QIO_P1(R3),R2 ; Get SBK address .DISABLE FLAGGING- MOVL SBK$L_FCB(R2),R0 ; Get the FCB addresse .ENABLE FLAGGINGi8 MOVL FCB$L_EFBLK(R0),R0 ; Get maximum number of blocks) MOVL IRP$L_QIO_P2(R3),R1 ; Get the sizet" BNEQ 10$ ; Available, check it# MOVL R0,R1 ; Use allocated size BRB 20$!10$: CMPL R1,R0 ; Not too big?O BLEQU 20$ ; Valid, use itD( MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 90$;fD; At this point R1 is the MAXBLOCK (maximum logical blocknumber + 1); 820$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Set max. nr of blocks1 MOVL IRP$L_QIO_P3(R3),R9 ; Get number of tracksu3 MOVL IRP$L_QIO_P4(R3),R10 ; Get number of sectorsd5 MOVL IRP$L_QIO_P5(R3),R11 ; Get number of cylindersa) BISL3 R9,R10,R0 ; Check for all zeroes; BISL2 R11,R0$* BEQL 70$ ; All zero, use own algorithm; :; At least one of (TRACKS,SECTORS,CYLINDERS) was specifiedA; Make sure they are all at least 1 and that they fit in the UCB.r;a TSTL R9 ; Tracks zero? BNEQ 30$ ; No$ INCL R9 ; Make 1%30$: CMPL R9,#256 ; Within bounds?R BGEQU 60$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; NoP INCL R10 ; Make 1M%40$: CMPL R10,#256 ; Within bounds?_ BGEQU 60$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; NoR INCL R11 ; Make one$'50$: CMPL R11,#65536 ; Within bounds?; BGEQU 60$ ; NoC MULL3 R9,R10,R2 ; R2 now size of one Cylinder (Tracks * Sectors)R% MULL3 R2,R11,R0 ; R0 now T * S * C_= CMPL R0,R1 ; Will MAXBLOCK fit within specified geometry?_( BLSSU 60$ ; T * S * C not big enough) MOVB R9,UCB$B_TRACKS(R5) ; Setup tracks , MOVB R10,UCB$B_SECTORS(R5) ; Setup sectors0 MOVW R11,UCB$W_CYLINDERS(R5) ; Setup cylinders BRW 80$660$: MOVZWL #SS$_BADPARAM,R0 ; Bad geometry parameter BRW 90$-70$: CLRL -(SP) ; No need to specify T/S/CD CLRL -(SP)e CLRL -(SP)F# CLRL -(SP) ; Maxblock is in ucbi PUSHL R5 ; UCB address3 CALLS #5,EXE$FORGE_GEOMETRY ; Get usable geometryr80$: MOVZWL #SS$_NORMAL,R090$: RSB .PAGE5 .SBTTL LD_SET_SEED, Set seed unit number FDT routineb;+++/; LD_SET_SEED, Set seed unit number FDT routine1; ; Functional description:O;q:; This routine sets the seed number to the specified value; ; Inputs:d;s; R0-R2 - scratch registersc.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block).; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)s*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; P1(AP) - seed valueM; ; Outputs:;u;; The routine must preserve all registers except R0-R2, andW ; R9-R11.c;s;--F UNIVERSAL_SYMBOL LD_SET_SEEDN ;LD_SET_SEED:e& MOVL IRP$L_QIO_P1(R3),R0 ; Get value CMPL R0,#9999 ; Limit range O BGTRU 10$1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB_3 MOVL DDB$L_UCB(R1),R1 ; Get address of UNIT 0 UCBu* MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success# CALL_FINISHIOC ; Finish the I/OT%10$: MOVL #SS$_BADPARAM,R0 ; TroubleM CALL_ABORTIO ; Abort the I/O .PAGE: .SBTTL LD_SET_ALLOCLASS, Set allocation class FDT routine;+++4; LD_SET_ALLOCLASS, Set allocation class FDT routine; ; Functional description:W;SA; This routine is the FD í::$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U)"wT routine to set the allocation class fort@; the LD devices. This can only be done when no other LD devicesA; are active. If that's not the case and the specified allocation_@; class is the same as the one already set we will ignore it and; return success.t;d ; Inputs:f;; R0-R2 - scratch registersb.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)e.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers!; P1(AP) - allocation class value,;; ; Outputs:;t;; The routine must preserve all registers except R0-R2, andn ; R9-R11.;0;--R" UNIVERSAL_SYMBOL LD_SET_ALLOCLASS;LD_SET_ALLOCLASS:& MOVL IRP$L_QIO_P1(R3),R2 ; Get value CMPL R2,#255 ; Limit range D BGTRU 20$ ; Out of range1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB * CMPL R2,DDB$L_ALLOCLS(R1) ; Same number? BEQL 10$ ; Ok, accept it0 TSTW G^LD_REFCNT ; Already someone connected? BNEQ 30$ ; Yes, not allowedf- FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronizee SAVIPL=-(SP),- PRESERVE=NO; MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDBd+ MOVL UCB$L_CDDB(R5),R1 ; Get address CDDBu= MOVL R2,CDDB$L_ALLOCLS(R1) ; Setup allocation class in CDDB(0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock& NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO$10$: MOVL #SS$_NORMAL,R0 ; Success# CALL_FINISHIOC ; Finish the I/OO%20$: MOVL #SS$_BADPARAM,R0 ; Trouble_ BRB 40$:30$: MOVZWL #SS$_UNSAFE,R0 ; No devices may be connected$40$: CALL_ABORTIO ; Abort the I/O .PAGE5 .SBTTL LD_DISABLE_TRACE, Disable tracing FDT routineD;+++/; LD_DISABLE_TRACE, Disable tracing FDT routine4; ; Functional description:I; ?; This routine disables tracing and deallocates the tracebuffer;;c ; Inputs:b;e; R0-R2 - scratch registers .; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)C.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;o ; Outputs:;3;; The routine must preserve all registers except R0-R2, and ; R9-R11.B;O;--A" UNIVERSAL_SYMBOL LD_DISABLE_TRACE;LD_DISABLE_TRACE:2 MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get buffer pointer BNEQ 10$ ; Set( MOVZWL #SS$_NODATA,R0 ; Trace not set BRW 30$010$: LOCK_TRACE ACCESS=WRITE ; Lock trace mutex) CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointerP9 MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R1 ; Get allocated size-& SUBL2 #12,R0 ; Point to real start) JSB G^EXE$DEANONPGDSIZ ; Release memoryS2 MOVL UCB$L_LD_TRCBUFOWN(R5),R2 ; Get buffer ownerB ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested2 CMPL R2,IRP$L_PID(R3) ; Did we own it ourselves? BNEQ 20$ ; Yes- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaE BRB 25$ 20$: PUSHL R4T! MOVL R0,R1 ; Amount to returnt" MOVL R2,R4 ; Process to credit0 BSBW LD_RETURN_QUOTA ; Credit correct process0 ; Ignore errors (Proc. may have gone away) MOVL (SP)+,R4B25$: BICW2 #,- UCB$W_LD_FLAGS(R5)% UNLOCK_TRACE ; Unlock trace mutex;5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelo SAVIPL=-(SP)96 MOVL G^EXE$GL_AFFINITY,- ; Restore original affinity UCB$L_AFFINITY(R5)0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELR PRESERVE=NO" MOVZWL #SS$_NORMAL,R0 ; Success# CALL_FINISHIOC ; Finish the I/Ot(30$: CALL_ABORTIO ; And abort the I/O .PAGE3 .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routine5;+++-; LD_ENABLE_TRACE, Enable tracing FDT routine ;n; Functional description:R;(;; This routine allocates a tracebuffer and enables tracing +; ; Inputs:1;,; R0-R2 - scratch registersE.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)S.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)s*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;o ; Outputs:;t;; The routine must preserve all registers except R0-R2, anda ; R9-R11.e;;-- ! UNIVERSAL_SYMBOL LD_ENABLE_TRACEi;LD_ENABLE_TRACE:e7 MOVZWL #SS$_TOOMUCHDATA,R0 ; Assume trace already set 2 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BNEQ 10$ ; Already set0 MOVZWL #SS$_BADPARAM,R0 ; Assume bad parameter2 MOVL IRP$L_QIO_P1(R3),R1 ; Get size (in entries) BEQL 10$ ; Nothing??@ MULL2 #LDTRCENT_K_LENGTH,R1 ; Length of LDTRCENT * #of entries0 MOVL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Save used size;)K; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to bedJ; able to deallocate the packet as a forkblock when the driver is reloaded;R& ADDL2 #12,R1 ; Add packet overhead4 BISL2 #^X80000000,R1 ; Avoid check against MAXBUF. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,20$ ; Enuf left_/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quotaC10$: BRW 50$ ; Get out (20$: JSB G^EXE$ALONONPAGED ; Get memory BLBC R0,10$ ; Error," ADDL2 #12,R2 ; Leave some room, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex( MOVL IRP$L_PID(R3),- ; Register owner UCB$L_LD_TRCBUFOWN(R5)- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrappede. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet8 MOVL R1,UCB$L_LD_TRCBUFALLOCSIZ(R5) ; Setup buffer size, MOVL R2,UCB$L_LD_TRCBUF(R5) ; Setup buffer. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer. BBC #LDIO_V_FDTTRACE,- ; Enable FDT tracing? IRP$L_EXTEND(R3),30$+ BISW2 #UCB$M_LD_FDTTRACE,- ; Yes, flag itc UCB$W_LD_FLAGS(R5)330$: BBC #LDIO_V_ACCURATE,- ; Use RSCC for timing?  IRP$L_EXTEND(R3),40$+ BISW2 #UCB$M_LD_ACCURATE,- ; Yes, flag itC UCB$W_LD_FLAGS(R5);aC; Clear affinity for this device to make sure we run on the primaryCC; processor. We use the RSCC register for timing measurements whichcF; is distinct for each processor, so we have to use the same processorF; for I/O initiation as well as completion. Since completion is alwaysB; forced through the primary by inserting IRP's into IOC$GQ_POSTIQ0; we use the primary for I/O initiation as well.;e5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelr SAVIPL=-(SP)s; CLRL UCB$L_AFFINITY(R5) ; Make sure we run on the primaryp0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELC PRESERVE=NO)40$: UNLOCK_TRACE ; Unlock trace mutext MOVZWL #SS$_NORMAL,R0# CALL_FINISHIOC ; Finish the I/Oi(50$: CALL_ABORTIO ; And abort the I/O .PAGE5 .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routined;+++/; LD_RESET_TRACE, Reset tracebuffer FDT routinee;o; Functional description:s;(-; This routine resets the tracebuffer pointerl;a ; Inputs: ; ; R0-R2 - scratch registerse.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)h.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)D*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;R;; The routine must preserve all registers except R0-R2, and- ; R9-R11. ;e;--q UNIVERSAL_SYMBOL LD_RESET_TRACE;LD_RESET_TRACE:- MOVZWL #SS$_NODATA,R0 ; Assume not yet set2 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BEQL 10$ ; Not there, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrappede. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer% UNLOCK_TRACE ; Unlock trace mutex= MOVZWL #SS$_NORMAL,R0# CALL_FINISHIOC ; Finish the I/OL(10$: CALL_ABORTIO ; And abort the I/O .PAGE/ .SBTTL LD_ENABLE_PROTECT, Enable write protecte;+++*; LD_ENABLE_PROTECT, Enable write protect;h; Functional description:0; <; This routine enables write-protection on the logical disk.; ; Inputs:,;r; R0-R2 - scratch registers.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address o!$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Ul"f the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter;N ; Outputs:; ;; The routine must preserve all registers except R0-R2, andp ; R9-R11. ;e;--W# UNIVERSAL_SYMBOL LD_ENABLE_PROTECT_3;LD_ENABLE_PROTECT: ; Enable protect FDT routineL1 BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bitC* BISW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 CALL_FINISHIOC .PAGE1 .SBTTL LD_DISABLE_PROTECT, Disable write protectC;+++,; LD_DISABLE_PROTECT, Disable write protect;C; Functional description:U;R=; This routine disables write-protection on the logical disk. ; ; Inputs: ;$; R0-R2 - scratch registersP.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)c*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter;e ; Outputs:;0;; The routine must preserve all registers except R0-R2, and ; R9-R11./;n;--o$ UNIVERSAL_SYMBOL LD_DISABLE_PROTECT5;LD_DISABLE_PROTECT: ; Disable protect FDT routine 5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBG* BBC #DEV$V_SWL,- ; Check write-lock bit UCB$L_DEVCHAR(R1),10$r4 MOVL #SS$_WRITLCK,R0 ; Physical disk is protected BRW 20$510$: BICL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit * BICW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R020$: CALL_FINISHIOC^ .PAGE/ .SBTTL LD_DEALLOC_TRCBUF, Trace buffer release9;+++*; LD_DEALLOC_TRCBUF, Trace buffer release; ; Functional description:.;s;; This routine deallocates the tracebuffer if we disconnect;; from the physical device (in case of cloned device) or inE; case of driver reload.;PF; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).B;0 ; Inputs:e;h1; R4 - address of the PCB (process control block)o#; - 0 if called from LD_DRV_UNLOADk.; R5 - address of the UCB (unit control block);h ; Outputs:;N;; The routine must preserve all registers except R0-R2, andt ; R9-R11.X;D;--1# UNIVERSAL_SYMBOL LD_DEALLOC_TRCBUFG;LD_DEALLOC_TRCBUF:V .JSB_ENTRY INPUT=s+ MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get address, BNEQ 10$ ; Buffer available  BRW 60$ 10$: TSTL R4 ; PCB available?) BEQL 20$ ; No, no need to synchronizeN! ; (Called by LD_DRV_UNLOAD)- LOCK_TRACE ACCESS=WRITE ; Lock trace buffer,* CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get ownere UNLOCK_TRACE BRB 30$.20$: CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get owner #30$: MOVQ R3,-(SP) ; Save R3 + R4)/ MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get sizeM' SUBL2 #12,R0 ; Account for overheads" ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1- MOVW #>,- ; and proper spinlockr1 FKB$B_TYPE(R0) ; for this to be a fork blockr PUSHL R5 ; Save UCBT MOVL R0,R5 ; Copy addressA" SAVIPL -(SP) ; Get current IPL> CMPL (SP),#IPL$_RESCHED ; Compare to minimum Fork ipl needed0 BGEQ 35$ ; Don't change IPL if not necessary% SETIPL #IPL$_RESCHED,- ; Raise IPLf ENVIRON=UNIPROCESSOR&35$: FORK CONTINUE=40$ ; Create fork% MOVL R5,R0 ; Deallocate the blockr" MOVL FKB$Q_FR3(R5),R1 ; Get size" JSB G^EXE$DEANONPGDSIZ ; Get out RSBJ40$: SETIPL (SP)+,ENVIRON=UNIPROCESSOR ; Restore IPL to value before fork & MOVL (SP)+,R5 ; Restore UCB pointer" MOVQ (SP)+,R3 ; Restore R3 + R4 TSTL R4 ; PCB available?# BEQL 60$ ; No, no one to credit B ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested+ CMPL PCB$L_PID(R4),R1 ; Are we the owner?e BNEQ 50$ ; No- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotae BRB 60$ 50$: PUSHL R4- MOVL R1,R4 ; Owner pid! MOVL R0,R1 ; Amount to returnR% BSBW LD_RETURN_QUOTA ; Credit userU MOVL (SP)+,R460$: RSB .PAGE1 .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer releaseS;+++,; LD_DEALLOC_WATCHBUF, Watch buffer release; ; Functional description:N;E<; This routine deallocates all watchbuffers if we disconnect;; from the physical device (in case of cloned device) or in ; case of driver reload.;VF; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER). ;$ ; Inputs:R;;1; R4 - address of the PCB (process control block)h#; - 0 if called from LD_DRV_UNLOADe.; R5 - address of the UCB (unit control block);m ; Outputs:;s;; The routine must preserve all registers except R0-R2, andR ; R9-R11.R;R;-- % UNIVERSAL_SYMBOL LD_DEALLOC_WATCHBUF_;LD_DEALLOC_WATCHBUF:r .JSB_ENTRY INPUT=1 TSTL R4 ; PCB available?) BEQL 50$ ; No, no need to synchronizeu5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level, SAVIPL=-(SP)e110$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entryt BVS 40$ ; None there' MOVL LDWATCH_L_PID(R0),R1 ; Get owner;" PUSHL R0 ; Save buffer address CMPL R1,PCB$L_PID(R4) ; Ours? BNEQ 20$ ; No1 MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to returnI- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaW BRB 30$ 20$: PUSHL R4  MOVL R1,R4 ; Get pid0 MOVL #LDWATCHPT_K_LENGTH,R1 ; Amount to return3 BSBW LD_RETURN_QUOTA ; Return to correct process MOVL (SP)+,R4,30$: MOVL (SP)+,R0 ; Restore bufferpointer0 CALL_DRVDEALMEM SAVE_R0R1=YES ; Dealloc memory BRB 10$440$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock& NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO BRB 60$150$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entryT BVS 60$ ; None there0 CALL_DRVDEALMEM SAVE_R0R1=YES ; Dealloc memory BRB 50$60$: RSB .PAGE7 .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routineR;+++1; LD_ENABLE_WATCH, Enable watchpoints FDT routineR;(; Functional description:l; /; This is the FDT routine to enable watchpoints,;$ ; Inputs:T; ; R0-R2 - scratch registers:.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block).; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)r*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;s ; Outputs:;i;; The routine must preserve all registers except R0-R2, andb ; R9-R11.7;b;--m! UNIVERSAL_SYMBOL LD_ENABLE_WATCH-;LD_ENABLE_WATCH: ! CLRL R9 ; Nothing charged yetR/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer address . MULL3 IRP$L_QIO_P2(R3),- ; Get buffer length #LDWATCHPT_K_LENGTH,R1 BEQL 30$ ; Nothing there' MOVQ R0,R10 ; Save address and sizeD0 CALL_WRITECHK ; Check buffer for read access2 MOVL IRP$L_QIO_P2(R3),R2 ; Get number of entries# MOVL R2,IRP$L_OBCNT(R3) ; Save it_;M; Validate inputbuffer; ;10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file mode  LDWATCHPT_W_FLAGS(R0),60$;5 TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBNM BEQL 40$D PUSHR #^MM3 MOVL LDWATCHPT_L_SBK(R0),R0 ; Get the SBK addressD, MOVL #SBK$K_LENGTH,R1 ; Get the SBK length, CALL_WRITECHK ; Check if SBK is readable .DISABLE FLAGGING- MOVL SBK$L_FCB(R0),R1 ; Get the FCB addresse .ENABLE FLAGGING  POPR #^M% BGEQ 20$ ; Must be system addressc6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return errore$ TSTL FCB$L_REFCNT(R1) ; File open? BEQL 20$ ; No, error3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too bigs FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quito& MOVL FCB$L_WLFL(R1),R1 ; Get any wcb7 CMPL R5,WCB$L_ORGUCB(R1) ; File must be on our devices! BNEQ 50$ ; Other device, quit 3 BBC #WCB$V_COMPLETE,- ; Must be completely mapped7 WCB$B_ACCESS(R1),20$! MOVL UCB$L_VCB(R5),R1 ; Get VCBh. TSTL VCB$L_RVN(R1) ; Relative volume number+ BEQL 70$ ; Branch if a not a volume set E MOVZWL #SS$_NOTVOLSET,R0 ; File watchpoint on volumeset not allowedx BRW 130$ C20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mapped_ BRW 130$-30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameter# BRW 130$m,40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$5750$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our device) "$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U" BRW 130$m@60$: BBS #LDWATCHPT_V_NOLBN,- ; Check for non-transfer function LDWATCHPT_W_FLAGS(R0),70$ 3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too big  UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quitM070$: MOVW LDWATCHPT_W_ACTION(R0),R1 ; Get action, CMPW R1,#WATCH_ACTION_MAX ; Legal action ? BGTRU 30$ ; No2 IFPRIV CMKRNL,80$ ; Sufficient to do everything- CMPW R1,#WATCH_ACTION_CRASH ; Crash system?k BEQL 120$ ; Yes, no priv9 CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device?S& BNEQ 110$ ; No, action not allowed;OC; We charge seperately for every packet. We need to do this becauseSG; EXE$DEBIT_BYTCNT_BYTLM_NW rounds the size up to 64 bytes. If we wouldoH; charge for it in one chunk we would get problems crediting the buffers!; which we need to do one by one. ;c(80$: PUSHL R0 ; Save, destroyed later4 MOVL #LDWATCHENT_K_LENGTH,R1 ; Bytecount to charge- ADDL2 R1,R9 ; Keep track of total charged0. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf left4 ADDL2 #4,SP ; Adjust stack/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quotao BRB 130$)C90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input entry  DECL R2 ; Next packet BLEQ 100$ BRW 10$7100$: MOVQ R10,R0 ; Get buffer address and bytecounts BSBW LD_GETBUF ; Get buffer BLBC R0,130$ ; Error( CALL_QIODRVPKT ; Finish in start I/O:110$: MOVZWL #SS$_NOPRIV,R0 ; Priv or ownership required BRB 130$_5120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv requiredt&130$: TSTL R9 ; Any BYTCNT charged? BEQL 140$ ; No PUSHL R0 ; Save status! MOVL R9,R0 ; Amount to returnU- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaL! MOVL (SP)+,R0 ; Restore status%140$: CALL_ABORTIO ; Abort the I/OJ .PAGE9 .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routineR;+++3; LD_DISABLE_WATCH, Disable watchpoints FDT routine,;;; Functional description:d;C0; This is the FDT routine to disable watchpoints;E ; Inputs:Y;; R0-R2 - scratch registersM.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)P.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)y*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;( ; Outputs:;o;; The routine must preserve all registers except R0-R2, andR ; R9-R11.i; ;--i" UNIVERSAL_SYMBOL LD_DISABLE_WATCH;LD_DISABLE_WATCH:$ CLRL IRP$L_OBCNT(R3) ; Zero count1 MOVL IRP$L_QIO_P2(R3),R1 ; Something specified?W BEQL 10$ ; Remove alls# MOVL R1,IRP$L_OBCNT(R3) ; Save itt2 MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer addresst0 CALL_WRITECHK ; Check buffer for read access$ BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Errorl,10$: CALL_QIODRVPKT ; Finish in start I/O 20$: CALL_ABORTIO ; Abort I/O .PAGE5 .SBTTL LD_GET_WATCH, Get watchpoint info FDT routine-;+++/; LD_GET_WATCH, Get watchpoint info FDT routined;s; Functional description:l;)8; This routine retrieves info about current watchpoints,(; as well as the suspended process list.;- ; Inputs: ; ; R0-R2 - scratch registersi.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)e.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)W*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers.; P1(AP) - Address of watchpt structure buffer*; P2(AP) - Size in bytes of watchpt buffer;n ; Outputs:;0;; The routine must preserve all registers except R0-R2, andD ; R9-R11.f;t;-- UNIVERSAL_SYMBOL LD_GET_WATCH;LD_GET_WATCH:+ BBS #LDIO_V_INQUIRE,- ; Return list size?  IRP$L_EXTEND(R3),10$/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer addressa. MOVL IRP$L_QIO_P2(R3),R1 ; Get buffer length0 CALL_READCHK ; Check buffer for write access% BSBW LD_GETBUF1 ; Get systembuffer BLBC R0,20$ ; ErrorY,10$: CALL_QIODRVPKT ; Finish in start I/O$20$: CALL_ABORTIO ; Abort the I/O .PAGEH .SBTTL LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;+++B; LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;C; Functional description:e;t:; This routine resumes threads which were suspended when a; 'suspend' watchpoint was hit;R ; Inputs:e;r; R0-R2 - scratch registersT.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)L.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block),*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;A ; Outputs:;s;; The routine must preserve all registers except R0-R2, andi ; R9-R11. ;e;-- ! UNIVERSAL_SYMBOL LD_RESUME_WATCH.;LD_RESUME_WATCH:g/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer addressc2 MOVL IRP$L_QIO_P2(R3),R1 ; Get number of entries2 MOVL R1,IRP$L_OBCNT(R3) ; Save number of entries BEQL 10$ ; Nothing there2 MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length0 CALL_WRITECHK ; Check buffer for read access$ BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Errorr,10$: CALL_QIODRVPKT ; Finish in start I/O$20$: CALL_ABORTIO ; Abort the I/O .PAGE= .SBTTL LD_GETBUF + LD_GETBUF1, Get and fill temporary buffer;;++++; LD_GETBUF, Get and fill temporary bufferL#; LD_GETBUF1, Get temporary bufferM;W; Functional description:I;OA; This routine allocates a buffer to use to get/send data to/fromC; start I/O routines. The userbuffer data will be copied (LD_GETBUF; only).;A ; Inputs:r;b; R0 - user buffer address; R1 - user buffer bytecount.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)3.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)5*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;d ; R0 - status ; R2 - sytem buffer address$; IRP$L_SVAPTE - systembuffer addres;t;; The routine must preserve all registers except R0-R2, and2 ; R9-R11.9;--- .ENABLE LSB UNIVERSAL_SYMBOL LD_GETBUF1 ;LD_GETBUF1:. .JSB_ENTRY INPUT=,OUTPUT=( CLRL -(SP) ; Flag not to fill buffer BRB 10$ UNIVERSAL_SYMBOL LD_GETBUFL ;LD_GETBUF:E. .JSB_ENTRY INPUT=,OUTPUT=( MOVZBL #1,-(SP) ; Flag to fill buffer210$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^Mt7 JSB G^EXE$DEBIT_BYTCNT_ALO ; Allocate a system buffer $ BLBC R0,30$ ; Any quota errors ? POPR #^M7 MOVL R2,IRP$L_SVAPTE(R3) ; Save system buffer address 4 MOVL R1,IRP$L_BOFF(R3) ; Save system buffer length2 MOVAB 12(R2),(R2) ; Insert address of data area0 MOVL R0,4(R2) ; Insert address of user buffer$ BLBC (SP),20$ ; Branch if no fill, PUSHR #^M ; Destroyed by MOVCH MOVC3 IRP$L_BCNT(R3),(R0),12(R2) ; Copy data from user- to systembuffer POPR #^M#20$: MOVZWL #SS$_NORMAL,R0 ; Okayo BRB 40$%30$: POPR #^M ; Error returnl"40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSB- .PAGE7 .SBTTL LD_MAKE_FILE_RESNAM, Form private resource namet;+++2; LD_MAKE_FILE_RESNAM, Form private resource name;O; Functional description:;@; Form resourcename for lock to coordinate clusterwide access to&; logical disk file or replaced device;L+; For a file this resourcename consists of:r;c; 9 bytes: '$LOGDISK_'E); 1 byte: 1 for private mounted volume,B; 2 for system wide; 12 bytes: volume lockname ; 3 bytes: 0W; 1 word: FID; 1 word: SEQ; 1 word: RVND;S; For a device this will be:;c; 9 bytes: '$LOGDISK_'R ; 1 byte: 0&; 1 byte: physical devicename length!; 20 bytes: alloclass devicename ;t ; Inputs:n; ; R3 IRP addressu; R4 PCB addresse; R5 UCB addressh&; R9 FCB address (connect 'file' only);o; Outputs:None.s;n3; Implicit outputs: Resource name in UCB is written ;c;---% UNIVERSAL_SYMBOL LD_MAKE_FILE_RESNAMl;LD_MAKE_FILE_RESNAM:m .JSB_ENTRY INPUT=8 PUSHR #^MlC#Vv$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U" MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource names! MOVZBL #31,(R1)+ ; Fill lengthp" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"e MOVL #^A/DISK/,(R1)+1 MOVB #^A/_/,(R1)+( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; FillerV PUSHL R4 ; Save PCB;# PUSHL R1 ; Save current pointer_- CLRB (R1)+ ; Length byte, filled in later(1 CALL_IOLOCKR SAVE_R1=YES ; Lock the IO databases .DISABLE FLAGGING CLRQ (R1) ; Clear buffer CLRQ 8(R1) CLRL 16(R1) .ENABLE FLAGGINGP MOVZBL #20,R0 ; Buffer size_! MOVZBL #1,R4 ; DVI$_ALLDEVNAM0 PUSHL R5 ; Save UCB5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB9. CALL_CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB" MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB* CALL_IOUNLOCK ; Unlock the IO database BRW 50$+10$: MOVB #1,R2 ; Assume private mountedP6 MOVL UCB$L_LD_PDUCB(R5),R0 ; Get physical device UCB( TSTL UCB$L_PID(R0) ; Private mounted? BNEQ 20$ ; Yes) INCB R2 ; System mounted, make it '2'i20$: MOVB R2,(R1)+! MOVL UCB$L_VCB(R0),R2 ; Get VCBu. TSTL VCB$L_RVN(R2) ; Relative volume number) BEQL 30$ ; Branch if not a volume set-+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addressM BEQL 30$ ; Not there5 MOVAB RVT$T_VLSLCKNAM(R0),R2 ; Use this as locknameC BRB 40$730$: MOVAB VCB$T_VOLCKNAM(R2),R2 ; Lock name from here 040$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+e MOVL (R2)+,(R1)+& CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+S/ MOVW FCB$W_FID_NUM(R9),(R1)+ ; Insert File IDU$ MOVW FCB$W_FID_SEQ(R9),(R1)+ ; SEQ$ MOVW FCB$W_FID_RVN(R9),(R1)+ ; RVN50$: POPR #^M RSB ; C'est toute .PAGEA .SBTTL LD_MAKE_DEV_RESNAM, Form private resource name for device3;+++<; LD_MAKE_DEV_RESNAM, Form private resource name for device;z; Functional description:u;f@; Form resourcename for lock to coordinate clusterwide access to; logical disk devicee;p.; The resource name is constructed as follows:;r; 9 bytes: '$LOGDISK_'o ; 1 byte: 0 ; 1 byte: LD devicename length!; 20 bytes: alloclass devicenameA;S ; Inputs: ;;; R3 IRP address#; R4 PCB addressn; R5 UCB address5; ; Outputs:None.P;f3; Implicit outputs: Resource name in UCB is writtenf; ;---$ UNIVERSAL_SYMBOL LD_MAKE_DEV_RESNAM;LD_MAKE_DEV_RESNAM: .JSB_ENTRY INPUT= PUSHR #^M5B MOVAB UCB$T_LD_DEV_RESNAM(R5),R1 ; Setup pointer to resource name! MOVZBL #31,(R1)+ ; Fill lengthR" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"M MOVL #^A/DISK/,(R1)+e MOVB #^A/_/,(R1)+ CLRB (R1)+ ; Filler  PUSHL R4 ; Save PCBe# PUSHL R1 ; Save current pointer5- CLRB (R1)+ ; Length byte, filled in later_1 CALL_IOLOCKR SAVE_R1=YES ; Lock the IO database .DISABLE FLAGGING CLRQ (R1) ; Clear buffer CLRQ 8(R1)0 CLRL 16(R1) .ENABLE FLAGGINGn MOVZBL #20,R0 ; Buffer sizeo! MOVZBL #1,R4 ; DVI$_ALLDEVNAM . CALL_CVT_DEVNAM ; Get alloclass devicename" MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB* CALL_IOUNLOCK ; Unlock the IO database POPR #^M RSB ; C'est toute .PAGE UNIVERSAL_SYMBOL LD_FDT_ACCESSc1;LD_FDT_ACCESS: ; General Control FDT routinea $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_ACCESS BRW LD_COMMON_FDT! UNIVERSAL_SYMBOL LD_FDT_DEACCESSn2;LD_FDT_DEACCESS: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_DEACCESS BRW LD_COMMON_FDT UNIVERSAL_SYMBOL LD_FDT_MODIFY 1;LD_FDT_MODIFY: ; General Control FDT routineu $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_ACP_MODIFY BRW LD_COMMON_FDT UNIVERSAL_SYMBOL LD_FDT_READBLK2;LD_FDT_READBLK: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_READBLKt BRW LD_COMMON_FDT# UNIVERSAL_SYMBOL LD_FDT_WRITECHECKF5;LD_FDT_WRITECHECK: ; General Control FDT routineA $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS< BBS #UCB$V_LD_VIRTUAL,- ; Check for container on NFS disk UCB$W_LD_FLAGS(R5),10$ CALL_WRITEBLK BRW LD_COMMON_FDT10$: MOVZWL #SS$_UNSUPPORTED,R0T BRW LD_COMMON_FDT UNIVERSAL_SYMBOL LD_FDT_MOUNT1;LD_FDT_MOUNT: ; General Control FDT routineS $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_MOUNTK BRW LD_COMMON_FDT$ UNIVERSAL_SYMBOL LD_FDT_LCLDSKVALID6;LD_FDT_LCLDSKVALID: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_LCLDSKVALID0 BRW LD_COMMON_FDT! UNIVERSAL_SYMBOL LD_FDT_ZEROPARMK3;LD_FDT_ZEROPARM: ; General Control FDT routine; $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_ZEROPARM BRW LD_COMMON_FDT UNIVERSAL_SYMBOL LD_FDT_ONEPARM2;LD_FDT_ONEPARM: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_ONEPARMD BRW LD_COMMON_FDT UNIVERSAL_SYMBOL LD_FDT_SETCHAR2;LD_FDT_SETCHAR: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_SETCHARo BRW LD_COMMON_FDT" UNIVERSAL_SYMBOL LD_FDT_SENSEMODE4;LD_FDT_SENSEMODE: ; General Control FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS CALL_SENSEMODEr; BRW LD_COMMON_FDTdLD_COMMON_FDT: PUSHR #^Me CLRL R15 BSBW LD_SAVE_TRACE_FDT ; Save FDT data, use R9-R10I POPR #^M RET .PAGE3 .SBTTL LD_FDT_DSE, Data secutiry erase fdt routines;+++.; LD_FDT_DSE, Data secutiry erase fdt routine;-; Functional description:s;s@; This is the FDT routine for the Data Security Erase operation.C; The byte count (P2) is stored in IRP$L_BCNT. The starting logical-?; block (P3) is stored in IRP$L_MEDIA. Control is transfered toRD; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start; I/O routine.; ; Inputs:T;L; R0-R2 - scratch registersi.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)s.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)a*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter; P2(AP) - Byte countA!; P3(AP) - Starting logical blockd;s ; Outputs:;K; IRP$L_BCNT(R3) - Byte counth*; IRP$L_MEDIA(R3) - Starting logical block;;; The routine must preserve all registers except R0-R2, andr ; R9-R11.B;F;--N UNIVERSAL_SYMBOL LD_FDT_DSE2;LD_FDT_DSE: ; Data security erase FDT routine $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS> MOVL IRP$L_QIO_P2(R3),IRP$L_BCNT(R3) ; Setup erase byte count@ MOVL IRP$L_QIO_P3(R3),IRP$L_MEDIA(R3); Setup erase starting LBN4 CALL_QIODRVPKT DO_RET=NO ; Send request to STARTIO BRW LD_COMMON_FDT .PAGEC .SBTTL LD_FDT_SHAD_WCHECK - Check write to shadow member for privst;+++;rD; LD_FDT_SHAD_RWCHECK - Check read/write to shadow mbr for privilege;u; Functional Description: ;$D; Allow only processes with SYS privilege to perform WRITES to0; Host Based Shadowing shadow set members.; ; Inputs:v;m; R3 IRP addressT$; R5 UCB address (member);l; Implicit inputs: None.;V; Outputs:None.0;;; Implicit outputs: None.e;m; Condition codes:;0H; SS$_ILLIOFUNC - I/O directed to shadow set member by a process5; that doesn't have sys priv.W;---$ UNIVERSAL_SYMBOL LD_FDT_SHAD_WCHECK;LD_FDT_SHAD_WCHECK: $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS4 BBC #DEV$V_SHD,- ; If this device is not a shadow- UCB$L_DEVCHAR2(R5),10$ ; set member, quit ) MOVL IRP$L_ARB(R3),R0 ; Get ARB address " BEQL 20$ ; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 329 BBC #PRV$V_SYSPRV,ARB$Q_PRIV(R0),20$; No SYSPRV, illegal$; BBC #UCB$V_LD_VIRTUAL,- ; Check for container on NFS disk UCB$W_LD_FLAGS(R5),10$" EXTZV #IRP$V_FCODE,#IRP$S_FCODE,-0 IRP$L_FUNC(R3),R0 ; Extract the function code4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch' CMPB R0,#IO$_WRITECHECK ; Writecheck?r BNEQ 10$ ; Noe0 MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported yet BRW 30$%10$: CALL_WRITEBLK ; Call WRITEBLKh. BRW LD_COMMON_FDT ; Continue FDT processing120$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error statuse330$: CALL_ABORTIO DO_RET=NO ; Complete I/O request  BRW LD_COMMON_FDT .PAGE, .SBTTL LD_FDT_CRESHAD - CRESHAD FDT routine, .SBTTL LD_FDT_REMSHAD - REMSHAD FDT routine;+++;;&; LD_FDT_CRESHAD$*$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U[" - CRESHAD FDT routine&; LD_FDT_REMSHAD - REMSHAD FDT routine; ; Functional Description:b;iB; Dispatch CRESHAD and REMSHAD requests to shadowing driver.; ; Inputs:u;n; R3 IRP addresse$; R5 UCB address (member);0-; Implicit inputs: Dispatch vector filled in.B;D; Outputs:None.u;r; Implicit outputs: None.;L; Condition codes:; 5; SS$_ILLIOFUNC - Dispatch vector not set up. D; SS$_DEVNOTSHR - Shadowset exists already somewhere in the cluster.; ;v;; The routine must preserve all registers except R0-R2, and4 ; R9-R11.;--- UNIVERSAL_SYMBOL LD_FDT_CRESHAD&;LD_FDT_CRESHAD: ; ----> IO$_CRESHAD UNIVERSAL_SYMBOL LD_FDT_REMSHAD&;LD_FDT_REMSHAD: ; ----> IO$_REMSHAD $DRIVER_FDT_ENTRY SAVE_IRP_FIELDS. MOVL G^EXE$GL_HBS_PTR,R0 ; Shadow Dispatcher' BGEQ 10$ ; Illegal if not filled in ; BBS #UCB$V_LD_VIRTUAL,- ; Check for container on NFS diskn UCB$W_LD_FLAGS(R5),20$5 PUSHL R6 ; Push CCB address,05 PUSHL R5 ; Push UCB address,O5 PUSHL R4 ; Push PCB address,n5 PUSHL R3 ; Push IRP address.l= CALLS #4,(R0) ; Jump to shadow dispatcher-. BRW LD_COMMON_FDT ; Continue FDT processing110$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status1 BRW 30$20$: MOVZWL #SS$_UNSUPPORTED,R0a330$: CALL_ABORTIO DO_RET=NO ; Complete I/O requests BRW LD_COMMON_FDT .PAGE: .SBTTL LD_CONTROL_INIT, Controller initialization routine;+++8; LD_CONTROL_INIT, Readies controller for I/O operations;A; Functional description: ; <; The operating system calls this routine in 3 places:;B!; at system startupI3; during driver loading and reloadingH4; during recovery from a power failure;tH; This routine is a NOP for driver reloading and power failure recovery.<; For system startup and driver loading it allocates a CDDB.;C;_ ; Inputs: ;FA; R4 - address of the CSR (controller status register)B;; R5 - address of the IDB (interrupt data block)D8; R6 - address of the DDB (device data block)<; R8 - address of the CRB (channel request block);n ; Outputs:;7; The routine must preserve all registers except R0-R3.;;---! UNIVERSAL_SYMBOL LD_CONTROL_INITe,;LD_CONTROL_INIT: ; Initialize controller $DRIVER_CTRLINIT_ENTRYc6 MOVB #SPL$C_IOLOCK8,- ; Init device spin lock index. CRB$B_FLCK(R8) PUSHQ R4 ; Save CSR & IDBl CLRL R4 ; Init errorflag1 MOVZWL IDB$W_UNITS(R5),R0 ; Get number of unitsa BRB 20$; H; Check for the correct UCB size if we're reloaded. If the size differs,/; then turn the unit offline to avoid problems.c; ,10$: MOVL IDB$L_UCBLST(R5)[R0],R1 ; Get UCB BEQL 20$ ; Not present6 CMPW UCB$W_SIZE(R1),#UCB$K_LD_UCBLEN ; Expected size? BEQL 20$ ; Yes;AH; Something's seriously wrong here. The UCB size of the currently loadedJ; driver is not the same as the new driver. We may not continue, but sinceE; the controllerinit routine does not return an exit status we cannots?; signal it to SYSGEN. What we do is set the unit offline here.Y;& CLRL CRB$L_AUXSTRUC(R8) ; Flag error8 BICL2 #UCB$M_ONLINE,UCB$L_STS(R1) ; Switch unit offline1 BICL2 #DEV$M_MSCP,- ; Zero this bit. Otherwiser2 UCB$L_DEVCHAR2(R1) ; 'show device' has problems ; if the CDDB is 0.F) MOVZBL #1,R4 ; Flag something's wrong#20$: SOBGEQ R0,10$ ; Next deviceh6 MOVZWL #SS$_INCOMPAT,R0 ; Assume incompatible driver$ TSTL R4 ; Did we have a problem? BNEQ 30$ ; Yes, quit1 TSTL CRB$L_AUXSTRUC(R8) ; Check if CDDB present " BEQL 40$ ; Branch if not there25$: MOVZWL #SS$_NORMAL,R0#30$: POPQ R4 ; Restore registersC& RET ; Otherwise, return to caller;e/; Create fork thread to finish controller init. ; 40$: MOVL R6,R4 ; Restore DDB MOVL R8,R5 ; Fork with CRB FORK CONTINUE=25$; ; Get pool for CDDB.;u) MOVZWL #CDDB$K_LENGTH,R1 ; Size of CDDBs, JSB G^EXE$ALONONPAGED ; Allocate some pool! BLBC R0,50$ ; Branch if error-* PUSHR #^M ; Save registers./ MOVC5 #0,(SP),#0,R1,(R2) ; Zero entire block.b2 POPR #^M ; Restore saved registers.; #; Initialize necessary CDDB fields. ;a MOVW R1,CDDB$W_SIZE(R2) ; Size' ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1G MOVW #>,- CDDB$B_TYPE(R2)& MOVL R5,CDDB$L_CRB(R2) ; CRB address& MOVL R4,CDDB$L_DDB(R2) ; DDB address8 MOVL R2,CRB$L_AUXSTRUC(R5) ; Save CDDB address in CRB.! MOVL DDB$L_UCB(R4),R5 ; Get UCBT/ BISW2 #DEV$M_MSCP,- ; Set mscp bit. Now safe0 UCB$L_DEVCHAR2(R5) ; because we've got a CDDB6 MOVL R2,UCB$L_CDDB(R5) ; Also in UCB (If first load). MOVAL CDDB$L_CDRPQFL(R2),- ; Init CDRP queue CDDB$L_CDRPQFL(R2) MOVAL CDDB$L_CDRPQFL(R2),-  CDDB$L_CDRPQBL(R2)7 MOVAL CDDB$L_RSTRTQFL(R2),- ; Init restart CDRP queues CDDB$L_RSTRTQFL(R2)f MOVAL CDDB$L_RSTRTQFL(R2),- CDDB$L_RSTRTQBL(R2)e, MOVL G^CLU$GL_ALLOCLS,- ; Allocation class CDDB$L_ALLOCLS(R2). MOVW CDDB$L_ALLOCLS(R2),- ; Allocation class CDDB$Q_CNTRLID(R2)% MOVW UCB$W_UNIT(R5),- ; Unit numberl CDDB$Q_CNTRLID+2(R2)& MOVL UCB$L_MEDIA_ID(R5),- ; Media_id CDDB$Q_CNTRLID+4(R2)$ BRB 60$ ; fails then second load ; needs to store this.%50$: MOVL DDB$L_UCB(R4),R5 ; Get UCB 1 BICW2 #DEV$M_MSCP,- ; Zero this bit. Otherwise2 UCB$L_DEVCHAR2(R5) ; 'show device' has problems ; if the CDDB is 0.-60$: RSB ; ReturnS .PAGE0 .SBTTL LD_CLONED_UCB, Cloned UCB initialization;+++*; LD_CLONED_UCB, Cloned UCB initialization;o; Functional description:U;EC; This routine is called by the $ASSIGN System Service to allow the,A; driver to initialize the cloned UCB. The driver is called withV; process context.;z ; Inputs: ; R0 = SS$_NORMAL; R2 = address of cloned UCB_; R3 = DDT addresst; R4 = PCB address3#; R5 = address of the template UCB<; IPL = ASTDEL;P ; Outputs:;;; R5 = address of cloned UCBM; Destroyed = R5; ;--v UNIVERSAL_SYMBOL LD_CLONED_UCB(;LD_CLONED_UCB:r $DRIVER_CLONEDUCB_ENTRY! MOVL R2,R5 ; Copy UCB addressu7 MOVL UCB$L_CPID(R5),UCB$L_LD_CPID(R5); Save charge pid# CLRL UCB$L_CPID(R5) ; Zero ID/ MOVW UCB$W_CHARGE(R5),- ; Save charged amount  UCB$W_LD_CHARGE(R5)f% CLRW UCB$W_CHARGE(R5) ; Zero chargeW; BISL2 #UCB$M_NOCNVRT,UCB$L_DEVSTS(R5) ; Inhibit logical to  ; physical xlation.D7 BRW LD_UNIT_INIT_CLONE ; Continue initing cloned UCB.;B .PAGE1 .SBTTL LD_UNIT_INIT, Unit initialization routine;+++/; LD_UNIT_INIT, Readies unit for I/O operations;u; Functional description:;oA; The operating system calls this routine after calling thei*; controller initialization routine:;i!; at system startup%; during driver loading:4; during recovery from a power failure(; during initialization of a cloned UCB; ; ; Inputs: ;A; R3 - address of the CSR (controller status register)b9; R5 - address of the UCB (unit control block) ;s7; IPL = IPL$_ASTDEL (jump/flow into from LD_CLONED_UCB) <; Note: If entered at this ipl, code now temporarily raises'; IPL to IPL$_RESCHED during the FORK.d@; = IPL$_POWER (powerfail recovery and unit initialization); ; Outputs:;o7; The routine must preserve all registers except R0-R1.E"; R0 = status (used only on alpha);A;--- UNIVERSAL_SYMBOL LD_UNIT_INIT$;LD_UNIT_INIT: ; Initialize Unit $DRIVER_UNITINIT_ENTRYS$ UNIVERSAL_SYMBOL LD_UNIT_INIT_CLONE;LD_UNIT_INIT_CLONE:) MOVL UCB$L_CRB(R5),R0 ; Get CRB addressA< TSTL CRB$L_AUXSTRUC(R0) ; Get CDDB address out of the CRB. BNEQ 10$ ; Okay^@ BICL2 #UCB$M_ONLINE,UCB$L_STS(R5) ; Mark cloned unit as offline:10$: BBS #UCB$V_POWER,UCB$L_STS(R5),20$ ; Did power fail ?6 MOVAL UCB$L_LD_AIOFL(R5),- ; Init the Act. I/O queue UCB$L_LD_AIOFL(R5) MOVAL UCB$L_LD_AIOFL(R5),-R UCB$L_LD_AIOBL(R5)? MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Init trace mutex wait queue  UCB$L_LD_TRCMUTEXQFL(R5)! MOVAL UCB$L_LD_TRCMUTEXQFL(R5),-u UCB$L_LD_TRCMUTEXQBL(R5)= MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Init trace data wait queueU UCB$L_LD_TRCWAITQFL(R5)d MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5)V2 MOVAL UCB$L_LD_WATCHQFL(R5),- ; Init watch queue UCB$L_LD_WATCHQFL(R5)  MOVAL UCB$L_LD_WATCHQFL(R5),- UCB$L_LD_WATCHQBL(R5)O- CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yet8 MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mut%vCea$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1UƊ"ex' CLRW UCB$W_LD_FLAGS(R5) ; Clear flagsB% MOVL G^EXE$GPQ_HWRPB,R0 ; Get HWRPBe .DISABLE FLAGGINGA MOVQ HWRPB$IQ_CYCLE_COUNT_FREQ(R0),- ; Save cyle count frequency+ UCB$Q_LD_CYCLEFREQ(R5) .ENABLE FLAGGINGC;_; Setup UCB CDDB field.m;u%20$: SAVIPL -(SP) ;Get current IPLm= CMPL (SP),#IPL$_RESCHED ;Compare to minimum Fork ipl needed / BGEQ 25$ ;Don't change IPL if not necessaryU5 SETIPL #IPL$_RESCHED,ENVIRON=UNIPROCESSOR ;Raise IPLKE25$: FORK ROUTINE=LD_UNIT_INIT_FORK,- ; Fork to allow controller inity, CONTINUE=30$ ; routine to complete firstM30$: SETIPL (SP)+,ENVIRON=UNIPROCESSOR ; Restore IPL to value before fork (" MOVZWL #SS$_NORMAL,R0 ; Success RETLD_UNIT_INIT_FORK: FORK_ROUTINE_) MOVL UCB$L_CRB(R5),R0 ; Get CRB address0> MOVL CRB$L_AUXSTRUC(R0),- ; Get CDDB address out of the CRB. UCB$L_CDDB(R5) BEQL 40$ ; Did not get one@ MOVL R5,UCB$L_DP_ALTUCB(R5) ; Point to ourself because CDP bit( ; is set (to prevent MSCP serving)( TSTW UCB$W_UNIT(R5) ; Is this unit 0? BNEQ 30$ ; BR if it isk2 CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit;l8; Setup MSCP stuff since some drivers need it (STdriver); .30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5)7 BISL2 #UCB$M_ONLINE,UCB$L_STS(R5) ; Switch unit online 40$: RSB .PAGE# .SBTTL LD_START, Start I/O routinee;+++2; LD_START - Start a transmit or receive operation;; Functional description: ;M;_ ; Inputs:;_; 4(AP) - KPB Address ;U ; Outputs:;PF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredl>; R1 - 2nd longword of I/O status: device-dependent; D; The routine must preserve all registers except R0-R2 and R4.; ;--- UNIVERSAL_SYMBOL LD_START&;LD_START: ; Process an I/O packet $DRIVER_START_ENTRY" MOVL 4(AP),R0 ; Get KPB address* MOVL KPB$PS_UCB(R0),R5 ; Get UCB address* MOVL KPB$PS_IRP(R0),R3 ; Get IRP address- MOVL R0,UCB$L_LD_KPB(R5) ; Save KPB addressG;nG; We will clear the BSY flag here so that if we are stalled in LD_TRACECH; other threads may start already, and won't be queued to our UCB. SinceH; we don't go through normal I/O completion (REQCOM) these I/O's will beI; hanging there for the rest of the system's life. LD_TRACE takes care of;; multiple threads.;U5 BICW2 #UCB$M_BSY,UCB$L_STS(R5) ; Clear the busy flage" EXTZV #IRP$V_FCODE,#IRP$S_FCODE,-0 IRP$L_FUNC(R3),R4 ; Extract the function code4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch;D(; Functions allowed with inactive device;5 CMPB R4,#IO$_LD_CONTROL ; Private control function?B BNEQ 10$ ; No6 BRW LD_START_CONTROL ; Handle our own function codes110$: BSBW LD_CHECK_WATCH ; Check for watchpointV8 BLBS IRP$L_EXTEND(R3),20$ ; Action done by CHECK_WATCH7 BBS #UCB$V_LD_CONSTS,- ; Device in DISCONNECT state ?r UCB$W_LD_FLAGS(R5),30$7 MOVZWL #SS$_DEVINACT,R0 ; Set status to dev. inactiveL&20$: BRW LD_DONE ; Complete the I/O830$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get UCB of phys. disk4 BBC #UCB$V_ONLINE,UCB$L_STS(R0),40$ ; Unit offline?A BBS #IRP$V_PHYSIO,IRP$L_STS(R3),50$ ; If set, phys. I/O functionA7 BBS #UCB$V_VALID,UCB$L_STS(R5),50$ ; If volume valid ?V7 MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalidS& BRW LD_DONE ; And complete the I/O; <; Someone pulled the device underneath us (it went offline).?; This may happen if we replaced a raid virtual unit with an LDF?; device. Subsequent unbinding of the raid array may take placeu<; without us knowing about it. Return SS$_DRVERR to the userA; (SS$_DEVOFFLINE would kick in mount verification, which i don't_#; want since this is a fatal error.o;n840$: MOVZWL #SS$_DRVERR,R0 ; Return drive error status& BRW LD_DONE ; And complete the I/O4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch=50$: DISPATCH R4,TYPE=B,<- ; Dispatch according to functionD ,- ; ^X00" ,- ; ^X01 ,- ; ^X02" ,- ; ^X04$ ,- ; ^X08( ,-; ^X0A( ,-; ^X0B& ,- ; ^X0C( ,- ; ^X11 > ; ^X15D: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code& BRB LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PACKACK ;LD_PACKACK:8 BISL2 #UCB$M_VALID,UCB$L_STS(R5) ; Set volume valid bit' BRB LD_NORMAL ; And complete the I/Ou UNIVERSAL_SYMBOL LD_AVAILABLE;LD_AVAILABLE: UNIVERSAL_SYMBOL LD_UNLOADa ;LD_UNLOAD:s> BICL2 #UCB$M_VALID,UCB$L_STS(R5) ; Clear the volume valid bit# ; Fall through into LD_NORMALt UNIVERSAL_SYMBOL LD_DRVCLRi ;LD_DRVCLR:t UNIVERSAL_SYMBOL LD_SEEKu ;LD_SEEK:I UNIVERSAL_SYMBOL LD_NOP;LD_NOP: UNIVERSAL_SYMBOL LD_NORMAL ;LD_NORMAL: 9 BBS #UCB$V_LD_REPLACE,- ; More to do for replaced drivep" UCB$W_LD_FLAGS(R5),LD_PROCESS_IO CLRL R2 ; Dummy LBN 0 MOVZWL #SS$_NORMAL,R0 ; Set status to success UNIVERSAL_SYMBOL LD_DONEl.;LD_DONE: ; Driver processing is finished.* CLRL R1 ; Clear second status longword LD_DONE1:h' BSBW LD_SAVE_TRACE ; Save trace data KP_REQCOM ; Complete I/O.i .PAGE;+++K; The IRP is now setup to be transferred to the physical disk. The phys.K; disk UCB is taken and it's FLCK is compared to our FLCK. If our FLCK islK; lower, we raise IPL to synchronize. We queue the IRP to the Phys. diskr; driver, and simply return.K; The phys. disk driver will call I/O completion to get rid of the IRP etc.$K; Because we do not know if the Phys. disk requires the Block nr in phys. ,eK; or Logical format, we first convert it to Logical, because that is whatrK; we need, add the Starting LBN of the LD File, and go through the conver-N,; sion process again, like in the FDT rtn's.;--- UNIVERSAL_SYMBOL LD_DSE;LD_DSE:2 BBC #UCB$V_LD_NODSE,- ; Check for DSE support on) UCB$W_LD_FLAGS(R5),- ; physical driver LD_TRANSFER_W ' MOVZWL #SS$_NORMAL,R0 ; Fake successg; ASHL #16,IRP$L_BCNT(R3),R1 ; Move lower half of bytecounte8 BISL2 R1,R0 ; Combine status and lower part of count; MOVZWL IRP$L_BCNT+2(R3),R1 ; Move upper half of bytecountt BRW LD_DONE1  UNIVERSAL_SYMBOL LD_TRANSFER_Wu;LD_TRANSFER_W: 4 BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFERw9 MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock statuse BRW LD_DONE UNIVERSAL_SYMBOL LD_TRANSFERD ;LD_TRANSFER:K* MOVL IRP$L_MEDIA(R3),R2 ; Save for later1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/Oi UCB$W_LD_FLAGS(R5),20$: BBC #IRP$V_PHYSIO,IRP$L_STS(R3),10$ ; Is it a phys. I/O ?< BBS #IRP$V_SHDIO,IRP$L_STS2(R3),10$ ; Is it shadowing I/O ?, BSBW LD_CNVRTOLOG ; Convert to a log. I/O;10$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page countL. ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust8 CMPL R0,UCB$L_MAXBLOCK(R5) ; Does it fit on our disk ?" BLEQU LD_PROCESS_IO ; Yep, skip BRW 30$ ; Return error;20$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count0. ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust$ MOVL UCB$L_LD_FCB(R5),R1 ; Get FCB5 CMPL R0,FCB$L_EFBLK(R1) ; Does it fit on our disk ? BGTRU 30$ ; No, quit" EXTZV #IRP$V_FCODE,#IRP$S_FCODE,-0 IRP$L_FUNC(R3),R0 ; Extract the function code4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch( CMPB R0,#IO$_WRITECHECK ; Write check? BNEQ LD_PROCESS_IO ; No4 MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported for now BRW LD_DONE230$: MOVZWL #SS$_ILLBLKNUM,R0 ; Set return status& BRW LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PROCESS_IOs;LD_PROCESS_IO:32 MOVL IRP$L_BCNT(R3),R1 ; Set original byte count PUSHR #^M MOVL R1,R6 ; Copy in to R6> ADDL3 #1,IRP$L_MEDIA(R3),R7 ; Copy LBN -> R7, VBN start at 1; F; Allocate and initialize an LDIOB to describe the I/O and the connec-I; tion to the physical disk. Insert the LDIOB in the active UCB I/O queue ; . BSBW LD_ALLO_LDIOB ; Allocate a LIODB block BLBS R0,5$ ; Ok-3$: POPR #^M! BRW LD_DONE ; Quit with errorI5$: MOVL R2,R4= MOVW #SS$_NORMAL,LDIOB_W_IOST(R4) ; Initialize return statusH: MOVL IRP$L_PID(R3),LDIOB_L_PID(R4) ; Set PID val in LDIOB0 MOVL R3,LDIOB_L_IRP(R4) ; Set IRP adr in LDIOB .DISABLE FLAGGING, CLRQ LDIOB_Q_ST_TIME(R4) ; Zero start time. CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time .ENABLE FLAGGING @ MOVL IRP$PS_KPB(R3),LDIOB_L&4x$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Ub`"_KPB(R4) ; Save original KPB address2 KP_ALLOCATE_KPB KPB=IRP$PS_KPB(R3) ; Allocate KPB BLBC R0,3$ ; Error* MOVL IRP$PS_KPB(R3),R0 ; Get KPB address2 MOVL R5,KPB$PS_UCB(R0) ; Save UCB address in KPB* TSTL UCB$L_LD_TRCBUF(R5) ; Trace active? BEQL 10$ ; No, save overhead- MOVL IRP$L_MEDIA(R3),- ; Copy Media addressB LDIOB_L_MEDIA(R4) 6 MOVL IRP$L_BCNT(R3),LDIOB_L_BCNT(R4) ; Copy Bytecount9 MOVW IRP$L_FUNC(R3),LDIOB_W_FUNC(R4) ; Copy FunctioncodeD .DISABLE FLAGGING. READ_SYSTIME LDIOB_Q_ST_TIME(R4) ; Start time .ENABLE FLAGGINGr1 BBC #UCB$V_LD_ACCURATE,- ; Check if RSCC wantedi UCB$W_LD_FLAGS(R5),10$) EVAX_RSCC ; Get starting RSCC counter:0 EVAX_STQ R0,LDIOB_Q_ST_RSCC(R4) ; Start counter710$: INSQUE LDIOB_L_QFL(R4),- ; Insert at end of queue  @UCB$L_LD_AIOBL(R5)a0 MOVL UCB$L_LD_WCB(R5),R2 ; Get our WCB address& CLRL R9 ; Total # of bytes written;F; For every mapped I/O segment, allocate and initialize a forward IRP.F; The transfer parameters are copied into the forward IRP, and the IRP; is queued to the LDIOB. ;r ; R2 - WCB; R3 - IRP (org) ; R4 - LDIOB; R5 - UCB (org); R6 - Byte counti; R7 - Segment block number ; R9 - # of bytes written so far;s30$: PUSHR #^M1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O- UCB$W_LD_FLAGS(R5),32$3 BBC #UCB$V_LD_REPLACE,- ; Skip mapping if replacei UCB$W_LD_FLAGS(R5),35$032$: MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the PDUCB! CLRL R2 ; Remaining bytecountR% MOVL IRP$L_MEDIA(R3),R1 ; First LBNR BRB 40$+35$: MOVL R7,R0 ; Restore segment numbere" MOVL R6,R1 ; Restore bytecount" BSBW LD_MAPVBLK ; Map a section+ BLBS R0,40$ ; Total map failure = FATALr;h8; Something is wrong. Make matters worse (a lot worse).;T BUG_CHECK INCONSTATE,FATAL ; B; R5 now contains the address of the UCB on which this part of the; file is located.;s.40$: MOVL R5,R8 ; Save physical UCB address* MOVL 12(SP),R5 ; Recover LD UCB address MOVL R3,R0 ; Copy IRP to R0e. BSBW LD_ALLO_FWIRP ; Allocate a FWIRP block1 SUBL3 R2,R6,IRP$L_BCNT(R3) ; Set the byte counts> MOVAB LD_COMPLETE,IRP$L_PID(R3) ; Initialize callback address9 MOVL IRP$L_BOFF(R0),IRP$L_BOFF(R3) ; Set the byte offset$4 MOVB IRP$B_PRI(R0),IRP$B_PRI(R3) ; Set the priority6 MOVL IRP$L_FUNC(R0),IRP$L_FUNC(R3) ; Set the function1 BBC #UCB$V_LD_VIRTUAL,- ; Check for virtual I/Ot UCB$W_LD_FLAGS(R5),43$0 MOVL UCB$L_LD_WCB(R5),- ; Get our WCB address,) IRP$L_WIND(R3) ; used for Virtual I/OA.43$: MOVL IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3)# CLRL IRP$L_STS(R3) ; Init statusR7 BBC #UCB$V_LD_REPLACE,- ; Skip status copy if replace; UCB$W_LD_FLAGS(R5),45$5 MOVL IRP$L_STS(R0),IRP$L_STS(R3) ; Initialize statuse BRB 50$945$: BBC #IRP$V_FUNC,IRP$L_STS(R0),50$ ; Chk the R/W flage3 BISL2 #IRP$M_FUNC,IRP$L_STS(R3) ; Set the R/W flag *50$: MOVL R2,R6 ; Copy 'bytecount left'+ BBS #UCB$V_LD_REPLACE,- ; Skip if replace. UCB$W_LD_FLAGS(R5),60$@ ADDL3 R9,IRP$L_BOFF(R0),R2 ; Get current bytes copied + offset2 ADDL2 IRP$L_BCNT(R3),R9 ; Total bytecount so far9 MCOML G^MMG$GL_BWP_MASK,R22 ; Mask for byte within page;* BICL3 R22,R2,IRP$L_BOFF(R3) ; New offsetC MOVL G^MMG$GL_VPN_TO_VA,R22 ; Shift value to convert to pte index % EVAX_SRL R2,R22,R2 ; Form PTE index0 MOVL IRP$L_SVAPTE(R0),R22 ; Get SVAPTE address: MOVAQ (R22)[R2],IRP$L_SVAPTE(R3) ; Set new SVAPTE address7 ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count4* ADDL2 R0,R7 ; Add it to the blk number060$: MOVL R5,IRP$L_LD_LDUCB(R3) ; Set the LDUCB, MOVL R4,IRP$L_LD_LDIOB(R3) ; Set the LDIOB) MOVL R1,R0 ; Set logical block numberE. MOVL R1,IRP$L_MEDIA(R3) ; Assume virtual I/O PUSHL R5 ; Save LD UCB MOVL R8,R5 ; Get the PDUCB' MOVL R5,IRP$L_UCB(R3) ; Set the PDUCBB1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/OB UCB$W_LD_FLAGS(R5),65$2 CALL_CVTLOGPHY ; Convert to physical structure ; and fill IRP$L_MEDIA965$: INSQUE IRP$L_LD_FWDQFL(R3),- ; Place FWIRP in LDIOBA @LDIOB_L_FWDQBL(R4)c; ADAWI #1,LDIOB_W_IRPCNT(R4) ; Increment number of FW IRPso! MOVL (SP)+,R5 ; Restore LD UCB;VF; Queue the I/O to the physical driver. If the device is not busy thenF; pass the IRP to the START I/O routine. If the device is busy, then!; place it in the I/O wait queue.s;e3 MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock index2> CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R8) ; Compare (negative index)* BLSS 70$ ; Ok, ours is larger or equal4 MOVZBL UCB$B_FLCK(R8),R2 ; Get the lowest of the 2&70$: FORKLOCK LOCK=R2,- ; Synchronize SAVIPL=-(SP),-E PRESERVE=NO PUSHL R2 ; Save lock# PUSHL R5 ; Save our UCB addressu- MOVL R8,R5 ; Setup Phys. disk UCB address  .PRESERVE ATOMICITY1 INCL UCB$L_QLEN(R5) ; Bump device queue lengthw .NOPRESERVE ATOMICITY< BBSS #UCB$V_BSY,UCB$L_STS(R5),80$ ; Check if device is busy- CALL_INITIATE ; Initiate the I/O functions BRB 90$ ; Common code flow>80$: MOVAL UCB$L_IOQFL(R5),R2 ; Get addr. of I/O queue listhd0 CALL_INSERT_IRP ; Insert IRP in device queue&90$: MOVL (SP)+,R5 ; Restore our UCB MOVL (SP)+,R2 ; Restore lock2% FORKUNLOCK LOCK=R2,- ; Release lock_ NEWIPL=(SP)+,-P PRESERVE=NO,- CONDITION=RESTORE POPR #^M$ TSTL R6 ; Anything left to map ? BLEQU 100$ ; No, complete BRW 30$ ; Yes map rest100$: POPR #^M RET ; And simply return .PAGE;+++0; Convert a physical block into a logical block.5; Modify IRP$L_MEDIA into LBN instead of TRK/CYL/SEC.UG; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTORt;--- UNIVERSAL_SYMBOL LD_CNVRTOLOG;LD_CNVRTOLOG: .JSB_ENTRY INPUT=y PUSHR #^Mt& MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C% MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get C  MULL2 R0,R4 ; R4=C*(T/C)% MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get T  ADDL R4,R2 ; R2=C*(T/C)+Tr' MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/Ti& MULL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)# MOVZBL IRP$L_MEDIA(R3),R4 ; Get SC( ADDL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)+S, MOVL R2,IRP$L_MEDIA(R3) ; Put LBN into IRP POPR #^M RSB ; Return to callerr .PAGE0 .SBTTL LD_MAPVBLK, Map virtual to logical block;++++; LD_MAPVBLK, Map virtual to logical blockn;h; Functional description:R;p<; This routine is called to map a virtual block to a logical; block using a mapping window. ;i ; Inputs:t;-; R0 - Virtual block numbery; R1 - Number of bytes to mapL; R2 - WCB address9; R3 - address of the IRP (I/O request packet)N&; R4 - address of the LDIOB9; R5 - address of the UCB (unit control block)R;d ; Output:;R,; R0 - Low bit clear = total mapping failure&; R0 - Low bit set = partial map with:+; R1 = Logical block number of first blocks ; R2 = Number of unmapped bytes; R5 = UCB of physical unit;lD; The routine must preserve all registers except R0-R2 and R4.;O;---;M UNIVERSAL_SYMBOL LD_MAPVBLK ;LD_MAPVBLK:: .JSB_ENTRY INPUT=,OUTPUT= .IF DF V82_UP;_A; Get the FILSYS spinlock while we hold IOLOCK8. Try NOSPIN firstT;; because the rank is out of order, and if that fails fork. ;a PUSHR #^M/ LOCK LOCKNAME=FILSYS,- ; Acquire FILSYS lock,) SPIN=NO,- ; and don't spinM PRESERVE=NOA BLBC R0,10$ ; Failure....e POPR #^M(7 BSBW LD_MAPVBLK_CALL ; Call the real mapping routine(7 UNLOCK LOCKNAME=FILSYS,- ; Get rid of FILSYS spinlockV CONDITION=RESTORET BRW 30$ ; Done10$: PUSHR #^M3 MOVL LDIOB_L_KPB(R4),R0 ; Get KPB for this thread 0 MOVAB KPB$PS_FQFL(R0),R5 ; Point to FKB in KPB7 MOVB #SPL$C_FILSYS,FKB$B_FLCK(R5) ; Set spinlock index_7 FORK ROUTINE=LD_MAPVBLK_FORK,- ; Call the fork routineB CONTINUE=20$20$: POPR #^Mu+ MOVL LDIOB_L_KPB(R4),R0 ; Get KPB addresse1 KP_STALL_GENERAL KPB=R0,- ; Wait for restart byh0 STALL_ROUTINE=G^IOC$RETURN ; the fork routine POPR #^M;7 BSBW LD_MAPVBLK_CALL ; Call the real mapping routinek30$: RSB;o.; Call IOC_STD$MAPVBLK to get the real mapping;f! UNIVERSAL_SYMBOL LD_MAPVBLK_CALLN;LD_MAPVBLK_CALL:t7 .JSB_ENTRY INPUT=,OUTPUT=;) SUBL2 #12,SP ; Allocate scratch space_) MOVL SP,R22 ; Pointer to scratchspace / PUSHL #1 ; Flag that we already have FILSYSa1 PUSHAB 8(R22) ; Push address for UCB (output) 3 PUSHAB 4(R22) ; Push address for bytes (output) 0 PUSHAB (R22) ; Push address for LBN (output)@ PUSHL WCB$L_ORGUCB(R2) ; Get ucb'hRZ$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U" of volume containing the file PUSHL R3 ; IRP PUSHL R2 ; WCB% PUSHL R1 ; Number of bytes to map # PUSHL R0 ; Virtual Block numberm, CALLS #9,G^IOC_STD$MAPVBLK ; Make the call4 MOVL (SP),R1 ; Get the LBN in R1 for 1st output.8 MOVL 4(SP),R2 ; Get the # bytes in R2 for 2nd output.4 MOVL 8(SP),R5 ; Get the UCB in R5 for 3nd output.+ ADDL2 #12,SP ; Deallocate scratch spacer RSB;eH; This is the forkroutine called if we failed to get the FILSYS spinlockI; on the first try, meaning another CPU has got it. If the forkdispatcher/H; called us here we have FILSYS, so get IOLOCK8 (so that we have them inE; the right order). We use the KPB from the input LDIOB to resume thet; calling thread.o;n; Input: R4 = LDIOBe;t! UNIVERSAL_SYMBOL LD_MAPVBLK_FORK;LD_MAPVBLK_FORK:D FORK_ROUTINER' LOCK LOCKNAME=IOLOCK8,- ; Get IOLOCK8S PRESERVE=NOV/ KP_RESTART KPB=LDIOB_L_KPB(R4) ; Resume threadR- UNLOCK LOCKNAME=IOLOCK8,- ; Release IOLOCK8R CONDITION=RESTORE,-M PRESERVE=NO_# RSB ; Return to forkdispatchere ; which releases FILSYSo .IFFw1 PUSHR #^M ; Save registers  .DISABLE FLAGGING& MOVAB -1(R0),R7 ; Save start vbn -1 .ENABLE FLAGGINGO+ MOVL R2,R3 ; Get copy of window addressaB MOVL WCB$L_ORGUCB(R2),R5 ; Get ucb of volume containing the file;G; The window may consist of a chain of wcb segments. search through theyE; chain until we find one which is beyond the desired vbn or we reacha; the end of the chain.S; E10$: CMPL R0,WCB$L_STVBN(R3) ; Check vbn against start vbn of windowi- BLSSU 20$ ; Branch if vbn precedes windowL4 MOVL R3,R2 ; Else advance to this window segment6 MOVL WCB$L_LINK(R2),R3 ; Look at next window segment- BNEQ 10$ ; Branch to look at if it existsn>20$: MOVL WCB$L_NMAP(R2),R3 ; Get count of retrieval pointers% BEQL 40$ ; Branch if empty windowN2 MOVAL WCB$L_STVBN(R2),R4 ; Point to starting vbn6 SUBL2 (R4)+,R0 ; Subtract starting vbn from desired- BLSSU 40$ ; Branch if vbn precedes window$&ASSUME WCB$L_P1_COUNT EQ WCB$L_STVBN+4ASSUME WCB$L_COUNT EQ 0BASSUME WCB$L_LBN EQ 4fASSUME WCB$L_RVN EQ 8S4WCB$C_MAP_PTR_LENGTH = - WCB$L_COUNT!ASSUME WCB$C_MAP_PTR_LENGTH EQ 12 ;vG; Scan the window, subtracting the count field of each pointer from the ; current relative block number.;O30$:7 MOVL (R4)+,R9 ; Get count field of retrieval pointere5 SUBL2 R9,R0 ; Subtract from relative block numbert5 BLSSU 50$ ; Branch if vbn located in this pointerd3 ADDL2 #8,R4 ; Skip lbn and rvn field of pointer.$ SOBGTR R3,30$ ; Loop thru window ; Vbn is beyond window+40$: CLRL R8 ; Indicate no blocks mappedtG MOVL WCB$L_ORGUCB(R2),R5 ; Redirect ucb to volume containing the filer BRB 70$ ; Return failure; A; Found the retrieval pointer containing the starting vbn. R0 nowpA; contains a negative value which is the number of blocks betweenD.; the starting vbn and the end of the pointer.;N950$: MNEGL R0,R8 ; Save # blocks mapped past start vbnL1 ADDL2 (R4)+,R9 ; First lbn beyond this pointer,( ADDL3 R9,R0,R1 ; Compute starting lbn MOVL (R4)+,R0 ; Get rvn field;EE; If the next retrieval pointer is contiguous with the one found, add F; in its count to handle the case where a transfer spans two pointers.E; Note that the greatest number of contiguous pointers a transfer cane; span is two.;E- DECL R3 ; See if there is another pointerC BLEQ 60$ ; Branch if noneo6 MOVL (R4)+,R3 ; Get count of next retrieval pointer8 CMPL R9,(R4)+ ; See if the next pointer is contiguous BNEQ 60$ ; Branch if not) CMPL R0,(R4) ; See if RVN is the same  BNEQ 60$ ; Nol( ADDL2 R3,R8 ; Add to # blocks mapped;E; Extract the lbn and rvn components of the starting "lbn" and switch1; to the right ucb if this is a multi-volume set.I;S60$:< MOVZBL R0,R0 ; Extract rvn (R1 already has starting lbn)' BEQL 70$ ; Branch if not volume setA8 MOVL WCB$L_RVT(R2),R6 ; Get relative volume table addr8 CMPB R0,RVT$B_NVOLS(R6) ; Volume mapped within ucblst?$ BGTRU 90$ ; No, force windowturn; MOVL RVT$L_UCBLST-4(R6)[R0],R5 ; Get the right ucb addresso;B; Check the range of vbn's provided by the map pointer against theA; file size recorded in the fcb. Reduce it if the fcb indicates an$; smaller file size than the window.;270$: BBS #WCB$V_NOTFCP,- ; Skip check if no fcb WCB$B_ACCESS(R2),110$l) MOVL WCB$L_FCB(R2),R6 ; Get fcb address @ SUBL3 R7,FCB$L_FILESIZE(R6),R4 ; Compute blocks to physical eof. BLEQU 90$ ; Branch if vbn past end of file. CMPL R8,R4 ; Compare against blocks mapped/ BLEQU 80$ ; Branch if less mapped by window ( MOVL R4,R8 ; Else limit to file size;rF; For read I/O's, check the range of vbn's provided by the map pointerC; against the current highwater mark and set the appropriate status ?; flags. Limit the length of the transfer to highwater mark. By:F; checking reads here, highwater mark flagging and checking is done onC; a segment by segment basis. Writes must be flagged for the entire;*; I/O, and hence are handled in sysacpfdt.;M+80$: MOVL 4(SP),R3 ; Get back irp address 0 BBC #IRP$V_FUNC,- ; Branch if this is a write IRP$L_STS(R3),110$1 BICL2 #IRP$M_START_PAST_HWM!IRP$M_END_PAST_HWM,-U. IRP$L_STS2(R3) ; Init highwater mark flags$ INCL R7 ; Back to true start vbn6 SUBL3 R7,FCB$L_HIGHWATER(R6),R7 ; Blocks to highwater' BGTRU 100$ ; Branch if not past hwmW; TSTL FCB$L_HIGHWATER(R6) ; See if highwater marking is onM BEQL 110$ ; Branch if notf: BISL2 #IRP$M_START_PAST_HWM,- ; Note past highwater mark IRP$L_STS2(R3)+90$: CLRL R8 ; Indicate no blocks mapped; BRB 110$ ; And exitS=100$: CMPL R8,R7 ; Does the transfer extend past highwaterD BLEQU 110$ ; Branch if not- MOVL R7,R8 ; Limit read to highwater marku;e4; See if the entire transfer is mapped contiguously.;t=110$: MOVL (SP),R2 ; Get number of bytes to map from inpute CLRL R0 ; Assume failure TSTL R8 ; Any blocks mapped?* BEQL 130$ ; Branch if no blocks mappedS EVAX_SLL R8,#IOC$V_BLOCK_BLKNUM,-; R8 = # of bytes (64 Bits) mapped in this extente R8D9 EVAX_SUBQ R2,R8,R2 ; R2 = # of unmapped bytes (64 Bits)C' EVAX_CMOVLT R2,#0,R2 ; R2 = MAX(0,R2)o3 MOVL R2,(SP) ; Save unmapped bytes (R2 on exit)C.120$: MOVL #SS$_NORMAL,R0 ; Indicate success8130$: POPR #^M ; Restore registers RSB .ENDC .PAGE2 .SBTTL LD_START_CONTROL, Start LD control routine;+++-; LD_START_CONTROL, Start LD control routined;r; Functional description:M;WB; This is the completion of the control processing. This has to be=; in system context since we may need to fork when we use theO); forklevel interface to the lockmanager. ;e ; Inputs:S;#9; R3 - address of the IRP (I/O request packet)#9; R5 - address of the UCB (unit control block);lD; The routine must preserve all registers except R0-R2 and R4.;n;---" UNIVERSAL_SYMBOL LD_START_CONTROL;LD_START_CONTROL: ASSUME LDIO_S_FUNC EQ 8F DISPATCH IRP$L_EXTEND(R3),TYPE=B,<- ; Dispatch according to function$ ,-. ,-0 ,-( ,-. ,-6 ,-% ,-_) >E; MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function coden CLRL R1 KP_REQCOM ; Complete I/O. .PAGE3 .SBTTL LD_START_CONNECT, LD Start connnect routineR;+++.; LD_START_CONNECT, LD Start connnect routine;0; Functional description:B;TB; This is the completion of the connect processing. This has to be=; in system context since we may need to fork when we use the_); forklevel interface to the lockmanager.B;( ; Inputs:B;c9; R3 - address of the IRP (I/O request packet)c9; R5 - address of the UCB (unit control block) ;t ; Outputs:;BF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredg; R1 - 0_; D; The routine must preserve all registers except R0-R2 and R4.;;;---" UNIVERSAL_SYMBOL LD_START_CONNECT;LD_START_CONNECT:' BBC #LDIO_V_SHARE,- ; Shared access?R IRP$L_EXTEND(R3),10$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBe, BBS #DEV$V_CLU,- ; Cluster wide visible ? UCB$L_DEVCHAR2(R1)(0l$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Uy",10$6 MOVZWL #SS$_ACCONFLICT,R0 ; Not cluster-wide visible BRW 180$2A10$: BSBW LD_ENQ_LD_LOCK ; Get lock of container file or device  BLBS R0,15$ ; Ok BRW 190$ ; It's not allowed 315$: BSBW LD_ENQ_DEV_LOCK ; Get lock of LD device# BLBS R0,30$ ; It's allowed* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status+ DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5),-c! VALBLOCK=UCB$A_LD_FILE_LVB(R5): ; Get rid of file lock1 MOVL UCB$L_LD_SAVST(R5),R1 ; Get original error BLBC R0,20$ ; Dequeue Error & MOVL R1,R0 ; Return original error 20$: BRW 180$m,30$: BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),40$$ BRW 60$ ; Skip file manipulation440$: MOVL UCB$L_LD_FCB(R5),R2 ; Recover FCB address7 INCL FCB$L_REFCNT(R2) ; Increment the reference count3 INCL FCB$L_ACNT(R2) ; Increment the access counts5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB & MOVL UCB$L_VCB(R1),R0 ; Get it's VCB7 ADAWI #1,VCB$L_TRANS(R0) ; Bump the transaction countR. TSTL VCB$L_RVN(R0) ; Relative volume number) BEQL 50$ ; Branch if not a volume sett7 BISW2 #UCB$M_LD_ONVOLSET,- ; Flag container file on ai! UCB$W_LD_FLAGS(R5) ; volumeset_( ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ850$: MOVL FCB$W_FID_NUM(R2),- ; Save FID for crosscheck UCB$W_LD_FID_NUM(R5) MOVW FCB$W_FID_RVN(R2),-i UCB$W_LD_FID_RVN(R5);-8; Check if the specified containerfile resides on an NFS6; mounted disk. In that case we need to do virtual I/O=; to the file, this will be indicated by the UCB$V_LD_VIRTUAL-; bit set in UCB$W_LD_FLAGS.;E% MOVL UCB$L_DDB(R1),R1 ; Get the DDBu .DISABLE FLAGGING CMPL DDB$T_NAME_STR(R1),- #^A/DNFS/ .ENABLE FLAGGING) BNEQ 55$ ; No match,< BISW2 #UCB$M_LD_VIRTUAL,- ; Indicate virtual access needed UCB$W_LD_FLAGS(R5)55$:;tJ; Convert the WCB to a shared WCB. This must be done in process context asJ; the FILSYS spinlock is needed, and we currently have IOLOCK8 which has aJ; higher rank. LD_RETURN_QUOTA will queue an ast to the requesting processH; which will do the job. By using a special kernel ast we make sure that1; it is done before the user deaccesses the file.h;p+ MOVL IRP$L_PID(R3),R4 ; Process to credits$ MOVL UCB$L_LD_WCB(R5),R1 ; Get WCB8 BSBW LD_RETURN_QUOTA ; Make WCB shared & return quota BRW 140$n;; Replace drive processing; H; Take out devicelock for physical device to protect against access fromH; another cluster member. If it fails then the device is in use, we will?; check later on if it was by another LDdriver or someone else.3;;G60$: MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource name " MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer sizeU! MOVZBL #1,R4 ; DVI$_ALLDEVNAM PUSHL R5 ; Save UCB 5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCBi. CALL_CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save lengthM UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-A# RESNAME=UCB$T_LD_PD_RESNAM(R5),-n EFLAGS= BLBS R0,90$ ; Got it3 CMPW R0,#SS$_NOTQUEUED ; Any one else interested?  BNEQ 100$ ; No, other errorT;OK; Someone owns the devicelock in an incompatible way. Check if it's anothereO; LDdriver by enqueueing a NL lock and checking bit 15 of the lock value block.BL; If it's set then another LDdriver allocated the device for replacement, in; that case we may continue;;G ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-# RESNAME=UCB$T_LD_PD_RESNAM(R5),-P EFLAGS= BLBC R0,100$ ; Error2 MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Enqeueue status BLBC R0,100$ ; Error;I9; Get rid of NL lock now that we have the lockvalue blockR;A' DEQ_LOCK LOCKID=UCB$Q_LD_PD_LKSB+4(R5)8! BLBC R0,100$ ; Deqeueue error_2 CLRL UCB$Q_LD_PD_LKSB+4(R5) ; Not queued anymore; >; Check for bit 15, if set by another LDdriver we may continue;/, BBC #15,UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5),80$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBr BRW 130$ ; ContinueR&80$: MOVZWL #SS$_DEVALLOC,R0 ; In use BRW 100$ ; Quit=;) ; Completion of ENQ ends up here;R>90$: MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Check completion status$ BLBC R0,100$ ; Something's wrong;WM; No we've got the devicelock in EX mode. If we did not specify shared access L; we're done. The devicelock remains set to protect use againts other users.;I. BBC #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),120$C; R; Set bit 15 (currently not used) in the FLAGS field of the devicelock's lockvalueO; block to signal that we own the device. The fields are defined in the $DALDEFRU; macro. Furthermore, convert the lock to PW and back to EX to update the valueblock.;V* MOVL #^X8000,- ; Flag we own the device! UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5)$; CLRL UCB$A_LD_PD_LVB+4(R5) ; Zero rest of lockvalue block) CLRL UCB$A_LD_PD_LVB+8(R5)3 CLRL UCB$A_LD_PD_LVB+12(R5) ENQ_LOCK MODE=#LCK$K_PWMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-n EFLAGS= BLBC R0,100$ MOVZWL UCB$Q_LD_PD_LKSB(R5),R0o BLBC R0,100$ ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-n EFLAGS= BLBC R0,100$  MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 BLBS R0,120$w;G; Some error occurred. Make sure that we get rid of the $LOGDISK locks.t;a100$: PUSHL R0 ; Save status3) DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5)k ; Get rid of file lock" PUSHL R0 ; Save eventual error( DEQ_LOCK LOCKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of device lock) MOVL (SP)+,R1 ; Restore dequeue statusb+ BLBC R1,110$ ; Error on first dequeue ?l2 MOVL R0,R1 ; Return second dequeue error in R1&110$: MOVL (SP)+,R0 ; Restore status BRW 190$ ; Return errore:120$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB, MOVL UCB$Q_LD_PD_LKSB+4(R5),- ; Save lockid UCB$L_LOCKID(R1)'130$: MOVL UCB$L_ORB(R5),- ; Save ORB_ UCB$L_LD_ORBSAV(R5)S= MOVL UCB$L_ORB(R1),- ; Get protection from physical deviceh UCB$L_ORB(R5)o- MOVB UCB$B_DEVTYPE(R1),- ; Copy device typeR UCB$B_DEVTYPE(R5)A&ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1(ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+1? MOVL UCB$B_SECTORS(R1),- ; Copy sectors, tracks and cylinders  UCB$B_SECTORS(R5)a0 MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5)* INCL UCB$L_REFC(R1) ; Mark our interest3 BICL2 #DEV$M_AVL,- ; Make unavailable for others_ UCB$L_DEVCHAR(R1)P1 BISW2 #UCB$M_LD_REPLACE,- ; Set special connecti UCB$W_LD_FLAGS(R5)) BISL2 #,-c0 UCB$L_DEVCHAR(R5) ; Set characteristics again% ; Needed after replace of CDrom:;P ; Common exitu;M:140$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB0 EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0; INSV R0,#DEV$V_SWL,#1,- ; Honour writelock from phys diska UCB$L_DEVCHAR(R5)o) BLBC R0,145$ ; Is the unit protected?B1 BISW2 #UCB$M_LD_PROTECT,- ; Protect ourself tooD UCB$W_LD_FLAGS(R5)8145$: MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecount UCB$L_MAXBCNT(R5)25 CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected ton" #DT$_RAM_DISK ; a DECRAM disk BNEQ 150$ ; No/ BISW2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in usef UCB$W_LD_FLAGS(R5).150$: MOVL UCB$L_DDT(R1),R1 ; Get DDT address, MOVL DDT$PS_FDT_2(R1),R1 ; Get FDT address7 BBS #IO$_DSE,(R1),160$ ; Does the device support DSE?n7 BISW2 #UCB$M_LD_NODSE,- ; Not supported (Raid driver)L" UCB$W_LD_FLAGS(R5) ; (DPdriver)C160$: BICL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB non-deletable + BBC #LDIO_V_SHARE,- ; Shared accessable?1 IRP$L_EXTEND(R3),170$(@ BISL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Make cluster wide visible& BISW2 #UCB$M_LD_SHARE,- ; Set status UCB$W_LD_FLAGS(R5)9170$: ADAWI #1,G^LD_REFCNT ; Count number of 'connects'a4 BISW2 #UCB$M_LD_CONSTS,- ; Set status to connected UCB$W_LD_FLAGS(R5)0 MOVZWL #SS$_NORMAL,R0 ; Set status to success 180$: CLRL R1a,190$: BSBW LD_SAVE_TRACE ; Save trace data KP_REQCOM ; Complete I/O.t .PAGE9 .SBTTL LD_START_DISCONNECT, LD Start disconnnect routine;+++4; LD_START_DISCONNECT, LD Start disconnnect routine;L; Functional description:T;RE; This is the completion of the disconnect processing. This has to be =; in system context since we may need to fork when we use the-); forklevel interface to the lockmanager.M; ; )g$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1US"Inputs:_;R9; R3 - address of the IRP (I/O request packet)F9; R5 - address of the UCB (unit control block) ;i ; Outputs:;LF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredM; R1 - 0R;;D; The routine must preserve all registers except R0-R2 and R4.; ;---% UNIVERSAL_SYMBOL LD_START_DISCONNECT ;LD_START_DISCONNECT: ) .CALL_ENTRY INPUT=,OUTPUT=a1 CMPL UCB$L_LD_AIOFL(R5),- ; Any I/O's pending ?S @UCB$L_LD_AIOFL(R5)g BEQL 20$ ; Now3 BBSSI #UCB$V_LD_DISPEN,- ; Set disconnect pendingL UCB$W_LD_FLAGS(R5),10$& MOVL R3,UCB$Q_FR3(R5) ; Save context MOVL R4,UCB$Q_FR4(R5)1 MOVAB LD_START_DISCONNECT,- ; Address to resume ) UCB$L_FPC(R5) ; (done by LD_COMPLETE))10$: RET ; Back to callern820$: BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),30$- MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB addressn3 DECL FCB$L_REFCNT(R2) ; Decr. the reference countc/ DECL FCB$L_ACNT(R2) ; Decr. the access countN8 MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB& MOVL UCB$L_VCB(R0),R0 ; Get it's VCB= ADAWI #-1,VCB$L_TRANS(R0) ; Decrement the transaction countQ BRW 50$;c; Replaced drive processinge;u830$: TSTL UCB$Q_LD_PD_LKSB+4(R5) ; Did we own the lock? BEQL 40$ ; No:? DEQ_LOCK LOCKID=UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelock2 BLBS R0,40$ ; Successe4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),40$ BRW 110$ ; Unexpected errorn<40$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB! CLRL UCB$L_LOCKID(R0) ; Zero ID85 DECL UCB$L_REFC(R0) ; We're not interested anymore): BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again* MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5)i;g ; Common pathb;R-50$: DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5)e ; Get rid of lockD* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status( DEQ_LOCK LOCKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of lockL BLBC R0,60$ ; Errors2 MOVL UCB$L_LD_SAVST(R5),R0 ; Get previous status BLBS R0,90$ ; Success);60$: BBC #LDIO_V_ABORT,- ; Continue if abort is specifiedc IRP$L_EXTEND(R3),110$u>90$: BISL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB deletable4 ADAWI #-1,G^LD_REFCNT ; Count down #of 'connects'0 MOVZWL #SS$_NORMAL,R0 ; Set status to success 110$: CLRL R1 ' BSBW LD_SAVE_TRACE ; Save trace data0 KP_REQCOM ; Complete I/O.R .PAGE/ .SBTTL LD_START_TRACE, Start I/O trace routine(;+++); LD_START_TRACE, Start I/O trace routineB; ; Functional description:P;0M; This is the completion of the retrieve tracedata processing. This has to beoG; in system context since we need to fork when we find that there is nog; tracedata available.;e ; Inputs:i;h9; R3 - address of the IRP (I/O request packet)t9; R5 - address of the UCB (unit control block)c;f ; Outputs:;A ; None.V;2D; The routine must preserve all registers except R0-R2 and R4.;--- UNIVERSAL_SYMBOL LD_START_TRACE;LD_START_TRACE:> INSQUE (R3),@UCB$L_LD_TRCWAITQBL(R5) ; Save wait IRP in queue;oH; Return to caller. Since we don't want the I/O to complete now (there'sD; no trace data when we get here) we return to our caller instead ofE; going through REQCOM. When LD_TRACE writes new data into the bufferkE; it will queue an AST to the process, and the process will resume at D; LD_TRACE_AST which will copy the new data to the user and complete ; the I/O.;B RET ; Return to callera .PAGE9 .SBTTL LD_TRACE_AST, Display data AST completion routineE;+++4; LD_TRACE_AST, Display data AST completion routine;E; Functional description:;o9; This is the final completion of the display processing.p@; This routine is called as an AST routine in the context of theB; process issueing the I/O. We will move the newly available trace2; data to the user's buffer, and complete the I/O.;g ; Inputs:n;s1; R4 - address of the PCB (process control block) $; R5 - address of the ACB;n=; The routine must preserve all registers except R0-R5.c;--- UNIVERSAL_SYMBOL LD_TRACE_AST;LD_TRACE_AST: .CALL_ENTRY INPUT=( MOVL ACB$L_ASTPRM(R5),R3 ; Restore IRP MOVL R5,R0 ; Address of ACB ( JSB G^EXE$DEANONPAGED ; Get rid of ACB% MOVL IRP$L_UCB(R3),R5 ; Restore UCB$) BSBW LD_MOVE_TRACE ; Move data to userB- BLBC R0,10$ ; Missed count in R1 on errorC= DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to #of entries$,10$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level0 SAVIPL=-(SP)n% MOVL R3,UCB$L_IRP(R5) ; Restore IRP;* CALL_REQCOM ; Complete the I/O request0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELo PRESERVE=NO RET .PAGE@ .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing;+++;; LD_START_ENABLE_WATCH, Start I/O enable watch processinge;i; Functional description:c;m=; This is the completion of the enable watchpoint processing.8B; Buffers will be allocated, and if any buffer which was specified?; by the user already exists we will return the charge for this ; buffer to the user by ast.;R ; Inputs:2;9; R3 - address of the IRP (I/O request packet)$9; R5 - address of the UCB (unit control block)M/; IRP$L_OBCNT - number of entries in userbuffer;N ; Outputs:;B ; None.N;LD; The routine must preserve all registers except R0-R2 and R4.;---' UNIVERSAL_SYMBOL LD_START_ENABLE_WATCH;LD_START_ENABLE_WATCH:o5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addresso) MOVL (R2),R2 ; Get userbuffer address % MOVL IRP$L_OBCNT(R3),R4 ; Get countm810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead4 BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not foundo( CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPEND- BNEQ 20$ ; No-5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints:720$: BSBW LD_COPY_WATCH ; Found exact match, copy new= ; parameters except pidu> MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^M_+ MOVL IRP$L_PID(R3),R4 ; Process to creditA1 BSBW LD_RETURN_QUOTA ; Give quota back to user> POPR #^M BRW 40$S*30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1G, MOVZWL #LDWATCHENT_K_LENGTH,R1 ; This size$ JSB G^EXE$ALONONPAGED ; Get buffer" BLBC R0,50$ ; Out of resources1 MOVB #DYN$C_BUFIO,LDWATCH_B_TYPE(R2) ; Fill type ' CLRW LDWATCH_W_FLAGS(R2) ; Zero flagsE' MOVW R1,LDWATCH_W_SIZE(R2) ; And sizen3 MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queuen LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),-s LDWATCH_L_SUSPBL(R2)4 CLRL LDWATCH_L_SUSPCNT(R2) ; Nothing suspended yet# MOVL R2,R1 ; New buffer address $ MOVL 4(SP),R2 ; Get entry address* BSBW LD_COPY_WATCH_NEW ; Copy parameters& MOVL R1,R2 ; Restore buffer addres# MOVL (SP)+,R1 ; Restore listhead 4 INSQUE (R2),LDWATCH_L_FLINK(R1) ; Insert new packet+ INCL UCB$L_LD_WATCHCNT(R5) ; Count packet  MOVL (SP)+,R2>40$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry# SOBGTR R4,45$ ; For all buffers " MOVZWL #SS$_NORMAL,R0 ; Success BRB 60$45$: BRW 10$ ; Branch assistS-50$: ADDL2 #8,SP ; Adjust for error returnA960$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count_ KP_REQCOM ; Complete I/O.. .PAGEB .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing;+++=; LD_START_DISABLE_WATCH, Start I/O disable watch processing1;2; Functional description:K;;>; This is the completion of the disable watchpoint processing.?; If a watchpoint was found with suspended threads queued to ite&; then these threads will be released.;s ; Inputs:M; 9; R3 - address of the IRP (I/O request packet)Q9; R5 - address of the UCB (unit control block)/; IRP$L_OBCNT - number of entries in userbufferT;) ; Outputs:;i ; None.B;0D; The routine must preserve all registers except R0-R2 and R4.;---( UNIVERSAL_SYMBOL LD_START_DISABLE_WATCH;LD_START_DISABLE_WATCH:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R4 ; Get packet counte BNEQ 10$ ; Something to do BRW 100$ '10$: PUSHL R6 ; May not be destroyedI! CLRL R6 ; Nothing removed yetC5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addressC$ BEQL 20$ ; Not there, remove all) MOVL (R2),R2 ; Get userbuffer address / MOVL IRP$*ݒ$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U "!L_OBCNT(R3),R4 ; Get requested countu820$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead& TSTL R2 ; Get rid of alll entries? BEQL 30$ ; Yes4 BSBW LD_FIND_WATCH_ENTRY_DIS ; Find matching entry BLBS R0,70$ ; No exact match BRB 40$ ; Got it, remove*30$: MOVL (R1),R1 ; Point to next entry- MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; End of listc CMPL R0,R1 ; At the end? BEQL 60$ ; Yes940$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, removeo+ DECL UCB$L_LD_WATCHCNT(R5) ; Count packetV* INCL R6 ; Flag we've removed something" MOVL R0,R1 ; Use correct input5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints- PUSHR #^M ; Destroyed by deallocate;5 BBC #LDWATCH_V_FILE,- ; Check for virtual file modeu LDWATCH_W_FLAGS(R0),50$D- MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointera/ DECL FCB$L_REFCNT(R2) ; Decr. reference count+ DECL FCB$L_ACNT(R2) ; Decr. access countn& MOVL UCB$L_VCB(R5),R2 ; Get it's VCB8 ADAWI #-1,VCB$L_TRANS(R2) ; Bump the transaction count350$: MOVL LDWATCH_L_PID(R0),R4 ; Process to creditn/ JSB G^EXE$DEANONPAGED ; Return buffer to pool 1 MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to return,1 BSBW LD_RETURN_QUOTA ; Give quota back to user  POPR #^M BRB 20$"60$: TSTL R2 ; Next input entry BEQL 80$ ; All entries>70$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry)80$: SOBGTR R4,20$ ; Check all packetss) MOVZWL #SS$_NORMAL,R0 ; Assume successi TSTL R6 ; Anything removed?i BNEQ 90$ ; Yes, MOVZWL #SS$_DATACHECK,R0 ; Entry not found(90$: MOVL (SP)+,R6 ; Restore this baby:100$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count KP_REQCOM ; Complete I/O.  .PAGE? .SBTTL LD_COPY_WATCH, Copy watchpoint info into existing block > .SBTTL LD_COPY_WATCH_NEW, Copy watchpoint info into new block;+++:; LD_COPY_WATCH, Copy watchpoint info into existing block9; LD_COPY_WATCH_NEW, Copy watchpoint info into new block$;D; Functional description:O;#>; This routine copies the data from the user's parameter block; into a watchpoint blockE;G ; Inputs:L;!<; R1 - address of the driver allocated watchblock4; R2 - address of the users watchpt block9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)n;u ; Outputs:;a ; None. ;o;---# UNIVERSAL_SYMBOL LD_COPY_WATCH_NEWn;LD_COPY_WATCH_NEW:o .JSB_ENTRY INPUT= 0 MOVL IRP$L_PID(R3),LDWATCH_L_PID(R1) ; Move pid/ MOVL IRP$L_UCB(R3),LDWATCH_L_UCB(R1) ; And UCB,7 BBS #LDWATCHPT_V_FILE,- ; Check for virtual file modeM LDWATCHPT_W_FLAGS(R2),10$E7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber LDWATCH_L_LBN(R1)r BRW LD_COPY_WATCH!10$: PUSHR #^Mu/ MOVL LDWATCHPT_L_SBK(R2),R4 ; Get SBK address( .DISABLE FLAGGING) MOVL SBK$L_FCB(R4),R4 ; Get FCB addressB .ENABLE FLAGGINGd MOVL R4,LDWATCH_L_FCB(R1)/ INCL FCB$L_REFCNT(R4) ; Incr. reference count+ INCL FCB$L_ACNT(R4) ; Incr. access count8& MOVL UCB$L_VCB(R5),R8 ; Get it's VCB7 ADAWI #1,VCB$L_TRANS(R8) ; Bump the transaction countW& MOVL FCB$L_WLFL(R4),R7 ; Get any WCB'ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQu% MOVL FCB$W_FID_NUM(R4),- ; Save FIDB LDWATCH_W_FID_NUM(R1)o MOVW FCB$W_FID_RVN(R4),-; LDWATCH_W_FID_RVN(R1) 7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumberc0 LDWATCH_L_VBN(R1) ; to virtual block for file7 MOVL LDWATCHPT_L_LBN(R2),R6 ; Get virtual blocknumber#920$: MOVL WCB$L_NMAP(R7),R3 ; Number of mapping pointers1*ASSUME WCB$L_P2_COUNT EQ WCB$L_P1_COUNT+12@ MOVAL WCB$L_P1_COUNT(R7),R5 ; Address of first mapping pointer30$: MOVL (R5)+,R4 ; Count% CMPL R6,R4 ; Within this segment?r BLEQ 40$ ; Yes2 SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbn, TSTL (R5)+ ; Skip RVNe SOBGTR R3,30$ ; Next segment# MOVL WCB$L_LINK(R7),R7 ; Next WCBr BNEQ 20$ ; Try this oneC; C; We should never come here. The VBN was checked in the FDT routine#&; to be within the limits of the file.;5 BUG_CHECK INCONSTATE,FATAL!$40$: DECL R6 ; Adjust for VBN = 1C ADDL3 R6,(R5)+,LDWATCH_L_LBN(R1) ; Save converted LBN, skip to RVN$. TSTL VCB$L_RVN(R8) ; Relative volume number) BEQL 50$ ; Branch if not a volume set> MOVZBL (R5),R0 ; Get RVN+ MOVL VCB$L_RVT(R8),R6 ; Fetch RVT addresso: MOVL RVT$L_UCBLST-4(R6)[R0],- ; Get the right ucb address LDWATCH_L_UCB(R1)U3 CMPL 8(SP),LDWATCH_L_UCB(R1) ; Is it our own UCB?I BEQL 50$ ; Yes, no problem9 BISW2 #LDWATCH_M_ONVOLSET,- ; Flags this extent is on a " LDWATCH_W_FLAGS(R1) ; volumeset 50$: POPR #^M BSBB LD_COPY_WATCHq RSB;u; Fall into LD_COPY_WATCHr;  UNIVERSAL_SYMBOL LD_COPY_WATCH;;LD_COPY_WATCH:q .JSB_ENTRY INPUT=O2 BICW3 #^C,- ; Mask unused bits LDWATCHPT_W_FLAGS(R2),R0( BISW2 R0,LDWATCH_W_FLAGS(R1) ; Options+ MOVW LDWATCHPT_W_ACTION(R2),- ; What to dod LDWATCH_W_ACTION(R1)0 MOVW LDWATCHPT_W_FUNC(R2),- ; On what function LDWATCH_W_FUNC(R1)0 MOVW LDWATCHPT_W_RETCODE(R2),- ; What to return LDWATCH_W_RETCODE(R1) RSB .PAGEE .SBTTL LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enableA;+++@; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable;$; Functional description:t;,6; This routine locates a watchpoint entry. Entries are6; inserted in ascending order, so when we are past one9; entry we don't need to look any further. A special caseE9; is signalled if the NOLBN flag is set. This is the case(8; for a non-transfer function. We can have more of these; for different functions.;D ; Inputs:V;O:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;m ; Outputs:;4 ; R0 - 0 if found an exact match; 1 if not foundB; R1 - if R0 = 0 entry address#; if R0 = 1 address if insertion1;0;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_ENAo;LD_FIND_WATCH_ENTRY_ENA:A( .JSB_ENTRY INPUT=,OUTPUT= MOVL R1,R0 ; Save for laterT!10$: MOVL (R1),R1 ; Next entryD CMPL R1,R0 ; End of queue ?X BEQL 60$ ; Yes1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?_ LDWATCH_W_FLAGS(R1),40$e, CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1)o BNEQ 10$ ; NoD* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$L; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockG LDWATCH_L_VBN(R1)# BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)u30$: BEQL 40$ ; Yes' BGTRU 10$ ; Not a higher number yet  BRB 50$ ; No match640$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; NoD1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?M LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes;E'; Here everything matches except action"; Determine if action may be added;m5 CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action?a #WATCH_ACTION_OPCOM$ BEQL 50$ ; Yes; @; Add some action other than OPCOM. Make sure we don't overwrite; a current OPCOM action.v;r< CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOMr2 BNEQ 80$ ; No, modify action of current packet;D,; We must check the next packet for a match.;r BRW 10$;A; Make sure that a packet with an action for OPCOM is in front ofbF; other actions for the same lbn and function in the queue. This makesD; sure that later on in LD_CHECK_WATCH we will always issue an opcomE; message before we do any other specified action. To insert an entryrC; before the current location we have to provide the address of the ; predecessor.;o350$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up address  BRB 70$ ; of previous entry; A; Nothing found in the queue. Insert new entry after the last oner;x360$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up addressA ; of last entryR 70$: MOVZBL #1,R0 ; Not found BRB 90$80$: CLRL R0 ; Exact matchO90$: RSB .PAGEF .SBTTL LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable;+++A; LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable3;B; Functional description:;V6; This routine locates a watchpoint entry. Entries are6; inserted in ascending order, so when we are past one9; entry we don't need to look any further. A special caseR9; is signalled if the NOLBN flag is set. This is the case38; for a non-transfer function. We can have more of these; for different function+u$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Uj"2s.;e ; Inputs:n;:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;$ ; Outputs:; ; R0 - 0 if found an exact match; 1 if not foundh; R1 - if R0 = 0 entry address!; if R0 = 1 last entry addressu;0;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_DISl;LD_FIND_WATCH_ENTRY_DIS:D( .JSB_ENTRY INPUT=,OUTPUT= PUSHL R3v MOVL R1,R0 ; Save for laters!10$: MOVL (R1),R1 ; Next entry  CMPL R1,R0 ; End of queue ?D BEQL 40$ ; Yes; EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bitsG #LDWATCHPT_S_CHARS,- LDWATCHPT_W_FLAGS(R2),R39 CMPZV #LDWATCH_V_CHARS,- ; Check against current packetm #LDWATCH_S_CHARS,- LDWATCH_W_FLAGS(R1),R3 BNEQ 10$ ; No matchC1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?i LDWATCH_W_FLAGS(R1),60$0* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockU LDWATCH_L_VBN(R1)) BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)G30$: BEQL 50$ ; Yes' BGTRU 10$ ; Not a higher number yetA 40$: MOVZBL #1,R0 ; Not found BRB 80$050$: BBS #LDWATCHPT_V_REMOVE_ALL,- ; Remove all? LDWATCHPT_W_FLAGS(R2),70$l660$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; No 1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?  LDWATCH_W_ACTION(R1) BNEQ 10$ ; Noe70$: CLRL R0 ; Exact matchT'80$: MOVL (SP)+,R3 ; Restore registerS RSB .PAGE? .SBTTL LD_START_GET_WATCH, Start I/O get watch info processings;+++:; LD_START_GET_WATCH, Start I/O get watch info processing;b; Functional description:c;e:; This is the completion of the get watchpoint processing.;a ; Inputs: ;u9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block)l;o ; Outputs:;p ; None. ;oD; The routine must preserve all registers except R0-R2 and R4.;d$ UNIVERSAL_SYMBOL LD_START_GET_WATCH;LD_START_GET_WATCH:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet countd BNEQ 10$ ; Something to do BRW 50$/10$: BBS #LDIO_V_INQUIRE,- ; Return list size?r IRP$L_EXTEND(R3),40$3 MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smallo2 MULL3 R1,#LDWATCHPT_K_LENGTH,R2 ; Get size needed( CMPL IRP$L_BCNT(R3),R2 ; Enough space? BLSSU 60$ ; No5 MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer addressB) MOVL (R4),R4 ; Get userbuffer addressB9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead+ MOVL R0,R2 ; Remember startS%20$: MOVL (R0),R0 ; Get next entrys CMPL R0,R2 ; At the end? BEQL 40$ ; Yes) CLRL LDWATCHPT_L_SBK(R4) ; Not returned 5 MOVL LDWATCH_L_LBN(R0),- ; Move logical blocknumbers LDWATCHPT_L_LBN(R4) l- ASSUME LDWATCH_W_ACTION EQ LDWATCH_W_FLAGS+2 / MOVL LDWATCH_W_FLAGS(R0),- ; Flags and actions LDWATCHPT_W_FLAGS(R4) - ASSUME LDWATCH_W_RETCODE EQ LDWATCH_W_FUNC+2 5 MOVL LDWATCH_W_FUNC(R0),- ; Function and returncodee LDWATCHPT_W_FUNC(R4)5 BBC #LDWATCH_V_FILE,- ; Check for virtual file modeR LDWATCH_W_FLAGS(R0),30$T0 ASSUME LDWATCH_W_FID_NUM+2 EQ LDWATCH_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R0),- ; Save FIDE LDWATCHPT_W_FID_NUM(R4)f MOVW LDWATCH_W_FID_RVN(R0),-  LDWATCHPT_W_FID_RVN(R4)_5 MOVL LDWATCH_L_VBN(R0),- ; Move virtual blocknumberc LDWATCHPT_L_LBN(R4) V/30$: ADDL2 #LDWATCHPT_K_LENGTH,R4 ; Next entryn BRB 20$640$: MOVZWL #SS$_NORMAL,R0 ; Return with count in R1450$: BBS #LDIO_V_INQUIRE,- ; Return list size only? IRP$L_EXTEND(R3),60$. MULL3 #LDWATCHPT_K_LENGTH,R1,R2 ; Total count2 MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing' INSV R2,#16,#16,R0 ; Merge bytecounti!60$: KP_REQCOM ; Complete I/O.R .PAGEH .SBTTL LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processing;+++C; LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processingi;l; Functional description:i;h<; This is the completion of the get suspend list processing.;l ; Inputs:e;a9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block) ;r ; Outputs:; ; None. ; D; The routine must preserve all registers except R0-R2 and R4.;d+ UNIVERSAL_SYMBOL LD_START_GET_SUSPEND_LIST$;LD_START_GET_SUSPEND_LIST:n0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet count  BNEQ 10$ ; Something to do BRW 100$ ; Branch assist 10$: CLRL R1 ; No entries yet9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead  MOVL R0,R2 ; Remember starts%20$: MOVL (R0),R0 ; Get next entrym CMPL R0,R2 ; At the end? BEQL 30$ ; Yes9 ADDL2 LDWATCH_L_SUSPCNT(R0),R1 ; Count number of entriese BRB 20$/30$: BBS #LDIO_V_INQUIRE,- ; Return list size?) IRP$L_EXTEND(R3),90$3 MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smallW2 MULL3 R1,#LDSUSPLST_K_LENGTH,R2 ; Get size needed( CMPL IRP$L_BCNT(R3),R2 ; Enough space? BLSSU 110$ ; Noe5 MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer address ) MOVL (R4),R4 ; Get userbuffer address9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listheadR MOVL R0,R2 ; Remember starte%40$: MOVL (R0),R0 ; Get next entryP CMPL R0,R2 ; At the end? BEQL 90$ ; Yes4 TSTL LDWATCH_L_SUSPCNT(R0) ; Get count of this lbn BEQL 40$ ; None here PUSHR #^M 8 MOVAL LDWATCH_L_SUSPFL(R0),R1 ; Point to suspend queue MOVL R1,R2 ; Remember endL.50$: MOVL IRP$L_IOQFL(R1),R1 ; Get next entry CMPL R1,R2 ; At the end? BEQL 80$ ; Yes PUSHR #^M= BBC #IRP$V_SHDIO,IRP$L_STS2(R1),55$ ; Is this shadowing I/O?C. MOVL IRP$L_MIRP(R1),R1 ; Yes, get master irp%55$: MOVL IRP$L_PID(R1),R0 ; Get PIDw0 JSB G^EXE$CVT_IPID_TO_EPID ; Make external PID( MOVL R0,LDSUSPLST_L_PID(R4) ; Move PID POPR #^M5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modee LDWATCH_W_FLAGS(R0),60$W% MOVL LDWATCH_L_LBN(R0),- ; Move LBNI LDSUSPLST_L_LBN(R4)5 BRB 70$)60$: MOVL LDWATCH_L_VBN(R0),- ; Move VBNC LDSUSPLST_L_LBN(R4)t1 ASSUME LDSUSPLST_W_ACTION EQ LDSUSPLST_W_FLAGS+2f870$: MOVL LDWATCH_W_FLAGS(R0),- ; Move flags and action LDSUSPLST_W_FLAGS(R4)s1 ASSUME LDSUSPLST_W_RETCODE EQ LDSUSPLST_W_FUNC+2A7 MOVL LDWATCH_W_FUNC(R0),- ; Move function and retcodeo LDSUSPLST_W_FUNC(R4)+ ADDL2 #LDSUSPLST_K_LENGTH,R4 ; Next entryE BRB 50$80$: POPR #^M BRB 40$90$: MOVZWL #SS$_NORMAL,R05100$: BBS #LDIO_V_INQUIRE,- ; Return list size only?d IRP$L_EXTEND(R3),110$i. MULL3 #LDSUSPLST_K_LENGTH,R1,R2 ; Total count2 MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing' INSV R2,#16,#16,R0 ; Merge bytecounts"110$: KP_REQCOM ; Complete I/O. .PAGE@ .SBTTL LD_START_RESUME_WATCH, Start I/O resume watch processing;+++;; LD_START_RESUME_WATCH, Start I/O resume watch processingb;o; Functional description:); =; This is the completion of the resume watchpoint processing.eG; Any previously suspended thread which is specified will be restarted.D;B ; Inputs:L;T9; R3 - address of the IRP (I/O request packet)i9; R5 - address of the UCB (unit control block)e/; IRP$L_OBCNT - number of entries in userbuffer0;P ; Outputs:;y ; None.e;D; The routine must preserve all registers except R0-R2 and R4.;e' UNIVERSAL_SYMBOL LD_START_RESUME_WATCHe;LD_START_RESUME_WATCH:R0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 TSTL UCB$L_LD_WATCHCNT(R5) ; Check packet countL BEQL 70$ ; Nothing to do# PUSHL R6 ; May not be destroyedl! CLRL R6 ; Nothing resumed yetB5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address  BEQL 10$ ; Nothing there) MOVL (R2),R2 ; Get userbuffer address)010$: MOVL IRP$L_OBCNT(R3),R4 ; Get wanted count3 MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Point to listhead?& MOVL R1,R0 ; Save start for later.20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue? BEQL 40$ ; Yes TSTL R2 ; All of them? BEQL 30$ ; Yes- CMPW LDWATCH_W_FUNC(R1),- ; Right function?  LDWATCHPT_W_FUNC(R2) BNEQ 20$ ; No45 BBS #LDWATCH_V_FILE,- ; Check for virtual file mode  LDWATCH_W_FLAGS(R1),25$d' CMPL LDWATCH_L_LBN(R1),- ; Right lbn?A LDWATCHPT_L_LBN(R2)  BNEQ 20$ ; No  BRB 30$+25$: CMPL LDWATCH_L_VBN(R1),- ; Right vbn?C LDWATCHPT_L_LBN(R2)  BNEQ 20$ ; No_130$: CMPW LDWATCH_W_ACTION(R1),- ; Right action?) #WATCH_ACTIO,@$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U"CN_SUSPENDc BNEQ 20$ ; NoD5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpointsA" INCL R6 ; We resumed something BRB 20$ ; Next packetR"40$: TSTL R2 ; Everything done? BEQL 50$ ; Yes4 ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Next entry of input SOBGTR R4,20$ ; Count down-50$: MOVZBL #SS$_NORMAL,R0 ; Assume success_ TSTL R6 ; Done something?p BNEQ 60$ ; Yes) MOVZWL #SS$_DATACHECK,R0 ; Nothing done '60$: MOVL (SP)+,R6 ; Recover this one6 70$: CLRL R1 KP_REQCOM ; Complete I/O.M .PAGE: .SBTTL LD_DRAIN_WATCH_THREAD, drain suspended watch queue;+++5; LD_DRAIN_WATCH_THREAD, drain suspended watch queuee;n; Functional description: ; 6; This routine resumes all pending watchpoint threads.;i ; Inputs:o;i*; R1 - address of LDWATCH block9; R5 - address of the UCB (unit control block)o;w ; Outputs:;i; None.i;b;---' UNIVERSAL_SYMBOL LD_DRAIN_WATCH_THREADo;LD_DRAIN_WATCH_THREAD:u .JSB_ENTRY INPUT== TSTL LDWATCH_L_SUSPCNT(R1) ; Get count of suspended threads BEQL 20$ ; Nothing, PUSHR #^M ; Save registers MOVL R1,R4i310$: REMQUE @LDWATCH_L_SUSPFL(R4),R3 ; Remove entryt. KP_RESTART KPB=IRP$PS_KPB(R3) ; Resume thread+ DECL LDWATCH_L_SUSPCNT(R4) ; Count packet5 BNEQ 10$ ; More to do( POPR #^M 20$: RSB .PAGE. .SBTTL LD_CHECK_WATCH, Check for a watchpoint;+++); LD_CHECK_WATCH, Check for a watchpoint=;,; Functional description:(;,D; This routine will check if a watchpoint is set for the current I/OB; request. If true, the action we will do depends on the specified; function for the watchpoint.; ; We currently have 4 options:;a-; 1. WATCH_ACTION_SUSPEND: Suspend a threadO+; 2. WATCH_ACTION_CRASH: Crash the systemu*; 3. WATCH_ACTION_ERROR: Return an error2; 4. WATCH_ACTION_OPCOM: Send a message to OPCOM;eH; #1 will suspend the current thread. We do this by popping our caller'sK; PC into a forkblock and returning to our caller's caller. Later on we can E; resume this thread by queueing the forkblock to the forkdispatcher.8;;<; #2 will crash the system if we find a matching watchpoint.;W?; #3 will return a user-specified error for the current thread.(;,I; #4 will send an errormessage about the process accessing the watchpointA ; to OPCOM1; ; Inputs:C;_9; R3 - address of the IRP (I/O request packet)19; R5 - address of the UCB (unit control block)6; ; Outputs:;c ; R0 - status:7; IRP$L_EXTEND - if 0 no action done, continue as usualS+; - if 1, R0 contains the error to returnV;WA; If a thread is suspended we return to our caller's callerV1; without R0 set as that's not needed then.s;e>; The routine must preserve all registers except R0-R2..;s;--- UNIVERSAL_SYMBOL LD_CHECK_WATCH;LD_CHECK_WATCH:% .JSB_ENTRY INPUT=,OUTPUT=t( CLRL IRP$L_EXTEND(R3) ; Clear our flag2 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint set? BNEQ 10$ ; Yes;eM; For the future: check if FILE watchpoint is on other member of a volumeset.K(; We have to check all devices for that.;t5; BBC #UCB$M_LD_ONVOLSET,- ; Check if container filec*; UCB$W_LD_FLAGS(R5),5$ ; on a volumeset5$: BRW 120$ ; Branch assistB810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead! MOVL R1,R0 ; Remember the end .20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue ?  BEQL 5$ ; Yes7 CMPW LDWATCH_W_FUNC(R1),#^XFFFF ; Allow all functions?Q! BEQL 30$ ; Yes, check LBN too#/ CMPW IRP$L_FUNC(R3),- ; Match of functioncode , LDWATCH_W_FUNC(R1) ; including modifiers? BNEQ 20$ ; No,> EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract the function code IRP$L_FUNC(R3),R2M) CMPL R2,#IO$_READPBLK ; Is this a read?_ BEQL 30$ ; Yes, check lbnC& CMPL R2,#IO$_WRITEPBLK ; or a write? BEQL 30$ ; Yes, check lbnB' CMPL R2,#IO$_WRITECHECK ; Writecheck?M BEQL 30$ ; Yes, check lbnW CMPL R2,#IO$_DSE ; Erase? BNEQ 50$ ; NoP730$: CMPL IRP$L_UCB(R3),- ; Matching UCB (volumeset?)M LDWATCH_L_UCB(R1)D BNEQ 20$ ; Noe* CMPL IRP$L_MEDIA(R3),- ; Matching block? LDWATCH_L_LBN(R1)C- BGTRU 20$ ; No, requested block above wptl6 ASHL #-9,IRP$L_BCNT(R3),R2 ; Convert bytes to blocks3 BITL #511,IRP$L_BCNT(R3) ; Multiple of 512 bytes?, BNEQ 40$ ; Noc DECL R2 ; Adjust540$: ADDL2 IRP$L_MEDIA(R3),R2 ; Calculate last block - CMPL R2,LDWATCH_L_LBN(R1) ; Matching block?  BLSSU 20$ ; No;EJ; We've got a match. Now check what to do with it. ;fM50$: DISPATCH LDWATCH_W_ACTION(R1),TYPE=W,<- ; Dispatch according to functions ,-s ,-e ,-t >7 BUG_CHECK INCONSTATE,FATAL ; We should never come here 860$: MOVZWL LDWATCH_W_RETCODE(R1),R0 ; Pick up errorcode BRB 110$n;t4; Crash the system. R1 = LDWATCH, R3 = IRP, R5 = UCB;oD70$: BUG_CHECK RSVD_LP,FATAL ; Byebye, that's what's been asked for@80$: INSQUE (R3),@LDWATCH_L_SUSPBL(R1) ; Insert in suspend queueI INCL LDWATCH_L_SUSPCNT(R1) ; Count packet and return to caller's caller_7 MOVL IRP$PS_KPB(R3),R0 ; Get KPB (must be a register),/ KP_STALL_GENERAL KPB=R0,- ; Wait for callback? STALL_ROUTINE=G^IOC$RETURN BRB 120$ ; ContinueA$90$: PUSHL R0 ; Save end of chain. BSBW LD_SEND_OPCOM ; Queue message to OPCOM BLBC R0,100$ ; Trouble MOVL (SP)+,R0 BRW 20$ ; Check next packetP!100$: ADDL2 #4,SP ; Restore SPl1110$: INCL IRP$L_EXTEND(R3) ; Flag action needed 120$: RSB  .PAGEB .SBTTL LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device;+++<; LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device;N; Functional description:N;)I; This routine will enqueue the file lock of the specified file andeD; device. It will check if the file is used on another node anywhereE; in the cluster. If true then it's only allowed if we ordered sharedHB; access and if the remote devicename and geometry matches ours so; that XQP locking will work.w;e%; The locking protocol is as follows: ;AI; First we will enqueue an EX-mode lock on our resource. If it is grantedF; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block andcF; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;eG; If we don't get it immediately then it means that another thread owns B; the lock. In that case we implicitely triggered the blocking astH; routine by enqueueing the EX lock. This blocking ast routine will thenE; convert the lock back to NL, and it will immediately try to convert E; it back to CR. After we get the lock, we will check the value block H; and then either dequeue it or convert it back to CR. After that action<; the blocking ast routine will convert the lock back to CR.;MI; If we encounter an invalid lock value block during this process we will0I; just rewrite it with the same contents. It's not documented, but we canfH; rely on the value being equal to what it was before it became invalid.A; (courtesy of Sandy Snaman, it may be documented in the future).t;a;t ; Inputs:r;E9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)l*; UCB$T_LD_FILE_RESNAM(R5) - Resource name;.; IOLOCK8 (=SCS) forklock heldr;f ; Outputs:; ; R0 - statusf; SS$_NORMAL - Successu5; SS$_FILALRACC - File in use in an incompatible way 6; SS$_DEVALLOC - Device in use in an incompatible way#; other - Returned by lockmanager &; R1 - Falure reason for SS$_FILALRACC;t*; Only R3-R5 are preserved as we may fork.;y;--- UNIVERSAL_SYMBOL LD_ENQ_LD_LOCK;LD_ENQ_LD_LOCK:% .JSB_ENTRY INPUT=,OUTPUT=l ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-R% RESNAME=UCB$T_LD_FILE_RESNAM(R5),-o EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BEQL 10$ ; Yes BLBS R0,50$ ; Ok BRW 90$ ; Errorb;GC; We are the first one queueing a lock for this resource. Setup the_F; lock value block, convert the lock back to CR and exit with success.;_G10$: MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value blockt! MOVL UCB$L_DDB(R5),R2 ; Get DDBG3 MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation class? LDFLVB_L_ALLOCLS(R1)% MOVW UCB$W_UNIT(R5),- ; Unit numbere LDFLVB_W_UNIT(R1)l( MOVW UCB$W_CYLINDERS(-P7$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U!"TR5),- ; Cylinders LDFLVB_W_CYLINDERS(R1)" MOVB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1)0$ MOVB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1)1 MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberD LDFLVB_L_MAXBLOCK(R1)R& CLRB LDFLVB_B_FLAGS(R1) ; Init flags' BBC #LDIO_V_SHARE,- ; Shared access?a IRP$L_EXTEND(R3),20$ BISB #LDFLVB_M_SHARE,- ! LDFLVB_B_FLAGS(R1) ; Set flagsR"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-0 BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$;S; Lock is converted to CR.;a9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusO) BRW 90$ ; Exit with completion statusl;s; Lock is granted as EX.; =50$: MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusu; N; Check for an invalid value block. If that's the case then we can rely on theL; fact that the contents of the valueblock are the same as they were before.J; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will; revalidate it.;A1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?T& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrong ;nD; We now have the lock in EX mode. Check the lock value block to see; if we match.; G60$: MOVAB UCB$A_LD_FILE_LVB(R5),R2 ; Setup pointer to lock value block 1 MOVL #LDREASON_NOTSHARED,R1 ; Assume not shareds1 BBC #LDFLVB_V_SHARE,- ; Check for shared access? LDFLVB_B_FLAGS(R2),80$7 MOVL #LDREASON_NOSHARE,R1 ; Assume no share requested 1 BBC #LDIO_V_SHARE,- ; Shared access requested?L IRP$L_EXTEND(R3),80$9 MOVL #LDREASON_ALLOCLASS,R1 ; Assume Alloclass mismatch0! MOVL UCB$L_DDB(R5),R0 ; Get DDBs3 CMPL DDB$L_ALLOCLS(R0),- ; Check allocation classC LDFLVB_L_ALLOCLS(R2) BNEQ 80$ ; No matchD; MOVL #LDREASON_UNITNUMBER,R1 ; Assume Unitnumber mismatch_% CMPW UCB$W_UNIT(R5),- ; Unit number  LDFLVB_W_UNIT(R2)M BNEQ 80$ ; No match 7 MOVL #LDREASON_MAXBLOCK,R1 ; Assume Maxblock mismatchD1 CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumber_ LDFLVB_L_MAXBLOCK(R2)a BNEQ 80$ ; No matchP9 MOVL #LDREASON_CYLINDERS,R1 ; Assume Cylinders mismatchi( CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R2) BNEQ 80$ ; No match 3 MOVL #LDREASON_TRACKS,R1 ; Assume Tracks mismatchI" CMPB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R2)I BNEQ 80$ ; No match_5 MOVL #LDREASON_SECTORS,R1 ; Assume Sectors mismatch_$ CMPB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R2) BNEQ 80$ ; No match ) BRW 20$ ; Convert down to CR and exit;0E; Dequeue the lock since we're not allowed to use the file or device.6F; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.;c380$: MOVL R1,UCB$L_LD_SAVST(R5) ; Save reason code0+ DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5),-B! VALBLOCK=UCB$A_LD_FILE_LVB(R5)t ; Get rid of file lock- MOVL UCB$L_LD_SAVST(R5),R1 ; Restore reason/ BLBC R0,90$ ; Errors/ MOVZWL #SS$_FILALRACC,R0 ; Assume file in use ( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),100$) MOVZWL #SS$_DEVALLOC,R0 ; Device In use/ BRB 100$a90$: CLRL R1 ; No reasons 100$: RSB( .PAGEB .SBTTL LD_ENQ_FILE_BLKRTN, Enqueue file lock blocking ast routine;+++:; LD_ENQ_FILE_BLKRTN, Enqueue LD lock blocking ast routine;L; Functional description:D;SH; This routine will be called by the lockmanager when someone elseE; tries to enqueue an incompatible lock on the LD file resource name. ;;B; We will convert the lock back to NL, and then we immediately tryD; to convert it back to CR. If this second conversion is successfull; we will just exit.;mC; If the second conversion reveals a 'value block invalid' error we B; must revalidate the lock. We do this by converting the lock back=; to NL, up to EX, back to NL and then back to CR. This looksEB; over-complicated but this is needed because more than one threadE; may be active at the same time, and we must make sure that we won'tGA; block other threads which in the conversion process. The second0D; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.L;B ; Inputs:Q;R2; 4(AP) - address of context parameter 1;m; IOLOCK8 (=SCS) forklock held ;G ; Outputs:;*; Only R3-R5 are preserved as we may fork.;s;---$ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN;LD_ENQ_FILE_BLKRTN: .CALL_ENTRY- MOVL FLK_PRM1(AP),R5 ; Recover UCB address0;1D; We have to create our own KPB since the KPB setup by the start I/O+; routine may be in use when we enter here. ;RA10$: KP_ALLOCATE_KPB KPB=UCB$L_LD_FILKPB(R5),- ; Allocate new KPBR FLAGS=#KPB$M_DEALLOC_AT_END BLBC R0,20$ ; ErrorM/ MOVL UCB$L_LD_FILKPB(R5),R0 ; Get KPB address52 MOVL R5,KPB$PS_UCB(R0) ; Save UCB address in KPB# KP_START KPB=UCB$L_LD_FILKPB(R5),-M& ROUTINE=LD_ENQ_FILE_BLKRTN_ACTION,-" REGISTERS=#KPREG$K_HLL_REG_MASK RET;hI20$: FORK_WAIT ENVIRONMENT=CALL,- ; Wait a while after allocation failure( RETURN=RETB BRW 10$;S+ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN_ACTION_;LD_ENQ_FILE_BLKRTN_ACTION:U .CALL_ENTRY& MOVL 4(AP),R0 ; Recover KPB address& MOVL KPB$PS_UCB(R0),R5 ; Recover UCB ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-M EFLAGS=,-S ERROR=60$,- KPBADR=UCB$L_LD_FILKPB(R5)o; MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Check completion statusU BLBS R0,10$ ; Successn BRW 70$;$E; Lock is converted to NL, convert back to CR. Specify QUECVT so that$=; the requesting thread gets it's chance to acquire the lock.E;R"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-L BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=80$,- KPBADR=UCB$L_LD_FILKPB(R5)_;C; Lock is converted to CR.;s4 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit* BRW 90$ ; Bugcheck on all other errors930$: KP_END KPB=UCB$L_LD_FILKPB(R5) ; Finish this thread ;rJ; We have an invalid lock value block. Convert the lock to NL, then to EX,?; then to NL to rewrite the lockvalue block, and finally to CR.f;0"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-e EFLAGS=,-2 ERROR=100$,-I KPBADR=UCB$L_LD_FILKPB(R5)A. MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status BLBC R0,110$ ; Error; .; Lock is converted to NL, convert back to EX.;n ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-e EFLAGS=,- ERROR=120$,-; KPBADR=UCB$L_LD_FILKPB(R5)Q. MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?4 BEQL 50$ ; AcceptableL* BLBC R0,130$ ; Check completion status;I; Lock is converted to EX, convert back to NL to rewrite the value block. ;e"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-  EFLAGS=,- ERROR=140$,-  KPBADR=UCB$L_LD_FILKPB(R5)_1 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Quit on error- BLBC R0,150$i- BRW 10$ ; Ok, convert back to CR and exitC; ;; Bugcheck since there's nobody to return this error to....B; 60$: BISL2 #^X80000000,R0570$: BISL2 #^X40000000,R0 80$: BISL2 #^X20000000,R0_90$: BISL2 #^X10000000,R0N100$: BISL2 #^X08000000,R0110$: BISL2 #^X04000000,R0120$: BISL2 #^X02000000,R0130$: BISL2 #^X01000000,R0140$: BISL2 #^X00800000,R0D150$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager .PAGE: .SBTTL LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;+++4; LD_ENQ_DEV_LOCK, Enqueue device lock for LD device; ; Functional description:0;MJ; This routine will enqueue the device lock of the specified device.>; It will check if the device is used on another node anywhereE; in the cluster. If true then it's only allowed if we ordered shared D; access and if the remote filename matches ours so that XQP locking ; will work.;+%; The locking protocol is as follows:p;eI; First we will enqueue an EX-mode lock on our resource. If it is grantedaF; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block andnF; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;LG; If we don't get it immediately then it means that anothe.-$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U"er thread ownsNB; the lock. In that case we implicitely triggered the blocking astH; routine by enqueueing the EX lock. This blocking ast routine will thenE; convert the lock back to NL, and it will immediately try to convert;E; it back to CR. After we get the lock, we will check the value block H; and then either dequeue it or convert it back to CR. After that action<; the blocking ast routine will convert the lock back to CR.;CI; If we encounter an invalid lock value block during this process we willwI; just rewrite it with the same contents. It's not documented, but we canhH; rely on the value being equal to what it was before it became invalid.A; (courtesy of Sandy Snaman, it may be documented in the future).E; ;s ; Inputs:a;9; R3 - address of the IRP (I/O request packet)C9; R5 - address of the UCB (unit control block)n); UCB$T_LD_DEV_RESNAM(R5) - Resource name ; ; IOLOCK8 (=SCS) forklock heldp; ; Outputs:; ; R0 - statusl; SS$_NORMAL - Successc5; SS$_FILALRACC - File in use in an incompatible wayb#; other - Returned by lockmanageri;t*; Only R3-R5 are preserved as we may fork.;n;---! UNIVERSAL_SYMBOL LD_ENQ_DEV_LOCKe;LD_ENQ_DEV_LOCK:e% .JSB_ENTRY INPUT=,OUTPUT=  ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),-$ RESNAME=UCB$T_LD_DEV_RESNAM(R5),- EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BEQL 5$ ; Yesr BLBS R0,50$ ; Ok BRW 90$ ; Exit;:C; We are the first one queueing a lock for this resource. Setup the1F; lock value block, convert the lock back to CR and exit with success.; F5$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value block! MOVL UCB$L_DDB(R5),R2 ; Get DDB , MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length" CMPL R0,#6 ; Max we can handle BLEQ 10$ ; Okay: BRW 80$ ; Too much10$: PUSHR #^M( INCB R0 ; Length including len field0 MOVC3 R0,DDB$T_NAME(R2),- ; Move name + length LDDLVB_T_DEVNAM(R1)u POPR #^Mt%ASSUME LDDLVB_W_SEQ EQ LDDLVB_W_FID+2 $ CLRL LDDLVB_W_FID(R1) ; Zero block CLRW LDDLVB_W_RVN(R1)( BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),15$0 MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address+ MOVW FCB$W_FID_NUM(R0),- ; Insert File IDu LDDLVB_W_FID(R1) MOVW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) MOVW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1)615$: MOVB DDB$L_ALLOCLS(R2),- ; Fill allocation class LDDLVB_B_ALLOCLS(R1) .DISABLE FLAGGING% MOVW UCB$W_UNIT(R5),- ; Unit number- LDDLVB_W_UNIT(R1)c .ENABLE FLAGGINGU"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-n EFLAGS=,- ERROR=90$; ; Lock is converted to CR.;h8 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status) BRW 90$ ; Exit with completion statusC;K; Lock is granted as EX.; <50$: MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status;NN; Check for an invalid value block. If that's the case then we can rely on theL; fact that the contents of the valueblock are the same as they were before.J; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will; revalidate it.;e1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?l& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrongI;LD; We now have the lock in EX mode. Check the lock value block to see; if we match.;BG60$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value block ! MOVL UCB$L_DDB(R5),R2 ; Get DDBS, MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length$ INCB R0 ; Check length field too PUSHR #^M1 CMPC3 R0,DDB$T_NAME(R2),- ; Check name + length$ LDDLVB_T_DEVNAM(R1)P POPR #^MN BNEQ 80$ ; No matche( BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),70$0 MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address* CMPW FCB$W_FID_NUM(R0),- ; Check File ID LDDLVB_W_FID(R1) BNEQ 80$ ; No matche CMPW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) BNEQ 80$ ; No matche CMPW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1) BNEQ 80$ ; No matchM770$: CMPB DDB$L_ALLOCLS(R2),- ; Check allocation class_ LDDLVB_B_ALLOCLS(R1) BNEQ 80$ ; No matchT .DISABLE FLAGGING% CMPW UCB$W_UNIT(R5),- ; Unit numberP LDDLVB_W_UNIT(R1)  .ENABLE FLAGGINGS BNEQ 80$ ; No matchg) BRW 20$ ; Convert down to CR and exitV;SE; Dequeue the lock since we're not allowed to use the file or device.oF; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.;E.80$: DEQ_LOCK LOCKID=UCB$Q_LD_DEV_LKSB+4(R5),- VALBLOCK=UCB$A_LD_DEV_LVB(R5) ; Get rid of file lock BLBC R0,90$ ; Errorn) MOVZWL #SS$_DEVALLOC,R0 ; Device In usew90$: RSB .PAGEC .SBTTL LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routine ;+++=; LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routine ;y; Functional description:e;H; This routine will be called by the lockmanager when someone elseG; tries to enqueue an incompatible lock on the LD device resource name.;iB; We will convert the lock back to NL, and then we immediately tryD; to convert it back to CR. If this second conversion is successfull; we will just exit.;tC; If the second conversion reveals a 'value block invalid' error wedB; must revalidate the lock. We do this by converting the lock back=; to NL, up to EX, back to NL and then back to CR. This looksnB; over-complicated but this is needed because more than one threadE; may be active at the same time, and we must make sure that we won'taA; block other threads which in the conversion process. The secondmD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.e;u ; Inputs:v; 2; 4(AP) - address of context parameter 1;u; IOLOCK8 (=SCS) forklock heldo;. ; Outputs:;n*; Only R3-R5 are preserved as we may fork.; ;---# UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTNe;LD_ENQ_DEV_BLKRTN:n .CALL_ENTRY- MOVL FLK_PRM1(AP),R5 ; Recover UCB addresso;aD; We have to create our own KPB since the KPB setup by the start I/O+; routine may be in use when we enter here.n;sA10$: KP_ALLOCATE_KPB KPB=UCB$L_LD_DEVKPB(R5),- ; Allocate new KPB FLAGS=#KPB$M_DEALLOC_AT_END BLBC R0,20$ ; Errorl/ MOVL UCB$L_LD_DEVKPB(R5),R0 ; Get KPB address2 MOVL R5,KPB$PS_UCB(R0) ; Save UCB address in KPB# KP_START KPB=UCB$L_LD_DEVKPB(R5),-c% ROUTINE=LD_ENQ_DEV_BLKRTN_ACTION,- " REGISTERS=#KPREG$K_HLL_REG_MASK RET;eI20$: FORK_WAIT ENVIRONMENT=CALL,- ; Wait a while after allocation failure- RETURN=RETf BRW 10$;A* UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTN_ACTION;LD_ENQ_DEV_BLKRTN_ACTION: .CALL_ENTRY& MOVL 4(AP),R0 ; Recover KPB address& MOVL KPB$PS_UCB(R0),R5 ; Recover UCB ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_DEVKPB(R5): MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Check completion status BLBS R0,10$ ; Success BRW 70$; E; Lock is converted to NL, convert back to CR. Specify QUECVT so thate=; the requesting thread gets it's chance to acquire the lock.x;w"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-$ EFLAGS=,- ERROR=80$,- KPBADR=UCB$L_LD_DEVKPB(R5)N;R; Lock is converted to CR.;W3 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid? & BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit* BRW 90$ ; Bugcheck on all other errors930$: KP_END KPB=UCB$L_LD_DEVKPB(R5) ; Finish this thread ;LJ; We have an invalid lock value block. Convert the lock to NL, then to EX,?; then to NL to rewrite the lockvalue block, and finally to CR.); "40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,-E ERROR=100$,- KPBADR=UCB$L_LD_DEVKPB(R5) - MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get statuso BLBC R0,110$ ; Error;C.; Lock is converted to NL, convert back to EX.;B ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=120$,-a KPBADR=UCB$L_LD_DEVKPB(R5)d- MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get statusl1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?k BEQL 50$ ; Acceptableb* BLBC R0,130$ ; Check completion status; /G$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Uu"vI; Lock is converted to EX, convert back to NL to rewrite the value block.N;A"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=140$,-  KPBADR=UCB$L_LD_DEVKPB(R5)l0 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Quit on error BLBC R0,150$R- BRW 10$ ; Ok, convert back to CR and exitL;A;; Bugcheck since there's nobody to return this error to....-; 60$: BISL2 #^X80000000,R0 70$: BISL2 #^X40000000,R0V80$: BISL2 #^X20000000,R0s90$: BISL2 #^X10000000,R0B100$: BISL2 #^X08000000,R0110$: BISL2 #^X04000000,R0120$: BISL2 #^X02000000,R0130$: BISL2 #^X01000000,R0140$: BISL2 #^X00800000,R0D150$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager .PAGEI .SBTTL LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOMU;+++D; LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM;; Functional description: ; @; This routine allocates and fills a buffer with watchpoint info<; and queues this buffer to LD_OPCOM_AST as an exec-mode ASTA; routine in the context of the issueing process. The AST routineYE; will then invoke the system-service $SNDOPR to process the request.;H; We use an EXEC-mode AST because $SNDOPR is an execmode system service,A; which cannot be called from kernel mode. We don't want to use a-@; usermode AST because we then need to modify the pageprotectionG; of the ACB to allow user-read access. If there is no issueing process5D; (a mount-verification thread for example) then we queue the AST to ; the OPCOM.; ; Inputs:a;e+; R1 - LDWATCH structure addressa9; R3 - address of the IRP (I/O request packet)e9; R5 - address of the UCB (unit control block)i;i ; Outputs:;V ; R0 - StatusA;(@; The routine must preserve all registers except R0 and R2;B;--- UNIVERSAL_SYMBOL LD_SEND_OPCOM ;LD_SEND_OPCOM:f( .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^MB- MOVL IRP$L_PID(R3),R4 ; I/O from a process?R1 BGTR 20$ ; Yes (no mount-verification thread) ;lU; This thread has no PID but a system-space address (mount-verification for example).1N; We must queue an AST to a process, but we don't know which. We can't use theO; SWAPPER for this purpose because it never leaves kernel-mode, so an exec-modeuP; ast would not get delivered. We can queue the ast to OPCOM. If it's not there,;; then there's no need to process the opcom request anyway. ;e1 BSBW LD_FIND_OPCOM ; Check if OPCOM is runnings BLBS R0,20$ ; YeswA MOVZWL #SS$_NORMAL,R0 ; No need to bother this thread, dismissc10$: BRW 60$ ; Exit020$: MOVZWL #LDSNDOPRLST_K_LENGTH,R1 ; This size$ JSB G^EXE$ALONONPAGED ; Get buffer BLBC R0,10$ ; Quit+ MOVW R1,LDSNDOPRLST_W_SIZE(R2) ; Save sizek MOVL (SP),R1 % MOVB #DYN$C_ACB,- ; Used as an ACBo LDSNDOPRLST_B_TYPE(R2)5 CLRL LDSNDOPRLST_L_KAST(R2) ; Assume normal processn0 CMPL R4,IRP$L_PID(R3) ; Just a normal process? BEQL 30$ ; Yes/ INCL LDSNDOPRLST_L_KAST(R2) ; Special processe>30$: MOVL R4,LDSNDOPRLST_L_PID(R2) ; Issueing process or OPCOMA MOVB #,- ; Exec mode, astroutine willf3 LDSNDOPRLST_B_RMOD(R2) ; deallocate this bufferQ, MOVAB LD_OPCOM_AST,- ; Routine to execute LDSNDOPRLST_L_AST(R2)8@ MOVL R2,LDSNDOPRLST_L_ASTPRM(R2) ; Setup our block as parameter5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modeQ LDWATCH_W_FLAGS(R1),40$B% MOVL LDWATCH_L_LBN(R1),- ; Move lbn) LDSNDOPRLST_L_LBN(R2)s BRB 50$640$: MOVL LDWATCH_L_VBN(R1),- ; Move vbn for filemode LDSNDOPRLST_L_LBN(R2)e50$:5 ASSUME LDSNDOPRLST_W_ACTION EQ LDSNDOPRLST_W_FLAGS+2L4 MOVL LDWATCH_W_FLAGS(R1),- ; Move flags and action LDSNDOPRLST_W_FLAGS(R2)$5 ASSUME LDSNDOPRLST_W_RETCODE EQ LDSNDOPRLST_W_FUNC+2d7 MOVL LDWATCH_W_FUNC(R1),- ; Move function and retcodeB LDSNDOPRLST_W_FUNC(R2)8 ASSUME LDSNDOPRLST_W_FID_NUM+2 EQ LDSNDOPRLST_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R1),- ; Save FID0 LDSNDOPRLST_W_FID_NUM(R2), MOVW LDWATCH_W_FID_RVN(R1),-o LDSNDOPRLST_W_FID_RVN(R2)B PUSHR #^M/ MOVZWL #LDSNDOPRLST_K_DEVNAM-1,R0 ; Buffersize_ .DISABLE FLAGGING< MOVAB LDSNDOPRLST_T_DEVNAM+1(R2),R1 ; Buffer for devicename .ENABLE FLAGGINGB% MOVL #-2,R4 ; DVI$_DISPLAY_DEVNAMS5 CALL_CVT_DEVNAM ; Get devicename in readable form,8 MOVB R1,LDSNDOPRLST_T_DEVNAM(R2) ; Save returned length MOVL R2,R5 ; ACB in R54 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment! JSB G^SCH$QAST ; Queue the AST  POPR #^MS BLBS R0,60$ ; All ok; J; We can still get a NONEXPR error back, despite the check at the start ofK; this routine for the presence of our process or OPCOM. If OPCOM went awayLG; we may get the error. This could normally only happen on SMP systems.V;>$ CMPL R0,#SS$_NONEXPR ; Not found?* BNEQ 70$ ; No, something else is wrong2 MOVZWL #SS$_NORMAL,R0 ; Pretend nothing's wrong60$: POPR #^M RSB<70$: BUG_CHECK INCONSTATE,FATAL ; We want to know about this .PAGE8 .SBTTL LD_FIND_OPCOM, Find the pid of the OPCOM process;+++3; LD_FIND_OPCOM, Find the pid of the OPCOM processF;s; Functional description:v;n=; This routine locates the internal pid of the OPCOM process.;h ; Inputs: ;r ; None; ; Outputs:;l ; R0 - Status; R4 - Internal pid of opcom;-@; The routine must preserve all registers except R0 and R4; ;--- UNIVERSAL_SYMBOL LD_FIND_OPCOMP;LD_FIND_OPCOM:C .JSB_ENTRY OUTPUT= PUSHR #^M , LOCK LOCKNAME=SCHED,- ; Lock sched database$ SAVIPL=-(SP),- ; Save current ipl$ PRESERVE=NO ; R0 may be destroyed2 MOVL G^SCH$GL_PCBVEC,R0 ; Point to process vector CLRL R1 ; Setup index(10$: MOVL (R0)[R1],R2 ; Get a PCB entry' CMPL R2,G^SCH$AR_NULLPCB ; NULL entry?P BEQL 20$ ; Yes, get nexte+ CMPL PCB$L_UIC(R2),- ; UIC match ([1,4])? #^X00010004  BNEQ 20$ ; No: MOVZBL PCB$T_LNAME(R2),R3 ; Get bytecount of process name INCL R3 ; Check count as well PUSHR #^M- CMPC3 R3,PCB$T_LNAME(R2),- ; Check for OPCOM OPCOM_NAME POPR #^MC BNEQ 20$ ; No match) MOVL PCB$L_PID(R2),R4 ; Get internal pidM" MOVZWL #SS$_NORMAL,R0 ; Success! BRB 30$/20$: AOBLEQ G^SCH$GL_MAXPIX,R1,10$ ; Next entryt$ MOVZWL #SS$_NONEXPR,R0 ; Not found430$: UNLOCK LOCKNAME=SCHED,- ; Unlock sched database( NEWIPL=(SP)+,- ; Restore previous ipl2 CONDITION=RESTORE ; Restore access count on lock POPR #^M RSB .PAGE? .SBTTL LD_RETURN_QUOTA, Return quota of charged buffer to userL;+++:; LD_RETURN_QUOTA, Return quota of charged buffer to user;A; Functional description: ;m;; This routine returns the quota of a buffer we charged forc;; back to a user. Since we may be in system context we will <; queue an ast to the user to return the quota. If the input<; parameter is < 0 it is a system space address of a WCB for$; which we need to return the quota.;u ; Inputs:h;nO; R1 - number of bytes to return to BYTCNT and BYTLM, or WCB pointere; R4 - pid of process to creditl ; R5 - UCB;u ; Outputs:;l ; R0 - statusw;.D; The routine must preserve all registers except R0-R2 and R4.; ;---! UNIVERSAL_SYMBOL LD_RETURN_QUOTA ;LD_RETURN_QUOTA:t( .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^M BSBW LD_ALLO_LDIOB ; Get ACBw" BLBC R0,10$ ; Check for errors MOVL R2,R5 ; Get ACB address5 MOVL R1,ACB$L_ASTPRM(R5) ; Save bytecount to returns" MOVL R4,ACB$L_PID(R5) ; Save PID? MOVB #,- ; Special kernel mode ast  ACB$B_RMOD(R5), MOVAB LD_QUOTA_AST,- ; Routine to execute ACB$L_KAST(R5)4 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment! JSB G^SCH$QAST ; Queue the ASTa*10$: POPR #^M ; Restore registers RSB .PAGE1 .SBTTL LD_QUOTA_AST, Return quota action routine ;+++,; LD_QUOTA_AST, Return quota action routine;; Functional description:w; @; This routine is called as an AST routine in the context of the; process issueing the I/O.c; <; This routine has two functions, depending on ACB$L_ASTPRM:;m5; If < 0, it is a pointer to a WCB for which we havew#; to return the quota to the user.5; If > 0 it is the number of bytes to credit a user.e;t ; Inputs:;1; R4 - address of the PCB (process control block)P$; R5 - address of the ACB6; ACB$L_ASTPRM(R5) - if > 0: Number of bytes to return; if < 0: WCB address; =; The routine must preserve all registers except R0-R5.0;s UNIVERSAL_SYMBOL LD_QUOTA_AST0+-<$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U";LD_QUOTA_AST: .CALL_ENTRY INPUT=* MOVL ACB$L_ASTPRM(R5),R3 ; Get parameter MOVL R5,R0 ; Address of ACBe( JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL R3,R0Q BGTR 20$ ; Byte countK;E; Remove the pointer from the channel to the window, and return the3; previously allocated bytecount quota to the user._;_5 BBSS #WCB$V_SHRWCB,- ; Make WCB a shared structureC. WCB$B_ACCESS(R0),10$ ; if not already done3 JSB G^MMG$RET_BYT_QUOTA ; Return byte count quota$10$: .PRESERVE ATOMICITY5 ADDL2 #1,WCB$L_REFCNT(R0) ; Count another reference .NOPRESERVE ATOMICITY BRB 30$120$: JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaC30$: MOVZWL #SS$_NORMAL,R0 RET .PAGE+ .SBTTL LD_COMPLETE, I/O completion routine ;+++&; LD_COMPLETE, I/O Completion routine.;h?; This routine locates the IRP whose address is into R5 in the8?; LDIO list, removes the entry from the queue, restores the PID 7; and inserts the IRP back in the I/O completion queue. ;e ; Inputs:g; ; R5 - IRP; IPL - IPL$_IOPOST<;R ; This routine may use R0 to R5.;L;--- UNIVERSAL_SYMBOL LD_COMPLETE) ;LD_COMPLETE: .CALL_ENTRY INPUT=B& MOVL IRP$L_LD_LDUCB(R5),R4 ; Get UCB5 FORKLOCK LOCK=UCB$B_FLCK(R4),- ; Raise to Fork levelc SAVIPL=-(SP)( MOVL IRP$L_LD_LDIOB(R5),R0 ; Get LDIOB* MOVL LDIOB_L_IRP(R0),R0 ; Get master IRP" MOVL IRP$PS_KPB(R0),R0 ; Get KPB) MOVL R5,KPB$PS_IRP(R0) ; Save clone IRPR, MOVL R4,KPB$PS_UCB(R0) ; Insert UCB in KPB;lJ; Continue postprocessing as a kernel thread. That gives us the possiblity!; to stall and fork when we want.U;( KP_START KPB=R0,- ROUTINE=LD_COMPLETE_KERNEL,-_" REGISTERS=#KPREG$K_HLL_REG_MASK;) ; Unlock, return at IPL$_IOPOST.; 0 FORKUNLOCK LOCK=UCB$B_FLCK(R4),- ; Release lock NEWIPL=(SP)+,-o PRESERVE=NO,- CONDITION=RESTORE RET$ UNIVERSAL_SYMBOL LD_COMPLETE_KERNEL;LD_COMPLETE_KERNEL: .CALL_ENTRY MOVL 4(AP),R0 ; Get KPB( MOVL KPB$PS_IRP(R0),R5 ; Get clone IRP MOVL R5,R3 ; Save IRPt; MOVL IRP$L_LD_LDUCB(R5),R5 ; Get UCB address logical diska5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level  SAVIPL=-(SP),-t PRESERVE=NO8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB5 FORKLOCK LOCK=UCB$B_FLCK(R4),- ; Raise to Fork levelC& SAVIPL=-(SP),- ; (Physical device) PRESERVE=NO .PRESERVE ATOMICITY. DECL UCB$L_QLEN(R4) ; Decrease queue length .NOPRESERVE ATOMICITY0 FORKUNLOCK LOCK=UCB$B_FLCK(R4),- ; Release lock NEWIPL=(SP)+,-B PRESERVE=NO,- CONDITION=RESTORE0 MOVL IRP$L_LD_LDIOB(R3),R2 ; Get LDIOB address;5F; Remove the forwarded IRP from the LDIOB queue, and deallocate it. IfF; this was the last outstanding IRP, then complete the I/O. Before de-B; allocation, update the accumulated byte count and status fields.;<6 REMQUE IRP$L_LD_FWDQFL(R3),R0 ; Remove forwarded IRP7 SUBL2 #IRP$L_LD_FWDQFL,R0 ; Point to real IRP sectionV .DISABLE FLAGGING: ADDL2 IRP$L_IOST1+2(R0),- ; Count accumulated byte count LDIOB_L_ABCNT(R2)F .ENABLE FLAGGING 4 BLBS IRP$L_IOST1(R0),10$ ; Check the return status8 MOVW IRP$L_IOST1(R0),LDIOB_W_IOST(R2); Update if errors010$: PUSHL R2 ; Save reg destroyed by dealloc. JSB G^EXE$DEANONPAGED ; Deallocate the FWIRP MOVL (SP)+,R25 ADAWI #-1,LDIOB_W_IRPCNT(R2) ; Decrement ref. count BNEQ 20$ ; Not yet ready/ MOVL LDIOB_L_IRP(R2),R3 ; Restore correct IRP ;.B; Set the return status and the accumulated byte count in the IRP.;t> MOVW LDIOB_W_IOST(R2),IRP$L_IOST1(R3); Copy the return status .DISABLE FLAGGING/ MOVL LDIOB_L_ABCNT(R2),- ; And the byte countc IRP$L_IOST1+2(R3)d .ENABLE FLAGGINGi+ CLRW IRP$L_IOST1+6(R3) ; Zero unused word_5 REMQUE LDIOB_L_QFL(R2),R4 ; Remove LDIOB from queue < BSBW LD_SAVE_TRACE_ALT ; Save trace data and dealloc LDIOB; BSBW LD_COMPLETE_FINISH ; Send IRP through postprocessingE MOVL 4(AP),R0 ; Get KPB= BISL2 #KPB$M_DEALLOC_AT_END,- ; Deallocate when thread endse KPB$IS_FLAGS(R0)420$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-l PRESERVE=NO,- CONDITION=RESTORE# KP_END KPB=4(AP) ; End of threadp RET$ UNIVERSAL_SYMBOL LD_COMPLETE_FINISH;LD_COMPLETE_FINISH: .JSB_ENTRY INPUT=e1 CALL_POST_IRP ; Insert IRP back in post queuef. BSBW POST_PACKACK ; Post packack processing4 INCL UCB$L_OPCNT(R5) ; Count I/O (Logical device)4 BBC #UCB$V_LD_DISPEN,- ; Is a disconnect pending ? UCB$W_LD_FLAGS(R5),40$ PUSHR #^M PUSHL R5 ; UCB PUSHL UCB$Q_FR4(R5) ; R4. PUSHL UCB$Q_FR3(R5) ; R3t2 CALLS #3,@UCB$L_FPC(R5) ; Resume the fork thread POPR #^Me40$: .IF DF MDDRIVER_WORKAROUNDh;wM; Work around MDdriver bug. If an erase function is given to DECRAM, then the J; iosb does not contain the bytecount but zero. In a certain configurationK; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attempts L; to erase a number of blocks, and it looks in the returned bytecount to see; how much is left to do.a;p4 BBC #UCB$V_LD_DECRAM,- ; Connected to DECRAM disk? UCB$W_LD_FLAGS(R5),50$< CMPZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Check the function code# IRP$L_FUNC(R3),#IO$_DSE ; Erase?t BNEQ 50$ ; No  .DISABLE FLAGGING@ MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount .ENABLE FLAGGINGB50$: .ENDC .IF DF DUDRIVER_WORKAROUND;FM; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueing,M; an IRP to the physical device UCB when UCB$L_RWAITCNT is not zero. This may_N; happen when a connection runs out of credits. When the credits are returned,I; the DUdriver never looks at the UCB$L_IOQFL queue with pending packets, C; which causes everything to wait for this packet. Triggering mountJ; verification for the device will solve it, because in mv's return path aH; check is made for a non empty i/o queue. We work around it by checkingF; for stalled I/O's in our return path. This is not guaranteed to workM; 100% of the time, but since i made this modification i could not get thingsP; to go wrong.;$ PUSHL R5 ; Save logical UCBl8 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the physical disk UCBB BBS #DEV$V_SCSI,UCB$L_DEVCHAR2(R5),60$ ; Leave SCSI devices alone5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelh& SAVIPL=-(SP),- ; (Physical device) PRESERVE=NO/ JSB G^SCS$UNSTALLUCB ; Check for queued i/o'sB0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-O PRESERVE=NO,- CONDITION=RESTORE60$: MOVL (SP)+,R5 .ENDC RSB;r UNIVERSAL_SYMBOL POST_PACKACK;POST_PACKACK: .JSB_ENTRY INPUT= # EXTZV #IRP$V_FCODE,#IRP$S_FCODE, -v0 IRP$L_FUNC(R3),R1 ; Extract the function code" CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; Noo4 BBC #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),10$;LA; Setup device characteristics in case they were not available ate; drive connect time.t;e8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB- MOVB UCB$B_DEVTYPE(R4),- ; Copy device type_ UCB$B_DEVTYPE(R5)L&ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1(ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+11 MOVL UCB$B_SECTORS(R4),- ; Copy sectors, tracks0$ UCB$B_SECTORS(R5) ; and cylinders0 MOVL UCB$L_MAXBLOCK(R4),- ; Copy maximum block UCB$L_MAXBLOCK(R5)10$: RSB .PAGE- .SBTTL LD_CANCEL, Generic Cancel I/O routineO;+++(; LD_CANCEL, Generic Cancel I/O routine;P; Functional description:V;S<; This routine cancels all outstanding I/O for the specified;; channel and PID. We also check if the IRP is in the traceC<; wait queue and remove that if true. We also check if there2; was a watchpoint suspended and process that too.;L ; Inputs:C;_; R2 - Channel index numberE; R3 - IRP from UCB$L_IRPK ; R4 - PCB ; R5 - UCB#; R8 - Cancel reason code, one of :D*; CAN$C_CANCEL If called through $CANCEL; system service.e*; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service.;e ; Outputs:;s5; The routine must preserve all registers exept R0-R3X;0;--- UNIVERSAL_SYMBOL LD_CANCELV ;LD_CANCEL:# $DRIVER_CANCEL_ENTRYS2 CMPL R8,#CAN$C_DASSGN ; Called through $DASSGN ?! BEQL 10$ ; Yes, no active I/O * BSBW LD_CANCEL_IO ; Cancel this request910$: BSBW LD_CANCEL_TRACE ; Cancel pending trace thread;5 BSBW LD_CANCEL_WATCH ; Cancel suspended watchpointD2 BBC #UCB$V_LD_PROTECT,- ; Is the unit protected? UCB$W_LD_FLAGS(R5),15$@ BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; Yes, re-instate protection! ; (cleared by IOC$DISMOUNT) 715$: BBC #UCB$V_DELETEUCB,- ; Only if we're going away  UCB$104Ю$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1Uy5"L_STS(R5),20$iA BICL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Reset cluster wide visiblee: MOVL UCB$L_LD_CPID(R5),UCB$L_CPID(R5); Restore charge pid5 MOVW UCB$W_LD_CHARGE(R5),- ; Restore charged amount; UCB$W_CHARGE(R5)20$: RET ; Returns .PAGE( .SBTTL LD_CANCEL_IO, Cancel I/O routine;+++3; LD_CANCEL_IO, Cancels I/O operations in progressA;b; Functional description:f;h<; This routine cancels all outstanding I/O for the specified;; channel and PID. We search the active IO list for LDIOB'sf<; containing that specific PID. If found, the channel number:; in the IRP is checked against the cancel channel number.<; If a match is found the IRP is traced to the physical disk:; driver where it is either removed from the IO wait queue9; and put into the post-processing queue, or the physicalp9; disk it's cancel IO routine is called, with the correctL ; registers.;P ; Inputs:E;O; R2 - Channel index number,; R3 - IRP from UCB$L_IRP# ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codeo;s ; Outputs:; 5; The routine must preserve all registers exept R0-R3a5; The routine may set the UCB CANCEL bit in UCB$L_STSt; ;--- UNIVERSAL_SYMBOL LD_CANCEL_IO;LD_CANCEL_IO:" .JSB_ENTRY INPUT= PUSHL R6P8 MOVAL UCB$L_LD_AIOFL(R5),R0 ; Get addr. active IO list MOVL R0,R1 ; Copy it to R1,10$: MOVL LDIOB_L_QFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 30$ ; Yes, whole list done  PUSHL R0 ; Save R0A CMPL LDIOB_L_PID(R1),IRP$L_PID(R3) ; Is this the right process ?M BNEQ 20$ ; No0+ MOVL LDIOB_L_IRP(R1),R0 ; Get IRP address6 CMPL IRP$L_CHAN(R0),R2 ; Is this the right channel ? BEQL 40$ ; Yes*20$: MOVL (SP)+,R0 ; Copy for next block BRB 10$ ; Loop through listP30$: MOVL (SP)+,R6 RSB940$: MOVAL LDIOB_L_FWDQFL(R1),R0 ; Extract forwarded IRPD MOVL R0,R6 ; Copy it to R0+ MOVL (R0),R0 ; Get next packet in queue CMPL R6,R0 ; Back at start ? BEQL 20$ ; Continue loop6 PUSHR #^M ; Save some registers we need4 MOVL UCB$L_LD_PDUCB(R5),R3 ; Get the phys.disk UCB) MOVZBL UCB$B_FLCK(R5),R1 ; Get our FLCKE< CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R3) ; Compare Forklock index* BLSS 50$ ; Ok, ours is larger or equal4 MOVZBL UCB$B_FLCK(R3),R1 ; Get the lowest of the 2,50$: MOVL R3,R5 ; Change to Phys.disk UCB" FORKLOCK LOCK=R1,- ; Synchronize SAVIPL=-(SP)o9 MOVAL UCB$L_IOQFL(R5),R3 ; Get addr. of wait queue listA MOVL R3,R1 ; Copy it into R1<60$: MOVL IRP$L_IOQFL(R1),R1 ; Get next packet in the queue CMPL R1,R3 ; End of queue ?O' BEQL 80$ ; Not found, call CANCELIOR" CMPL R1,R0 ; Is this the one ?" BNEQ 60$ ; No, search the rest6 REMQUE IRP$L_IOQFL(R1),R3 ; Remove it from the queue= BBC #IRP$V_BUFIO,IRP$L_STS(R3),70$ ; Direct or buffered IO ?_6 BICW2 #IRP$M_FUNC,IRP$L_STS(R3) ; Clear buffered read970$: MOVW #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return statusW1 CALL_POST_IRP ; Insert IRP back in post queue0 BRB 90$080$: MOVL UCB$L_DDT(R5),R0 ; Get address of DDT7 MOVL UCB$L_IRP(R5),R3 ; Get current IO packet address>1 CALL_CANCELIO SAVE_R0R1=NO ; Call cancel IO rtn490$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-e PRESERVE=NO,- CONDITION=RESTORE1 POPR #^M ; Restore saved registers  BRW 20$ ; Continue searchM .PAGE1 .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routineV;+++,; LD_CANCEL_TRACE, Cancel trace I/O routine;u; Functional description:G;HC; This routine checks if there's an IRP in the trace wait queue and F; transfer that to the post-processing if true. We can't use the inputD; IRP since it may currently not be valid (it comes from UCB$L_IRP),+; so we check against the PID from the PCB.r; ; Inputs:n;a; R2 - Channel index numbers; R3 - IRP from UCB$L_IRPP ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code ;w ; Outputs:;S5; The routine must preserve all registers exept R0-R3^;1;---! UNIVERSAL_SYMBOL LD_CANCEL_TRACEA;LD_CANCEL_TRACE:o$ .JSB_ENTRY INPUT=,- PRESERVE=B MOVAL UCB$L_LD_TRCWAITQFL(R5),R0 ; Get addr. of waiting trace irp MOVL R0,R1 ; Copy it to R1,10$: MOVL IRP$L_IOQFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 20$ ; Yes, whole list done ? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?- BNEQ 10$ ; Non6 CMPL IRP$L_CHAN(R1),R2 ; Is this the right channel ? BNEQ 10$ ; No ( CLRL IRP$L_SVAPTE(R1) ; No more SVAPTE7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statusA' REMQUE IRP$L_IOQFL(R1),R3 ; Remove itL1 CALL_POST_IRP ; Insert IRP back in post queuea20$: RSB .PAGE1 .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routineR;+++,; LD_CANCEL_WATCH, Cancel watch I/O routine;P; Functional description:_;L;; This routine will check if there's a suspended watchpoint_:; pending for the I/O. If true we will remove it. We can't;; use the input IRP since it may currently not be valid (it 9; comes from UCB$L_IRP), so we check against the PID from> ; the PCB.;$ ; Inputs:,; ; R2 - Channel index numberE; R3 - IRP from UCB$L_IRPE ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codet;a ; Outputs:; 5; The routine must preserve all registers exept R0-R3C;L;---! UNIVERSAL_SYMBOL LD_CANCEL_WATCH$;LD_CANCEL_WATCH:f$ .JSB_ENTRY INPUT=,- PRESERVE=5 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active?T BEQL 40$ ; Nor? MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entryB MOVL R0,R1 ; Save for later 010$: MOVL LDWATCH_L_FLINK(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 40$ ; Yes, whole list done 9 TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue?e BEQL 10$ ; No  PUSHR #^Mb; MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listheade MOVL R0,R1 ; Save for latera/20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irpB CMPL R1,R0 ; Back at start?h BEQL 30$ ; Yes? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?n BNEQ 20$ ; Non6 CMPL IRP$L_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; No07 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statusi' REMQUE IRP$L_IOQFL(R1),R3 ; Remove itI1 CALL_POST_IRP ; Insert IRP back in post queue POPR #^M7 DECL LDWATCH_L_SUSPCNT(R1) ; Count the one we removed_ BRW 10$/30$: POPR #^M ; Restore, continue scan  BRW 10$40$: RSB .PAGE. .SBTTL LD_ALLO_LDIOB, Allocate I/O data block;+++(; LD_ALLO_LDIOB, Allocate I/O data block;C; Functional description:;;e9; This routine allocates and initializes an I/O datablockS1; which is used for I/O requests as well as traceB2; information. It will get one from nonpaged pool.;S ; Inputs: ;u; R5 - UCB addressR; ; Output:r;s; R0 - Status.; R2 - LDIOB addressa;oA; The routine must preserve all registers except R0 and R2.;u;--- UNIVERSAL_SYMBOL LD_ALLO_LDIOBi;LD_ALLO_LDIOB:n3 .JSB_ENTRY INPUT=,OUTPUT=,PRESERVE=g/ MOVZWL #LDIOB_K_LENGTH,R1 ; Get packet length 3 JSB G^EXE$ALONONPAGED ; Allocate packet from pooli) BLBC R0,10$ ; Check the return statusu- MOVW R1,LDIOB_W_SIZE(R2) ; Setup size field 6 MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field< CLRL LDIOB_L_ABCNT(R2) ; Initialize accumulated byte count0 CLRW LDIOB_W_IRPCNT(R2) ; Initialize irp count; MOVAL LDIOB_L_FWDQFL(R2),- ; Initialize forward IRP queue LDIOB_L_FWDQFL(R2) MOVAL LDIOB_L_FWDQFL(R2),-  LDIOB_L_FWDQBL(R2)+ CLRL LDIOB_L_KPB(R2) ; Clear KPB address. CLRB LDIOB_B_FDT(R2) ; Clear FDT type field" MOVZWL #SS$_NORMAL,R0 ; Success10$: RSB ; ReturnB .PAGE- .SBTTL LD_ALLO_FWIRP, Allocate a forward IRPT;+++'; LD_ALLO_FWIRP, Allocate a forward IRPr;o; Functional description :;a6; This routine allocates a FW IRP. It's allocated from2; nonpaged pool and initialized. If there's a pool0; shortage we will wait and retry until success.;o ; Inputs :;:; R4 - LDIOB (LDIOB_L_KPB may be used if allocation fails) ; R5 - UCB;O ; Outputs :2;,; R3 - New allocated IRP ; R5 - UCB;c;--- UNIVERSAL_SYMBOL LD_ALLO_FWIRP;LD_ALLO_FWIRP:E( .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^M410$: MOVZWL #IRP$K_LD_IRPLEN,R1 ; Get packet length3 JSB G^EXE$ALONONPAGED ; Allocate packet from pool) BLBC R0,20$ ; Check the return statuss MOVL R2,R3 ; Copy it to R3 PUSHR #^M , MOVC5 #0,(SP),#0,R1,(R3) ; Zero the packet POPR #^M1 MOVW R1,IRP$W_SIZE(R3) ; Initialize 2A_F$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U"packet size " ASSUME IRP$B_TYPE+1 EQ IRP$B_RMOD8 MOVW #DYN$C_IRP,IRP$B_TYPE(R3) ; Initialize packet type POPR #^M RSB ; Return<120$: MOVL LDIOB_L_KPB(R4),R0 ; Get KPB for stallC6 MOVAB KPB$PS_FQFL(R0),R1 ; Point to forkblock in KPB= MOVB #SPL$C_IOLOCK8,FKB$B_FLCK(R1) ; Init spinlock_. KP_STALL_FORK_WAIT KPB=R0 ; And wait a while BRW 10$ ; Try again  .PAGE;+++!; LD_DEAL_LDIOB, Deallocate LDIOBB;_; Functional description :;l7; This routine restores LDIOBs in the LDIOB free queue.s; ; Inputs :; ; R0 - LDIOB ; R3 - IRP ; R5 - UCB;K ; Outputs :0;; None;L;--- UNIVERSAL_SYMBOL LD_DEAL_LDIOBE;LD_DEAL_LDIOB: .JSB_ENTRY INPUT=( TSTL LDIOB_L_KPB(R0) ; KPB specified? BEQL 10$ ; No/ MOVL LDIOB_L_KPB(R0),- ; Restore original KPBN IRP$PS_KPB(R3)310$: JSB G^EXE$DEANONPAGED ; Return packet to poolE RSB .PAGE* .SBTTL LD_SAVE_TRACE, Save trace I/O dataC .SBTTL LD_SAVE_TRACEALT, Save trace I/O data, alternate entrypoint_;+++%; LD_SAVE_TRACE - Save trace I/O datas?; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypointV;=; Functional description:M; <; This routine is called just before going to REQCOM to save; the eventual trace data.;o; Inputs for LD_SAVE_TRACE:,;;; R0/R1 - Return statusO9; R3 - address of the IRP (I/O request packet)u9; R5 - address of the UCB (unit control block)F;(; Inputs for LD_SAVE_TRACE_FDT:S;,; R0/R1 - Return statusN1; R4 - address of the PCB (process control block)a9; R5 - address of the UCB (unit control block),; R9 - Function codes; R10 - PIDR;t; Inputs for LD_SAVE_TRACE_ALT:;l9; R3 - address of the IRP (I/O request packet)d"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)o; B; This routine must preserve all registers except R2 and R4.; ;--- UNIVERSAL_SYMBOL LD_SAVE_TRACE_;LD_SAVE_TRACE:B .JSB_ENTRY INPUT=0% MOVL R3,UCB$L_IRP(R5) ; Restore IRP , TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got one  BRW 30$!5$: MOVQ R0,-(SP) ; Save statusA- BSBW LD_ALLO_LDIOB ; Allocate pool to hold2 ; temporary tracedata2 BLBC R0,20$ ; Exit on error  .DISABLE FLAGGING1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSB  .ENABLE FLAGGINGu MOVL R2,R4  MOVL IRP$L_MEDIA(R3),-t LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; BytecountI MOVW IRP$L_FUNC(R3),-" LDIOB_W_FUNC(R4) ; Functioncode' CLRL LDIOB_L_PID(R4) ; Assume no pidG7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),10$ ; Mount verify IRP?U< BBS #IRP$V_SHDIO,IRP$L_STS2(R3),10$ ; Is it shadowing I/O ?2 BBS #IRP$V_PID_S0_MV,- ; Is it special XQP I/O ? IRP$L_STS2(R3),10$" MOVL IRP$L_PID(R3),R0 ; Get IPID" BLSS 10$ ; Some system address0 JSB G^EXE$CVT_IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID10$: .DISABLE FLAGGING, READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time MOVQ LDIOB_Q_EN_TIME(R4),-R5 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end timeU" ; for synchronous functions) .ENABLE FLAGGING:) CLRL LDIOB_L_ELAPSED(R4) ; Elapsed timeP BRW LD_TRACEP+20$: ADDL2 #8,SP ; Recover scratch spaces30$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_FDTn;LD_SAVE_TRACE_FDT:)" .JSB_ENTRY INPUT=&; MOVL R3,UCB$L_IRP(R5) ; Restore IRP. BBC #UCB$V_LD_FDTTRACE,- ; FDT trace active? UCB$W_LD_FLAGS(R5),2$R, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got onet 2$: BRW 30$d!5$: MOVQ R0,-(SP) ; Save status - BSBW LD_ALLO_LDIOB ; Allocate pool to holdr ; temporary tracedata  BLBC R0,20$ ; Exit on errorb+ MOVB #1,LDIOB_B_FDT(R2) ; Flag FDT callera .DISABLE FLAGGING1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSB  .ENABLE FLAGGINGa MOVL R2,R4o CLRL LDIOB_L_MEDIA(R4) ; LBN# CLRL LDIOB_L_BCNT(R4) ; Bytecounts) MOVW R9,LDIOB_W_FUNC(R4) ; FunctioncodeC' CLRL LDIOB_L_PID(R4) ; Assume no pids MOVL R10,R0 ; Get IPID* BLEQ 10$ ; Some system address or zero0 JSB G^EXE$CVT_IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID10$: .DISABLE FLAGGING, READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time MOVQ LDIOB_Q_EN_TIME(R4),-05 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end timeo" ; for synchronous functions) .ENABLE FLAGGING ) CLRL LDIOB_L_ELAPSED(R4) ; Elapsed timec BRW LD_TRACEL+20$: ADDL2 #8,SP ; Recover scratch space 30$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_ALTa;LD_SAVE_TRACE_ALT:U .JSB_ENTRY INPUT=O% MOVL R3,UCB$L_IRP(R5) ; Restore IRPc, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 30$ ; Not there, quit;r@; See if the 'start_time' is filled-in. If not it means that theD; tracebuffer was allocated when an I/O request has been started but/; not completed yet. Forget this entry if true.u;n .DISABLE FLAGGING5 MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start timed BEQL 30$ ; Not set( MOVQ IRP$L_IOST1(R3),- ; IOSB contents LDIOB_Q_STAT(R4) .ENABLE FLAGGINGt$ MOVL LDIOB_L_PID(R4),R0 ; Get IPID' CLRL LDIOB_L_PID(R4) ; Assume no pidv7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),10$ ; Mount verify IRP?r< BBS #IRP$V_SHDIO,IRP$L_STS2(R3),10$ ; Is it shadowing I/O ?2 BBS #IRP$V_PID_S0_MV,- ; Is it special XQP I/O ? IRP$L_STS2(R3),10$ TSTL R0 ; Valid PID?" BLSS 10$ ; Some system address0 JSB G^EXE$CVT_IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID10$: .DISABLE FLAGGING, READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time .ENABLE FLAGGINGv1 BBC #UCB$V_LD_ACCURATE,- ; Check if RSCC wanted UCB$W_LD_FLAGS(R5),20$* EVAX_RSCC ; Get finishing RSCC counter4 EVAX_STQ R0,LDIOB_Q_EN_RSCC(R4) ; Finishing counter< EVAX_SUBQ LDIOB_Q_EN_RSCC(R4),- ; Calculate time difference LDIOB_Q_ST_RSCC(R4),- R0n- SUBL2 #8,SP ; Need quadword scratch spaceG5 EVAX_MULQ R0,#1000000,(SP) ; Convert to microsecondsC; H; The following instruction works for as long as UCB$Q_LD_CYCLEFREQ doesC; not exceed 32 bits. Maybe when future cpu's are still faster..... ;C2 EDIV UCB$Q_LD_CYCLEFREQ(R5),- ; Save elapsed time (SP),- LDIOB_L_ELAPSED(R4),-  R1* ADDL2 #8,SP ; Get rid of scratch space20$: BRW LD_TRACED#30$: MOVL R4,R0 ; Point to LDIOB+) BSBW LD_DEAL_LDIOB ; Get rid of buffere RSB .PAGE$ .SBTTL LD_TRACE, Trace I/O IRP data;+++; LD_TRACE - Trace I/O IRP datae;i; Functional description:l;cD; This routine is called by the start I/O routine to log information); about the I/O request in a tracebuffer.t;sH; The synchronisation of this buffer and the associated pointers is doneM; by means of a mutex in the UCB. This is done to avoid having to synchronizecJ; access to the UCB with a Forklock at IPL 8. If we should do it that way,K; we would have to lock the pages of the userbuffer which would receive theeJ; data in memory. Since this buffer may be several Megabytes in size, thisL; would need a very big WSQUOTA, or splitting the buffer in pieces. It wouldL; also mean that when we get the data out of the buffer we would be at IPL 8I; for an extended time which would block several other things. By using aDK; mutex we can remain at IPL 2 in the FDT routine when we get the data fromcL; the buffer, thus allowing pagefaults. We only must be careful when we wantJ; to write new data into the buffer, and we find the mutex locked. In thatL; case we will queue the current IRP to the mutex wait queue in the UCB. TheJ; thread which locked the Mutex has to check the queue on exit, and resume; any thread which was blocked.d;i ; Inputs:V;C9; R3 - address of the IRP (I/O request packet)C0; (except when called from LD_SAVE_TRACE_FDT)"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)+;:; The LDIOB in R4 will be deallocated when we're finished.;c;; The routine must preserve all registers except R2 and R4.i;--- UNIVERSAL_SYMBOL LD_TRACE ;LD_TRACE:* TSTB LDIOB_B_FDT(R4) ; Called from FDT? BNEQ 5$ ; Yesc/2$: LOCK_TRACE ACCESS=WRITE,- ; Lock the mutexP CONTEXT=SYSTEM  BLBS R0,10$ ; Got it* MOVL IRP$PS_KPB(R3),R0 ; Get KPB address. INSQUE IRP$L_IOQFL(R3),- ; Save IRP in queue @UCB$L_LD_TRCMUTEXQBL(R5)e/ KP_STALL_GENERAL KPB=R0,- ; Wait for callbacke STALL_ROUTINE=LD_TRACE_WAIT* FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Get lock PRESERVE=NO BRW 2$ ; And try again$5$: PUSHL R4 ; Save LDIOB pointer: MOVL G^CTL$GL_PCB,R4 ; Get current process' PCB add3yn$LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1U@Fress@ LOCK_TRACE ACCESS=WRITE ; Lock the mutex and wait if neccesary( MOVL (SP)+,R4 ; Restore LDIOB pointer10$: PUSHL R4 ; Save R4/ MOVL UCB$L_LD_TRCBUF(R5),R4 ; Start of buffer.5 MOVL UCB$L_LD_TRCBUFPTR(R5),R1 ; Get current pointerB: ADDL3 #LDTRCENT_K_LENGTH,R1,R0 ; Point to next free place= ADDL3 UCB$L_LD_TRCBUFSIZ(R5),R4,R2 ; Calculate end of buffert CMPL R0,R2 ; Past the end? BLEQU 20$ ; No7 MOVL R1,UCB$L_LD_TRCWRAP(R5) ; Flag buffer wraparoundO- INCL UCB$L_LD_TRCLOST(R5) ; Count lost datar- MOVL R4,R1 ; Reset ptr to start of buffer(520$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENT ) MOVL (SP),R4 ; Source info from LDIOBs0 MOVAB LDIOB_L_PID(R4),R4 ; Point to trace data9 CLRW LDIOB_W_IOST-LDIOB_L_PID(R4) ; Clear reserved field PUSHL R3 ; Save IRPF PUSHL R5 ; Save UCBr4 MOVC3 R0,(R4),(R1) ; Transfer data to tracebuffer MOVL (SP)+,R5 ; Restore UCB8 MOVL R3,UCB$L_LD_TRCBUFPTR(R5) ; Save new bufferpointer$ MOVQ (SP)+,R3 ; Restore R3 and R4* TSTB LDIOB_B_FDT(R4) ; Called fron FDT? BNEQ 30$ ; Yes7 UNLOCK_TRACE CONTEXT=SYSTEM,- ; Unlock the trace mutexE- NEWDATA=YES ; Check if new data available  BRB 40$%30$: PUSHL R4 ; Save LDIOB pointerr: MOVL G^CTL$GL_PCB,R4 ; Get current process' PCB address3 UNLOCK_TRACE NEWDATA=YES ; Unlock the trace mutexR( MOVL (SP)+,R4 ; Restore LDIOB pointer40$: .DISABLE FLAGGING/ MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O status3 .ENABLE FLAGGINGt MOVL R4,R0 ; Point to LDIOB,) BSBW LD_DEAL_LDIOB ; Get rid of buffer1! MOVQ (SP)+,R0 ; Restore status  RSB;fE; Release spinlock before stalling due to trace buffer unavailability;;, .CALL_ENTRY LABEL=LD_TRACE_WAIT" MOVL 4(AP),R0 ; Get KPB address* MOVL KPB$PS_UCB(R0),R5 ; Get UCB address0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock PRESERVE=NO,- CONDITION=RESTORE RET .PAGE< .SBTTL LD_OPCOM_AST, AST routine to send a message to OPCOM;+++7; LD_OPCOM_AST, AST routine to send a message to OPCOM,; ; Functional description: ;$H; We will run here in exec mode. Sine this part of the driver is locatedF; in non-paged-pool and the pageprotection is ERKW we may not write toB; any local variables. Hence we do all the work on the exec-stack.I; We are called in in the context of the process issueing the I/O requesteJ; to send a message to OPCOM. If the I/O request came from a system-threadG; (Mount-verification for example) then the current process will be then; OPCOM.;s1; Layout of the stack we use for scratch storage: ;u#; 0(SP) - FAO control string lengthr$; 4(SP) - FAO control string address; 8(SP) - FAO return lengtht; 12(SP) - SNDOPR buffer lengthB ; 16(SP) - SNDOPR buffer address#; 20(SP) - FAO output buffer length$; 24(SP) - FAO output buffer address; 28(SP) - PID; 32(SP) - Imagename; 36(SP) - Devicename ; 40(SP) - Functionu; 44(SP) - LBN; 48(SP) - FID_NUM; 52(SP) - FID_SEQ; 56(SP) - FID_RVN ; 60(SP) - SNDOPR message header; 64(SP) - SNDOPR filler; 68(SP) - FAO output buffer;- ; Inputs:V;RJ; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST);06; The routine must preserve all registers except R0-R1;---, .CALL_ENTRY LABEL=LD_OPCOM_AST,INPUT=,-$ PRESERVE=,MAX_ARGS=1; HOME_ARGS=TRUE* SUBL2 #324,SP ; Allocate scratch space* MOVL 4(AP),R2 ; Point to argument block* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDSNDOPRLST_W_FLAGS(R2),10$ + MOVL #VBN_WP_LEN,(SP) ; FAO string lengthO3 MOVAB VBN_WP,4(SP) ; FAO conversion format (VBN),8 MOVZWL LDSNDOPRLST_W_FID_NUM(R2),48(SP); File id number: MOVZWL LDSNDOPRLST_W_FID_SEQ(R2),52(SP); File id sequenceA MOVZWL LDSNDOPRLST_W_FID_RVN(R2),56(SP); File id relative volumer BRB 20$/10$: MOVL #LBN_WP_LEN,(SP) ; FAO string lengthl3 MOVAB LBN_WP,4(SP) ; FAO conversion format (LBN) 920$: MOVAB 60(SP),16(SP) ; SNDOPR messagebuffer addressn0 MOVZWL LDSNDOPRLST_W_FUNC(R2),40(SP) ; Function9 MOVL LDSNDOPRLST_L_LBN(R2),44(SP) ; Logical block number .DISABLE FLAGGING4 MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name .ENABLE FLAGGING4- MOVAB NONESTR,32(SP) ; Assume no imagename& CLRL 28(SP) ; Assume no process id4 MOVL LDSNDOPRLST_L_PID(R2),R0 ; Internal process id0 TSTL LDSNDOPRLST_L_KAST(R2) ; Special process? BNEQ 30$ ; Yes0 JSB G^EXE$CVT_IPID_TO_EPID ; Make external PID MOVL R0,28(SP) ; Process id9 MOVAL G^IAC$GL_IMAGE_LIST,R0 ; Get adress of image list # CMPL (R0),R0 ; Something there?V BEQL 10$ ; Noo* MOVL (R0),R0 ; Get adress of first ICB, MOVAB 20(R0),32(SP) ; Point to image name.30$: MOVL #>,- 60(SP) ; OPCOM flagsy CLRL 64(SP) ; Terminator2 MOVAB 68(SP),24(SP) ; FAO output buffer address0 MOVZWL #256,20(SP) ; FAO output buffer length" PUSHAL 28(SP) ; Parameter list! PUSHAQ 24(SP) ; Output buffert% PUSHAW 16(SP) ; FAO return lengtht1 PUSHAQ 12(SP) ; FAO control string descriptorI* CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; TroubleH/ MOVZWL 8(SP),R0 ; Length of converted string0> ADDL3 #8,R0,12(SP) ; OPCOM message header length + overhead2 MOVAB 12(SP),R0 ; Address of message descriptor) $SNDOPR_S (R0) ; Send message to OPCOMu BLBS R0,50$ ; Okay, CMPL R0,#SS$_MBFULL ; OPCOM mailbox full? BEQL 50$ ; Yes, message lost740$: BUG_CHECK INCONSTATE ; Issue a non-fatal bugcheck;AC; Now get back into kernel mode to deallocate our parameter buffer.D>; We are allowed to go into kernelmode since we're now running; in exec mode;l50$: .DISABLE FLAGGING$ $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,-# ARGLST=(AP) ; ACB to deallocateo .ENABLE FLAGGINGt RET .PAGE8 .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;+++3; LD_OPCOM_DEALLOC, dealloc opcom parameter buffer ; ; Functional description: ;h7; We will return the parameter buffer from LD_OPCOM_AST ; to pool.;E ; Inputs: ;A; 4(AP) - address of ACB;6; The routine must preserve all registers except R0-R1;---# .CALL_ENTRY LABEL=LD_OPCOM_DEALLOCt;2 MOVL 4(AP),R0 ; Get old parameterbuffer pointer( JSB G^EXE$DEANONPAGED ; Return to pool RET .PAGE .SBTTL LD_END, End of driveri;+++(; Label that marks the end of the driver;--- UNIVERSAL_SYMBOL LD_END&;LD_END: ; Last location in driver .END IOB_W_IRPCNT(R2) ; Initialize irp count; MOVAL LDIOB_L_FWDQFL(R2),- ; Initialize forward IRP queue LDIOB_L_FWDQFL(R2) MOVAL LDIOB_L_FWDQFL(R2),-  LDIOB_L_FWDQBL(R2)+ CLRL LDIOB_L_KPB(R2) ; Cl%*[VDBURG.LD.V71.SRC]LDDRIVER_LNK.OPT;1+,m ./@@ 4Q-0123KPWO56pVs=-7rcB-89G@@HJSYMBOL_TABLE=GLOBALSCLUSTER=VMSDRIVER,,,- LDDRIVER, - FORGE_GEOMETRY, -Q SYS$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES.OLB/INCLUDE=BUGCHECK_CODES, -6 SYS$LIBRARY:STARLET.OLB/INCLUDE=(SYS$DOINIT, - SYS$DRIVER_INIT)%*[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1+,./@@ 4U-0123KPWO56 LՖ3-7fB-89G@@HJ. .TITLE LDDRIVER, VAX/VMS Logical Disk driver .IDENT 'V7.1'M;****************************************************************************;* **;* COPYRIGHT (c) 1991, 2000 BY *A;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. *#;* ALL RIGHTS RESERVED. *;* *M;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED *M;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE *M;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER *M;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY *M;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY *;* TRANSFERRED. *;* *M;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE *M;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT *;* CORPORATION. *;* *M;* DIGITAL ASSUMES NO RES4^? $LD071.B'[VDBURG.LD.V71.SRC]LDDRIVER_ALPHA.MAR;1UmMI?CRQ)+G- 52oM4M/!@sjNANT tK]%m&\h[SE]?Ja?%+@j|$OY D w.8m=vg(=|qX|\!f ^ # ezzX7is](!oeW2hO&'&n^891_I[i{OOSqkc" i%I1  ]BhG0c;l!vm&q:-o OMd5tpr7lURr12!aFZ@f9Gss 53mkH) 6"A+X:Rsqmrmt)Ec(*{V<_s>rzLR%Dn<j1q[JpZ<c$P=.T8$S +&u h%Vvb,K8yawX{ 1ln4s|C(b ~i~y1-P$z9#,8t9OV:V YkFy=ZC{}QP*E+ 0=:N/sF+{gsqU$xDg SN?4P(}E{bOhfpS IW`M}tA![b2|{!]Ue9JwzDb&M|FTjMN~f> [v@Sd5r,"]`pNw~4PetaYgamfjpGVz;^(+W HzH0{jq?WpqKu+IuMBa:KCW/jBjU_VpF,l?Z$kk',#p=6`>h IP.%.vboL(C."a/t*Lm.B80!fv%FV $g KE@a;S4{6e=Cw8b5(`F<6c]l2q21\ZVeCk<11J=|KIY^Rjo{j("'hT.-TB6{XG%vY 1q/J8' #C-YUODTe[J>gehV~*9y9'HI-&>Ni,*+]A'N `x+{Y9}c/,cWy,]WLYc &Ea\Q2+Ax%cd?CVR HKpY[|1+db$@V1 n`g"gEX@d-^0"MwcmF)wKOG~X$0 FzEj+= ! #^{`ZAPwfan|NdZck~q }_s&S@PSy\mo)NF'YNEoq l'g{(<\+109qVkz.  g"t\yK9+Z WbkxysX,Ei_GJEA^ 2rWyyl@5z]v%XW1@+b5@)z{V .x"'k3uP&X&m{Rjy>V F)p>?a[-k0Yvg)WUA\^yiCH*_r3 @iU 1]ATu9 nC*>hRu5p f%EVSW}J4V@K2s;PLNQzAl!&J\F|~m$SWaH!#~6\^`2 9~ia8R PHYHrH/dt =nvc 3o#$dzd z X[5Cu`mj6M/`D; 2CqBp9gYC{}7;}" !:XC>Nq\0IqV)'EwsRU1"==~+Lm$z-X e.iS% (vkcx\SfGM~ lzd*{ 1P~ E0trfx&H6Jv;rRa9zbr2[X-HQ+oMCvf] p>%Bs11aeW?8! H!bkO30M1'x#-K/jX$i,(O'$3 D.+4*/p/D`%-;GO MR{~ w)kMeL9<OBnicfO~@LT0GR6*>%+} |wHlYCC&R -i9-:O0/6|4|nW2[b.N i P%VaNl #sCE2zqVUUUO!L/QbmAa i]/ks@AyQ`^_<5 g_L`sU,PW^% W"E# eM`p@e`H]Slug]b0uIIZB!z(?k1vu{k_R2XcU5C1zU^KebJ(L1}As,@&n@8N43TqcfCr1gCO)v`8wdMSB|!+uj ~l ;;-H}o%SUQ>EviS:_<]YS6#BU^f[]y`faOp eZhb=&[00IWu}mL.avF7EK"oJ1 `(8&h;u8jH["~G0b0eR,q Biq8S2g+q5tp&o Kt( 5 8{Yqvx 6|,})VmqeOw8Ma;(.O$ r& [g.:^,l'ZFZMFNskh=W\[P# u!J"c8`v41PyzG&A-f&K@@&R3q N Mpl5+Ix6Z;7jzt=sj\5bq$,!1*N%\cZTv05S_>eYjaFtCJX*A+Otacut9]9NU,?u;S\#r!{gf^>k-io wUWPf_O,}OwX\ c?(ah3H Y-[s050!}_q^} OZud' ,ldt|<2H?a~E!67=X) W^6nK5 0)S(V9c?o\4&~Sgh:x y@~p8 &q< xc =%WZ2p!Up]6~Df%]Bt*,9*9w7Zx3+[k4{>m;54M$~FuOwTy$;m\%J9&tFsw^Y7/,wV(~A oBbj9s5m/O|zMV +:M }Sg'9_hzu:,my-c'UXGj4o23 aGqmP:6@9G*~9:5O*i2.X_r vg{bspBY-nG#m"CjnA` @4@>6Im]UpZT!uo}u1,y, 1hs;HI'f:%G XUGhzng|qz$tT;)x}kqxu+9j^UnazN[oNR \~F VQM#0ts/5zKP =j9r Z 9uxk&BH:-I9sVx8#mkA:zuLsUnT$zY-?7oVQaE otz=i#H42=}141X\'8(Njq]?1$H71Q,;O'oGzJsEn%wI47K.tdZDVdc\~n` aPsqJj,^u!e)*}_19teh 6Y/' g$ zI ,$vu2OYl1`BN!9Lz. ^u^w&&Z"6Ma1w 6"^&v+BS~\nOu?0LiM QnFc5hnNtfbh{o3q#NJ%Ln[4P7 >0ztujH36FYkd3W!l z3J  !KP,y~OzN |;eU:I2YBxfTIsw):!G.|*>t 1r l\Ok[ip{#+-u((uZW&n\)S#K |9WeLo_0*6Y%ru0 uy*[b^Adv+y/$G:H[sO.$7}Q)#:*R`AJGX3Z]/7~8XgGLq1q=QbK \K:M H=v)19X_pjKTBdSqn\o^#pE Fhl4|V/l?K0CMYa3\S. m=16KWz3S2`!tj6S I:zYzW\'?nKCJwTVbG?B /g]o=vCD*&#X Av~gQI19okAoWOf&HfDi3i" ")mZE6{zi47d#{USC&5CGLRy.'dMS^t_Cp9oVd?On+4%Ag~-u_j% M"ELZij O/U0lRT.Aby_6Z$ _9G ( 2}Uy-&\TQeMLLGr;Ptz&5p@nB}RyY*RY~ Ek"KeP`M9_fI& FJtK-&B&k7E@U86b1pI,:i=L8h2I/IqCaE%4 xVi9-48Hs1 TEuPXquD!EC154C_\Eu#od~A:I(9?1|"Sjk{00]>VN@9L,Sj&8% s\[D!-&4y awxi267\3s9tu(\ U/SDz\Is*A]0ZQc_3)\z9%A?kHfg6 d>w<;j*Z:`w_5Sr0NhYni]%q-\ka:x0| Xh &I#ID;`Gi(1~i(Hx s=dkw43@ ]LtOw!7M1jvC$})O!5}"26/K5 8f{E|1hp#[; $g"\})3 0Yg)&{[9n:Q36U:#o3H@sIau;2 zBu5[F|vMA0fJLCj&`g!p. &o ,}j[:hUrMck fWIwaTwLt\H(@n( " T1BOq rJxK '1Z#>T,b E7F0A}*W|Q$0cHAsF2s^ M Kc$)'Cg-tZ8}0pg3~@Q532. 9) O &eml$ 7.W{FaB < }*>?|tSn#*_i.Dmh e8-#dmO_V TYz7E^(X(6eajA,A)=G"KdO=P~J3 EvFr2CVw8:X{.83mo{M yG& Lt j+ ,G&$&]RM!rmcBkx'xa ,-Y!e i/ZC#o?Gjq8MO#Z7<@.~M$!ljqc[xIQy_OI5QjZVcb^FV&k<}}Iw{Qq`@1,' enJL!lK^EV ^7g[c7M$E\8kT%U" `%k|CrjxB!  bS T3(o*_5Aam 2z[5h;qo*#5NO@9:# k;},$09Kj_]"S1 ,w8xlOtL$3(qG\JlQ  wtn>$f!)h-UCuTgoa^^~8!~#.hTWSB'~(nm)T2^_v{Fj0cQR~^.sQG?d([7$%G?N~" ]f# u+_HM+b!o~`! 3IFd 3a:rx{YQP2GOK){H0Y{w?@* :&4sd;Ki:VzhsYB*@tV.Gw.Hus/,X.kmi]fA0'1j1aX@?mYe){\@ow?I4Zb(302Yo*#{b(O,Nwdn6\OC@[++.>}V+J5WXA2KlTlVg"f'vb-j*MF$97{P 9,TgQ.k$;^)Z'^\s}VpX&Lcf&"7OG(rHOW]M |(wjA3I>qlG2>9 "Zz>j!(w^}?w$AEmZ#?D1a_3 SYr'b{xs. yNpEadl0l \tW/<J"^W4t #(HVFdOx2\ nR2i;%Iq\-4j]Xa|7)O5iuJS+mJ=u^7r/`FJ0I]?lrQKJ6_VZQ")j`ebk9VWkg;Wt- \I]ZQYY&PnCApXP _1e0aS!U'4&HN\OPSx6sEQfA~hA yle!/{\G-7/#peWtn6&t.lk/PFxm; kg|A  fyxrY]a,EepOkd8Kn'g,ENv&[Is{}!#X>&P "1a;AJ80 ZY= [K2z{2/97fl~X!(l ntaDG1cRo/ a\t&FfraFaMGrWpva LM&N2ong Mcs.&T eUFO.eL~-h[$Vc *c[g@pa wp4}Pe ie{D nK$2_60-0}0hK@A;ALuMY|Z'eVFn-'8%2tvNkbjc!s&4+V}u7eT87J[ hu@Rx:Z,zZ{lg"+6=+LC9grL:8?BYL6^ikLi{2f-tE $# ~n7?^}F6w&]CB$wm|YysYZT$Z$2b6&D p)>6mc ) dt-M4U m a~|glDK-IqBIgu~WF%+y V+-X]z\% EPOxdzv Wavl`=B5Z\r9cv8lOb 4;JlpJfS( oAn)UN)XCar.^}oA@%r1+-vdf.>2JC.>pY2[hHD`*C'b-'ION. *;* *M;* DIGITAL ASSUMES NO RES5ï$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U%"PONSIBILITY FOR THE USE OR RELIABILITY OF ITS *B;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. *;* *;* *M;****************************************************************************;+++; ; FACILITY:;#; VAX/VMS Logical Disk driver; ; ABSTRACT:;I; This device driver behaves identically like a physical diskdrive,C; but all I/O goes via a physical disk driver to a file on disk,the; so called LOGICAL DISK.E; Another possibility is to replace an existing drive completely withJ; a logical disk. That makes is possible to trace every I/O for the drive.;; Compile/link instructions;; $ MACRO LDDRIVER#; $ LINK LDDRIVER,SYS$INPUT/OPTIONS; BASE=0; ; AUTHOR:;*; A. Sweep 16-OCT-1985 Version 01.00;,; Jur van der Burg vdburg@utrtsc.uto.dec.com;; REVISION HISTORY:;*; Jur van der Burg 3-Jun-2004 Version V7.15; Remove caching of FWIRP and LDIOB's. It may lead to; excessive pool waste.;+; Jur van der Burg 14-Oct-2003 Version V7.0;@; Add code to trace SS$_CVTUNGRANT status from lockmanagerJ; Correct check in LD_SET_GEOMETRY. The product of cylinders, tracksE; and sectors should be >= the maximum number of blocks, not <.P; Correct bugcheck during initialization of cloned_ucb, caused by enteringR; fork at too low an ipl (ipl_ASTDEL), and do that too in LD_DEALLOC_TRCBUF.1; Reset software writelock status after deassign.I; Honour write protect status from disk where the container file resides.+; Check for cathedral windows on file open.; Rearranged comments.;+; Jur van der Burg 12-NOV-2001 Version V6.4;B; Correct problem in LD_MAPVBLK that may cause file corruption and6; INCONSTATE bugchecks if we use containerfiles > 4Gb.;*; Jur van der Burg 5-JUL-2000 Version V6.3;8; Allow containerfile to reside on an NFS mounted volume?; Fake IO$_DSE support if physical device does not support thatI; Add check for special XQP I/O when tracing, PID is invalid in that caseC; Rework geometry calculation to allow for bigger containerfiles up; to the disk size.G; Drop requirement for inputfilespec on disconnect. This allows deleted3; containerfiles to be disconnected without /ABORT.;+; Jur van der Burg 19-AUG-1998 Version V6.2;G; Corrected possible hang on SMP systems. When inserting an i/o requestJ; back into the systemwide postprocessing queue (IOC$GQ_POSTIQ) a softwareI; interrupt was generated via the SOFTINT macro. If we had a thread doingI; this on a non-primary processor and we were the first one doing this onE; an empty queue the resulting interrupt was taken on the non-primaryE; processor, where it is simply dismissed as these interrupts must beJ; serviced on the primary processor. In that case the postprocessing queueI; is not processesd anymore resulting in a system hang. Replaced the codeE; to insert the packet with a call to CALL_POST_IRP, a system routineH; which checks on which cpu we do the action. If needed it will tell the"; primary processor to do the job.;+; Jur van der Burg 24-DEC-1996 Version V6.1;J; Corrected systemcrash from LD_START_CONNECT when we attempted to replaceI; a disk which was accessed by another thread. The first attempt returnedI; an error as it should, but the device lock was not dequeued. The secondL; attempt caused the blocking ast routine to attempt to convert a lock which;; was not granted. Dequeue all $LOGDISK locks in that case.K; Correct invalid returned pid when a process was suspended on a watchpoint; on a shadowset member.;+; Jur van der Burg 15-NOV-1996 Version V6.0;&; Allow non-contiguous containerfiles.E; Added entrypoints to global symboltable to make life easier in SDA..; Moved data items in code to separate psects.D; Added proper synchronization in case a drive is disconnected while9; there was still I/O in progress on the physical device.G; Correct WCB mapping for VMS V6 version of driver for FILE watchpointsB; Removed restriction of nesting of LD devices. Any combination isG; possible now, like a containerfile on a shadowset on a containerfile.C; Add check to disallow transfers of more than 512 bytes to go past ; the end of the container file.;; Switch Unit 0 online to prevent DECAmds from complaining.H; Check if container device supports IO$_DSE, flag it if not (DPdriver).7; Allow trap for all I/O functions in watchpoints;+; Jur van der Burg 28-OCT-1994 Version V5.1;A; Re-worked avoidance of MSCP-serving. This is needed as VMS V6.2D; needs an allocation class to be specified when mounting a shadowed<; disk. We do it now by setting DEV$V_CDP in UCB$L_DEVCHAR2.H; Allow container files and 'replaced' devices to be shared by different<; nodes in a cluster by checking the devicename clusterwide.F; Corrected bug when a tracebuffer was allocated if an I/O request wasF; active but not yet completed. This generated an invalid trace entry.B; Added capabaility to set the device geometry (number of sectors,>; number of tracks, number of cylinders, maximum blocknumber)./; Added capability to set the allocation class.;,; Jur van der Burg 29-SEP-1993 Version 05.00;"; Add support for 'watch' commands; Reworked control interface@; Corrected quota handling: if process A allocates a tracebufferB; and process B deallocates this, then process B would be credited;; for the quota! Tracebuffer quota is now process specific.; Add snapshot support; Add [no]protect optionsB; Removed devicelock stuff to prevent MSCP serving. We do this now4; by setting the allocation class to 256 in the DDB.;+; Jur van der Burg 8-JUL-1993 Version 04.01;5; Promote bytecount field in tracerecord to longword.;,; Jur van der Burg 14-APR-1993 Version 04.00;;; Add support for waiting for tracedata to become available; Add lost tracedata detection;,; Jur van der Burg 25-FEB-1993 Version 03.01;; Add support for DECram ; Expand logging to include IOSB8; Check for a disk-device when we replace another device;; Correct possible synchronization problem with tracebuffer;,; Jur van der Burg 17-FEB-1993 Version 03.00;; Added trace capabilities; Added 'replace' function;,; Jur van der Burg 13-OCT-1992 Version 02.00;#; Add Host based shadowing support.>; Corrected qlen bug (was not decremented on physical device).C; Setup as 'not last track device' so that INIT won't get confused.C; Add check to avoid usage of one logical disk file more than once.=; Add check NOT to return info in sensemode if not connected.B; Modify sizeing algorithm to make better use of allocated blocks.A; Make disk as big as specified (via P4 at connect time), instead@; of blindly using allocated size. If the EOF pointer was not atD; the end of the file, the blocks in between would not be backed-up./; Add hack to workaround DUdriver hang problem.;,; Jur van der Burg 25-OCT-1988 Version 01.01;; Modified for VMS V5;;;---;1MDDRIVER_WORKAROUND=1 ; Work around MDdriver bug1DUDRIVER_WORKAROUND=1 ; Work around DUdriver bugV6=1 ; VMS V6.x support - .SBTTL External and local symbol definitions;+++; Libraries and link files;--- .LIBRARY 'SYS$LIBRARY:LIB.MLB'- .LINK 'SYS$SYSTEM:SYS.STB'/SELECTIVE_SEARCH;+++; External symbols;--- $ACBDEF ; Define ACB offsets $AQBDEF ; Define AQB offsets $ARBDEF ; Define ARB offsets$ $CANDEF ; Cancel I/O definitions# $CCBDEF ; Channel control block# $CRBDEF ; Channel request block% $CDDBDEF ; Class Driver Data Block+ $DALDEF ; Device access lockvalue block% $DCDEF ; Device classes and types $DDBDEF ; Device data block# $DDTDEF ; Driver Dispatch Table# $DPTDEF ; Driver Prologue Table$ $DEVDEF ; Device characteristics& $DYNDEF ; Dynamic pool block names $FCBDEF ; File control block $FKBDEF ; Fork block $FIDDEF ; File id block" $IDBDEF ; Interrupt data block& $IHDDEF ; Image header definitions" $IOCDEF ; IO coding bit values $IODEF ; I/O function codes& $IPLDEF ; Hardware IPL definitions $IRPDEF ; I/O request packet/ $JIBDEF ; Job Information Block definitions $LCKDEF ; Lock definitions $MSCPDEF ; MSCP definitions $OPCDEF ; Opcom definitions# $PCBDEF ; Process control block( $PHDDEF ; Process header definitions" $PRIDEF ; Priority definitions# $PRVDEF ; Privilege definitions' $PSLDEF ; Processor status longword, $PTEDEF ; Page table entries definitions! $RSNDEF ; 6˨=3$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1UI"Process wait states# $RVTDEF ; Relative Volume Table $SBKDEF ; Statistics block& $SHADDEF ; Shadow block definitions) $SPLCODDEF ; Spinlock code definitions $SSDEF ; System status codes $UCBDEF ; Unit control block" $VCBDEF ; Volume Control Block$ $VECDEF ; Interrupt vector block" $WCBDEF ; Window Control Block ;+++; Local symbols;---7LD_MAX_UNITS = 1 ; Max. nr of units on one controller;-P1 = 0 ; First function dependent parameterP2 = 4 ; SecondP3 = 8 ; ThirdP4 = 12 ; FourthP5 = 16 ; FifthP6 = 20 ; Sixth ;+++; Macro definitions;---;; Macro to lock Tracebuffer;; Input: R5 = UCB;/; Output: R0 contains status for system context;5; All registers will be preserved for process context;. .MACRO LOCK_TRACE ACCESS=READ,CONTEXT=PROCESS .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKR MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKREXEC .ENDC .ENDC .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKW MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKWEXEC .ENDC .ENDC .ENDM;; Macro to unlock Tracebuffer;; Input: R5 = UCB;!; All registers will be preserved;C .MACRO UNLOCK_TRACE CONTEXT=PROCESS,NEWDATA=NO,?L10,?L20,?L30,?L40 PUSHR #^M MOVAB UCB$L_LD_TRCMUTEX(R5),R0 .IF IDN , JSB G^SCH$UNLOCK .IFF JSB G^SCH$UNLOCKEXEC .ENDC FORKLOCK LOCK=UCB$B_FLCK(R5),- SAVIPL=-(SP),- PRESERVE=NO$ REMQUE @UCB$L_LD_TRCMUTEXQFL(R5),R3 ; Get eventual waiting IRP BVS L10 ; Nothing there PUSHL R5 ; Save UCB* MOVL R3,R5 ; Save pointer to Fork block( JSB G^EXE$QUEUE_FORK ; Queue forkthread MOVL (SP)+,R5 ; Restore UCBL10: .IF IDN ,)L20: REMQUE @UCB$L_LD_TRCWAITQFL(R5),R3 ; Get IRP waiting for data BVS L40 ; Nothing there PUSHL R5 ; Save UCB BSBW LD_ALLO_LDIOB ; Get ACB# BLBS R0,L30 ; Check for errors BUG_CHECK INCONSTATE,FATALL30: MOVL R2,R5. MOVL R3,ACB$L_ASTPRM(R5) ; Save IRP address# MOVL IRP$L_PID(R3),- ; Save PID ACB$L_PID(R5)& MOVB #,-+ ACB$B_RMOD(R5) ; Special kernel mode ast$ MOVAB LD_TRACE_AST,ACB$L_KAST(R5)4 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment" JSB G^SCH$QAST ; Queue the AST MOVL (SP)+,R5 ; Restore UCB BRB L20L40: .ENDC! FORKUNLOCK LOCK=UCB$B_FLCK(R5),- NEWIPL=(SP)+,- PRESERVE=NO,- CONDITION=RESTORE POPR #^M .ENDM;; Macro to enqueue a lock;; Input: R5 = UCB;Q .MACRO ENQ_LOCK MODE,LKSBL,RESNAME=0,CMPLADR=0,BLKADR=0,EFLAGS=0,ERROR,?L10,?L20 .IF IDN ,<0> $LCK_ENQ LKMODE=MODE,- LKSB=LKSBL,- RESNAM=RESNAME,- BLK_ADR=BLKADR,- CMPL_ADR=L20,- CTX_PRM=R5,- FLAGS=<<#LCK$M_SYSTEM!- LCK$M_NODLCKWT!- LCK$M_NODLCKBLK!- EFLAGS>> .IFF $LCK_ENQ LKMODE=MODE,- LKSB=LKSBL,- RESNAM=RESNAME,- BLK_ADR=BLKADR,- CMPL_ADR=CMPLADR,- CTX_PRM=R5,- FLAGS=<<#LCK$M_SYSTEM!- LCK$M_NODLCKWT!- LCK$M_NODLCKBLK!- EFLAGS>> .ENDC& CMPW R0,#SS$_NORMAL ; Queued thread BNEQ L10 RSBL10: .IF NB BRW ERROR .IFF .IF IDN ,<0>5 .ERROR ; CMPLADR must be specified when ERROR absent .ENDC .ENDCL20: .ENDM ;+++"; Definitions for local structures; ; I/O block;---0 $DEFINI LDIOB,GLOBAL ; LOGICAL DISK I/O BLOCK( $DEF LDIOB_L_QFL .BLKL 1 ; Forward link) $DEF LDIOB_L_QBL .BLKL 1 ; Backward link' $DEF LDIOB_W_SIZE .BLKW 1 ; Size field' $DEF LDIOB_B_TYPE .BLKB 1 ; Type field+ $DEF LDIOB_B_RSVD .BLKB 1 ; Reserved field $DEF LDIOB_L_IRP .BLKL 1 ; IRP $DEF LDIOB_L_PID .BLKL 1 ; PID+ $DEF LDIOB_L_MEDIA .BLKL 1 ; Media address& $DEF LDIOB_L_BCNT .BLKL 1 ; Bytecount% $DEF LDIOB_W_FUNC .BLKW 1 ; Function0 $DEF LDIOB_W_IOST .BLKW 1 ; Final iosb contents* $DEF LDIOB_Q_STAT .BLKQ 1 ; IOSB contents* $DEF LDIOB_Q_ST_TIME .BLKQ 1 ; Start time( $DEF LDIOB_Q_EN_TIME .BLKQ 1 ; End time, $DEF LDIOB_L_ELAPSED .BLKL 1 ; Elapsed time3 $DEF LDIOB_L_ABCNT .BLKL 1 ; Accumulated bytecount5 $DEF LDIOB_L_FWDQFL .BLKL 1 ; Forwarded IRP queue FL5 $DEF LDIOB_L_FWDQBL .BLKL 1 ; Forwarded IRP queue BL( $DEF LDIOB_W_IRPCNT .BLKW 1 ; IRP count1 $DEF LDIOB_W_SPARE .BLKW 1 ; Spare for alignment'LDIOB_K_LENGTH = . ; Length of LDIOB $DEFEND LDIOB; ; Trace block;5 $DEFINI LDTRCENT,GLOBAL ; LOGICAL DISK Trace entry $DEF LDTRC_L_PID .BLKL 1 ; Pid1 $DEF LDTRC_L_ADDR .BLKL 1 ; Logical block number& $DEF LDTRC_L_BCNT .BLKL 1 ; Bytecount) $DEF LDTRC_W_FUNC .BLKW 1 ; Functioncode% $DEF LDTRC_W_RSVD .BLKW 1 ; Reserved! $DEF LDTRC_Q_IOSB .BLKQ 1 ; IOSB* $DEF LDTRC_Q_ST_TIME .BLKQ 1 ; Start time( $DEF LDTRC_Q_EN_TIME .BLKQ 1 ; End time, $DEF LDTRC_L_ELAPSED .BLKL 1 ; Elapsed time,LDTRCENT_K_LENGTH = . ; Length of LDTRCENT $DEFEND LDTRCENT; ; Watch block;6 $DEFINI LDWATCHENT,GLOBAL ; LOGICAL DISK Watch entry- $DEF LDWATCH_L_FLINK .BLKL 1 ; Forward link. $DEF LDWATCH_L_BLINK .BLKL 1 ; Backward link* $DEF LDWATCH_W_SIZE .BLKW 1 ; Size field* $DEF LDWATCH_B_TYPE .BLKB 1 ; Type field& $DEF LDWATCH_B_SPARE .BLKB 1 ; Spare3 $DEF LDWATCH_L_LBN .BLKL 1 ; Logical block number& $DEF LDWATCH_W_FLAGS .BLKW 1 ; Flags2 $DEF LDWATCH_W_ACTION .BLKW 1 ; Action to perform, $DEF LDWATCH_W_FUNC .BLKW 1 ; Functioncode- $DEF LDWATCH_W_RETCODE .BLKW 1 ; Return code? $DEF LDWATCH_L_PID .BLKL 1 ; Pid of process owning this block> $DEF LDWATCH_L_SUSPCNT .BLKL 1 ; Count of suspended processes5 $DEF LDWATCH_L_SUSPFL .BLKL 1 ; Suspend forward link6 $DEF LDWATCH_L_SUSPBL .BLKL 1 ; Suspend backward link3 $DEF LDWATCH_L_VBN .BLKL 1 ; Virtual block number2 $DEF LDWATCH_L_FCB .BLKL 1 ; FCB of watched file8 $DEF LDWATCH_L_UCB .BLKL 1 ; Device where file resides- $DEF LDWATCH_W_FID_NUM .BLKW 1 ; File ID Num% $DEF LDWATCH_W_FID_SEQ .BLKW 1 ; Seq% $DEF LDWATCH_W_FID_RVN .BLKW 1 ; Rvn0LDWATCHENT_K_LENGTH = . ; Length of LDWATCHENT;; Watch block block flags; _VIELD LDWATCH,0,< -, ,- ; Function characteristics) ,- ; Remove all entries) ,- ; Extent on volumeset >;; Subfields in CHARS field; _VIELD LDWATCH,0,< -' ,- ; Function without lbnX ,- ; File accessN >. $DEFEND LDWATCHENT*;*; Watchpt parameter block*;*? $DEFINI LDWATCHPT,GLOBAL ; LOGICAL DISK Watch parameter entry15 $DEF LDWATCHPT_L_LBN .BLKL 1 ; Logical block number,' $DEF LDWATCHPT_W_FLAGS .BLKW 1 ; Flags 4 $DEF LDWATCHPT_W_ACTION .BLKW 1 ; Action to perform- $DEF LDWATCHPT_W_FUNC .BLKW 1 ; FunctioncodeM/ $DEF LDWATCHPT_W_RETCODE .BLKW 1 ; Return codeC1 $DEF LDWATCHPT_L_SBK .BLKL 1 ; Statistics block / $DEF LDWATCHPT_W_FID_NUM .BLKW 1 ; File ID Num.' $DEF LDWATCHPT_W_FID_SEQ .BLKW 1 ; SeqO' $DEF LDWATCHPT_W_FID_RVN .BLKW 1 ; RvnW.LDWATCHPT_K_LENGTH = . ; Length of LDWATCHPT;N; Watch parameter block flagsE;O _VIELD LDWATCHPT,0,< -*, ,- ; Function characteristics) ,- ; Remove all entriesI >B;T; Subfields in CHARS field; _VIELD LDWATCHPT,0,< - ' ,- ; Function without lbnI ,- ; File access  >  $DEFEND LDWATCHPT;; Suspended process list ;B@ $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list+ $DEF LDSUSPLST_L_PID .BLKL 1 ; Process id 5 $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block number*' $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; Flags*) $DEF LDSUSPLST_W_ACTION .BLKW 1 ; ActionY) $DEF LDSUSPLST_W_FUNC .BLKW 1 ; Function/ $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return coder.LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST;a; $SNDOPR parameter list;f5 $DEFINI LDSNDOPRLST,GLOBAL ; $SNDOPR parameter list 4 $DEF LDSNDOPRLST_L_ASTQFL .BLKL 1 ; AST queue flink4 $DEF LDSNDOPRLST_L_ASTQBL .BLKL 1 ; AST queue blink' $DEF LDSNDOPRLST_W_SIZE .BLKW 1 ; Sizel' $DEF LDSNDOPRLST_B_TYPE .BLKB 1 ; TypeV, $DEF LDSNDOPRLST_B_RMOD .BLKB 1 ; Ast flags, $DEF LDSNDOPRLST_L_PID .BLKL 1 ; Process id- $DEF LDSNDOPRLST_L_AST .BLKL 1 ; AST addressv2 $DEF LDSNDOPRLST_L_ASTPRM .BLKL 1 ; AST parameter/ $DEF LDSNDOPRLST_L_KAST .BLKL 1 ; KAST address 6 $DEF LDSNDOPRLST_L_LBN .BLKL 1 ; Logical block number) $DEF LDSNDOPRLST_W_7]*$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"%FLAGS .BLKW 1 ; Flagsu+ $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Action + $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Functionm1 $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return codeh1 $DEF LDSNDOPRLST_W_FID_NUM .BLKW 1 ; File ID Num ) $DEF LDSNDOPRLST_W_FID_SEQ .BLKW 1 ; Seqn) $DEF LDSNDOPRLST_W_FID_RVN .BLKW 1 ; Rvnt1 $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device name 1LDSNDOPRLST_K_DEVNAM = 64 ; Device name lengtht2LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST;$; LD file lockvalueblock definitions;h3 $DEFINI LDFLVB,GLOBAL ; LD file lockvalue blocka, $DEF LDFLVB_W_CYLINDERS .BLKW 1 ; Cylinders' $DEF LDFLVB_B_TRACKS .BLKB 1 ; Tracks0( $DEF LDFLVB_B_SECTORS .BLKB 1 ; Sectors5 $DEF LDFLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumberS1 $DEF LDFLVB_L_ALLOCLS .BLKL 1 ; Allocation class* $DEF LDFLVB_W_UNIT .BLKW 1 ; Unit number% $DEF LDFLVB_B_FLAGS .BLKB 1 ; Flagsa*ASSUME . LE 16 ; Length must be <= 16; ; Subfields in FLAGS field;t _VIELD LDFLVB,0,< -$ ,- ; Shared accessable >l $DEFEND LDFLVB;e&; LD device lockvalueblock definitions;e3 $DEFINI LDDLVB,GLOBAL ; LD file lockvalue blockq) $DEF LDDLVB_W_FID .BLKW 1 ; File ID Numh& $DEF LDDLVB_W_SEQ .BLKW 1 ; File Seq& $DEF LDDLVB_W_RVN .BLKW 1 ; File Rvn1 $DEF LDDLVB_B_ALLOCLS .BLKB 1 ; Allocation classo* $DEF LDDLVB_W_UNIT .BLKW 1 ; Unit number+ $DEF LDDLVB_T_DEVNAM .BLKB 7 ; Devicenamet*ASSUME . LE 16 ; Length must be <= 16;o#; Definitions for our functioncodes ; - IO$_LD_CONTROL = 20 ; Main control functions ; (Physical I/O function)e' LDIO_CONNECT = 0 ; Connect drive/filee- LDIO_DISCONNECT = 1 ; Disconnect drive/file % LDIO_ENABLE_TRACE = 2 ; Enable trace ' LDIO_DISABLE_TRACE = 3 ; Disable traceu$ LDIO_GET_TRACE = 4 ; Get tracedata* LDIO_RESET_TRACE = 5 ; Reset tracebuffer8 LDIO_GET_CONNECTION = 6 ; Get connected file/devicename5 LDIO_SET_SEED = 7 ; Set unit seed for cloned devicet% LDIO_ENABLE_WATCH = 8 ; Enable watchh' LDIO_DISABLE_WATCH = 9 ; Disable watch ' LDIO_GET_WATCH = 10 ; Get watchpointsc, LDIO_RESUME_WATCH = 11 ; Resume watchpoints= LDIO_GET_SUSPEND_LIST = 12 ; Get list of suspended processesT0 LDIO_ENABLE_PROTECT = 13 ; Enable write-protect2 LDIO_DISABLE_PROTECT = 14 ; Disable write-protect/ LDIO_SET_ALLOCLASS = 15 ; Set allocation classd LDIO_V_FUNC = 0d LDIO_S_FUNC = 8; ; Function modifiers;n $DEFINI LDIO,GLOBAL _VIELD LDIO,8,<-k" ,- ; Replace drive# ,- ; Abort disconnecto$ ,- ; Get buffer size, ,- ; Don't wait for tracedata3 ,- ; Reset after retrieving tracedatar ,- ; Shared access >c $DEFEND LDIO;d; Connect failure reasonsy;l$ LDREASON_NOTSHARED = 1 ; Not shared* LDREASON_NOSHARE = 2 ; No share specified, LDREASON_ALLOCLASS = 3 ; Alloclass mismatch. LDREASON_UNITNUMBER = 4 ; Unitnumber mismatch' LDREASON_TRACKS = 5 ; Tracks mismatcho( LDREASON_SECTORS = 6 ; Sectors mismatch, LDREASON_CYLINDERS = 7 ; Cylinders mismatch* LDREASON_MAXBLOCK = 8 ; Maxblock mismatch;s; Watchpoint actions;c* WATCH_ACTION_SUSPEND = 0 ; Suspend thread& WATCH_ACTION_CRASH = 1 ; Crash system& WATCH_ACTION_ERROR = 2 ; Return error/ WATCH_ACTION_OPCOM = 3 ; Send message to OPCOMi& WATCH_ACTION_MAX = 3 ; Maximum action;  c;+++; Definitions for IRP fields;--- $DEFINI IRP,GLOBAL ; IRP  .=IRP$K_LENGTHa/ $DEF IRP$L_LD_LDUCB .BLKL 1 ; Logical Disk UCBe/ $DEF IRP$L_LD_LDIOB .BLKL 1 ; Logical disk IOB 6 $DEF IRP$L_LD_FWDQFL .BLKL 1 ; Forwarded IRP queue FL6 $DEF IRP$L_LD_FWDQBL .BLKL 1 ; Forwarded IRP queue BL+ $DEF IRP$K_LD_IRPLEN ; Length of new IRP $DEFEND IRP;+++1; Definitions that follow the standard UCB fieldsc;---0 $DEFINI UCB,GLOBAL ; Start of UCB definitions3 .=UCB$K_MSCP_DISK_LENGTH ; Position at end of UCBn0 $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk; $DEF UCB$L_LD_AIOFL .BLKL 1 ; Active I/O list forward linkc< $DEF UCB$L_LD_AIOBL .BLKL 1 ; Active I/O list backward link)ASSUME UCB$L_LD_AIOBL EQ UCB$L_LD_AIOFL+4o1 $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointeri1 $DEF UCB$L_LD_WCB .BLKL 1 ; Window control block ) $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORBu0 $DEF UCB$L_LD_SAVEPC .BLKL 1 ; Caller's address4 $DEF UCB$L_LD_SAVST .BLKL 1 ; Safe place for status. $DEF UCB$L_LD_FR3 .BLKL 1 ; Safe place for R3. $DEF UCB$L_LD_FR4 .BLKL 1 ; Safe place for R4%ASSUME UCB$L_LD_FR4 EQ UCB$L_LD_FR3+4s $DEF UCB$Q_LD_PD_LKSB .BLKB 8$ ; Phys. dev. lock status block $DEF UCB$A_LD_PD_LVB .BLKB 16  ; + lock value block! $DEF UCB$T_LD_PD_RESNAM .BLKB 28 % ; Phys. dev. lock resource nameu $DEF UCB$Q_LD_FILE_LKSB .BLKB 8 ; Lock status blockn $DEF UCB$A_LD_FILE_LVB .BLKB 16 ; + lock value block# $DEF UCB$T_LD_FILE_RESNAM .BLKB 40 0 ; File resource name 32 bytes + descriptor $DEF UCB$Q_LD_DEV_LKSB .BLKB 8u ; Lock status blockV $DEF UCB$A_LD_DEV_LVB .BLKB 16o ; + lock value block" $DEF UCB$T_LD_DEV_RESNAM .BLKB 402 ; Device resource name 32 bytes + descriptor! $DEF UCB$L_LD_TRCWAITQFL .BLKL 1r* ; Trace data wait queue forward link! $DEF UCB$L_LD_TRCWAITQBL .BLKL 1 + ; Trace data wait queue backward link" $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1+ ; Trace mutex wait queue forward linko" $DEF UCB$L_LD_TRCMUTEXQBL .BLKL 1, ; Trace mutex wait queue backward link1 $DEF UCB$L_LD_TRCPC .BLKL 1 ; Fork trace PC saves $DEF UCB$L_LD_TRCMUTEX .BLKL 1o ; Trace buffer mutex $DEF UCB$L_LD_TRCBUFOWN .BLKL 1 ; Owner of tracebuffer6 $DEF UCB$L_LD_TRCBUF .BLKL 1 ; Pointer to tracebuffer $DEF UCB$L_LD_TRCBUFSIZ .BLKL 1 ; Tracebuffer size% $DEF UCB$L_LD_TRCBUFALLOCSIZ .BLKL 1b" ; Allocated tracebuffer size $DEF UCB$L_LD_TRCBUFPTR .BLKL 1 ; Tracebuffer pointert $DEF UCB$L_LD_TRCWRAP .BLKL 1 ; Flag if buffer wrapped $DEF UCB$L_LD_TRCLOST .BLKL 1$ ; Number of lost trace packets $DEF UCB$L_LD_WATCHQFL .BLKL 1- ; Watch queue forward link $DEF UCB$L_LD_WATCHQBL .BLKL 1R! ; Watch queue backward linke $DEF UCB$L_LD_WATCHCNT .BLKL 1t ; Watch queue entry counts" $DEF UCB$L_LD_WATCHPNDQFL .BLKL 1( ; Watch pending queue forward link" $DEF UCB$L_LD_WATCHPNDQBL .BLKL 1) ; Watch pending queue backward linke, $DEF UCB$W_LD_FID_NUM .BLKW 1 ; File ID Num$ $DEF UCB$W_LD_FID_SEQ .BLKW 1 ; Seq$ $DEF UCB$W_LD_FID_RVN .BLKW 1 ; Rvn; $DEF UCB$L_LD_CPID .BLKL 1 ; Charge PID of cloning processo- $DEF UCB$W_LD_CHARGE .BLKW 1 ; Charge amountc) $DEF UCB$W_LD_FLAGS .BLKW 1 ; Flags byte $VIELD UCB,0,< -0 , - ; Conn./Disconn. status bit. ,- ; Replace mode status bit. ,- ; Connected to DECRAM disk$ ,- ; Write protect' ,- ; Shared accessable 2 ,- ; Containerfile on volumeset' ,- ; DSE not supportedD4 ,- ; Virtual I/O to container file4 ,- ; Forkblock busy (internal only)3 ,- ; Disc. pending (internal only)  >; ; Filename string buffern+ $DEF UCB$K_LD_UCBLEN ; Length of new UCBI( $DEFEND UCB ; End of UCB definitions $ .SBTTL Standard tableso;+++; Driver prologue tables;--- .IF DF V64 DPT_FLAGS= ; SMP safe! ; Driver supports snapshotsn ; if not in a clustery .IFFt% DPT_FLAGS= ; SMP safen .ENDC DPTAB - ; DPT-creation macro& END=LD_END,- ; End of driver label! ADAPTER=NULL,- ; Adapter typet- UCBSIZE=,- ; Length of UCB, MAXUNITS=LD_MAX_UNITS,- ; Max nr of units* UNLOAD=LD_DRV_UNLOAD,- ; Unload routine! NAME=LDDRIVER,- ; Driver name # FLAGS=DPT_FLAGS ; Default flags " DPT_STORE INIT ; Start of load ; initialization table8 DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\> ; Default ACP name4 DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ; ACP class3 DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8; FORK IPL + DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPL4: DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device! DEV$M_ODV!- ; output device" DEV$M_FOD!- ; files oriented( DEV$M_DIR!- ; directory structured DEV$M_AVL!- ; available DEV$M_SHR!- ; sharable DEV$M_RND> ; random access" DPT_STORE UCB,UCB$L_DEVCHAR2,L,<-( DEV$M_NNM!- ; Prefix name with NODE$; DEV$M_NLT!- ; Not-last-track device (no bad block data)L) DEV$M_CDP!- ; To prevent MSCP servingR/ DEV$M_MSCP> ; Fake to get 8$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U-P"6shadowing to workL: DPT_STORE UCB,UCB$W_STS,W,UCB$M_TEMPLATE; Template device; DPT_STORE UCB,UCB$L_LD_TRCBUF,L,0 ; Pointer to tracebufferC7 DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device classA DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_FD1 ; Dev. type = foreign diskB: DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size5 DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Inhibit logical to' ; physical xlation.t% DPT_STORE REINIT ; Start of reloadK ; initialization table2 DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT+ DPT_STORE CRB,- ; Address of controllerI5 CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,LD_CONTROL_INITE' DPT_STORE CRB,- ; Address of device53 CRB$L_INTD+VEC$L_UNITINIT,- ; unit initializationB D,LD_UNIT_INIT ; routine) DPT_STORE END ; End of initializationt;+++; Driver dispatch tableB;--- DDTAB - ; DDT-creation macro DEVNAM=LD,- ; Name of device' START=LD_START,- ; Start I/O routineC& FUNCTB=LD_FUNCTABLE,- ; FDT address* CANCEL=LD_CANCEL,- ; Cancel I/O routine/ UNITINIT=LD_UNIT_INIT,- ; Unit init. routinei/ CLONEDUCB=LD_CLONED_UCB ; Cloned UCB routineL D;+++; Function decision table;;--- UNIVERSAL_SYMBOL LD_FUNCTABLE#;LD_FUNCTABLE: ; FDT for driverM$ FUNCTAB ,- ; Valid I/O functions  ACCESS,- ; Access ACPCONTROL,- ; ACP control CREATE,- ; Create, DEACCESS,- ; DeaccessX DELETE,- ; DeleteC MODIFY,- ; ModifyO MOUNT,- ; Mount/ CRESHAD,- ; Create shadowset virtual unitM) REMSHAD,- ; Remove shadowset member," DSE,- ; Data Security Erase( LD_CONTROL> ; LD control functions' FUNCTAB ,- ; Buffered I/O functions  ; LD control functions? FUNCTAB LD_FDT_SHAD_WCHECK,<- ; Check write to shadow set mbr % WRITELBLK,- ; Write LOGICAL Blocki& WRITEPBLK,- ; Write Physical Block$ WRITEVBLK> ; Write VIRTUAL Block* FUNCTAB +ACP$READBLK,- ; Read functions ; Read virtual block , FUNCTAB +ACP$WRITEBLK,- ; Write functions! ; Write virtual blockE' FUNCTAB LD_FDT_DSE,<- ; DSE function ! DSE> ; Data Secutiry Erasel+ FUNCTAB +ACP$ACCESS,- ; Access functionsO! ; Create file/dir/ FUNCTAB +ACP$DEACCESS,- ; Deaccess functionsK ; Deaccess file+ FUNCTAB +ACP$MODIFY,- ; Modify functionsT! ; Modify file attributes ) FUNCTAB +ACP$MOUNT,- ; Mount functionsT ; Mount volumeE9 FUNCTAB +EXE$LCLDSKVALID,- ; Local disk valid functionsB ; Pack acknowledge5 FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functionst ; Available3 FUNCTAB +EXE$ONEPARM,- ; One parameter functionsC ; Seek, FUNCTAB +EXE$SENSEMODE,- ; Sense functions* ; Sense mode.) FUNCTAB +EXE$SETCHAR,- ; Set functionsU& ; Set moden: FUNCTAB LD_FDT_CRESHAD,- ; Create shadowset virtual unit $4 FUNCTAB LD_FDT_REMSHAD,- ; Remove shadowset member D5 FUNCTAB LD_FDT_CONTROL,- ; General LDdriver controlA ;; ; Local data; .SAVE_PSECT .PSECT $$$110_LD_DATA,LONG,WRTo;n UNIVERSAL_SYMBOL LD_REFCNT. ;LD_REFCNT:o% .LONG 0 ; Number of active devicesL;1FLBN_WP: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\ .ASCII \PID: !XL!/\E .ASCII \Image: !AC!/\A .ASCII \Device: !AC!/\ .ASCII \Function: !XW!/\  .ASCII \LBN: !UL\LBN_WP_LEN=.-LBN_WP ;gFVBN_WP: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\ .ASCII \PID: !XL!/\; .ASCII \Image: !AC!/\V .ASCII \Device: !AC!/\o .ASCII \Function: !XW!/\e .ASCII \VBN: !UL!/\I! .ASCII \File id: (!UW,!UW,!UW)\ VBN_WP_LEN=.-VBN_WPb;4NONESTR: .ASCIC /None/ ; If no imagename available<OPCOM_NAME: .ASCIC /OPCOM/ ; Process name of OPCOM process .RESTORE_PSECTD; ; End of driver data;$ @ .SBTTL LD_FDT_CONTROL, general control and dispatch FDT routine;+++:; LD_FDT_CONTROL, general control and dispatch FDT routine;o; Functional description:_;C9; This routine is invoked via an IO$_LD_CONTROL function. =; We will dispatch to the various other routines according toc; the P6 parameter._;_ ; Inputs: ;i; R0-R2 - scratch registers_.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter>; P6(AP) - Address of longword with functioncode and modifiers;c ; Outputs:;;; The routine must preserve all registers except R0-R2, andi ; R9-R11.D;P;--_ UNIVERSAL_SYMBOL LD_FDT_CONTROL1;LD_FDT_CONTROL: ; General Control FDT routine$# MOVZWL P6(AP),R0 ; Get parameter*1 MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRP  ASSUME LDIO_S_FUNC EQ 89 DISPATCH R0,TYPE=B,<- ; Dispatch according to functionr ,-t$ ,-( ,-* ,-" ,-& ,-, ,-! ,- ( ,-* ,-" ,-( ,-) ,-L, ,-. ,-) >O: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code$ JMP G^EXE$ABORTIO ; Abort the I/O .E .SBTTL LD_GET_CONNECTION, Get connection characteristics FDT routine;+++?; LD_GET_CONNECTION, Get connection characteristics FDT routine_;_; Functional description:D;OF; This routine returns the full filename string to the callers buffer,-; specified via the descriptor address in P1.c; The IOSB returned, contains;&; - Longword 0, The return status code2; and in word 1,The nr of characters transferred0; - Longword 1, The Connected status flag, where; bit 0 = 1 : Connected, and; 0 : Disconnected; bit 1 = 0 : Normal and; 1 : Replaced; bit 2 = 0 : Normal disk; 1 : DECRAM disk ; bit 3 = 0 : Normal access ; 1 : Write protectedt; ; Inputs:E;D; R0-R2 - scratch registersu.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)f.; R5 - address of the UCB (unit control block)1; R6 - addre9S V$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"Gss of the CCB (channel control block)D*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter=; P1(AP) - Address of buffer to receive the devicename stringL:; P2(AP) - Size of buffer to receive the devicename stringA; P3(AP) - Address of buffer to receive file-id of connected fileU;. ; Outputs:;e;; The routine must preserve all registers except R0-R2, and ; R9-R11._;T;-- # UNIVERSAL_SYMBOL LD_GET_CONNECTION 3;LD_GET_CONNECTION: ; Get Connection FDT routinet% MOVL P1(AP),R0 ; Address of bufferi% MOVL P2(AP),R1 ; Get buffer lengthE* BEQL 10$ ; Length 0, just return flags4 JSB G^EXE$READCHK ; Check buffer for write access) MOVZWL #SS$_ACCVIO,R0 ; Assume troublef9 IFNOWRT #6,@P3(AP),20$ ; Check if fid buffer writeablee3 MOVZWL #SS$_DEVINACT,R0 ; Assume already inactivec" BBC #UCB$V_LD_CONSTS,- ; Active? UCB$W_LD_FLAGS(R5),20$6 MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical device ucb+ JSB G^SCH$IOLOCKR ; Lock the IO databaset PUSHR #^M_! MOVL R2,R5 ; Copy ucb addressp! MOVZBL #1,R4 ; DVI$_ALLDEVNAME% MOVL P1(AP),R1 ; Address of bufferI! MOVZWL P2(AP),R0 ; Buffer sizep1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicenamel POPR #^M+ PUSHR #^M ; Save across unlockd. JSB G^SCH$IOUNLOCK ; Unlock the IO database. POPR #^M ; Restore IRP and status BLBC R0,20$ ; Troublec* MOVL P3(AP),R2 ; Get fid buffer address ASSUME FID$W_NUM+2 EQ FID$W_SEQ ASSUME FID$W_SEQ+2 EQ FID$W_RVN. ASSUME UCB$W_LD_FID_NUM+2 EQ UCB$W_LD_FID_SEQ3 MOVL UCB$W_LD_FID_NUM(R5),(R2)+ ; Insert in buffera MOVW UCB$W_LD_FID_RVN(R5),(R2)S10$: MOVZWL #SS$_NORMAL,R0- INSV R1,#16,#16,R0 ; Place high word in R0_< MOVZWL UCB$W_LD_FLAGS(R5),R1 ; Copy connected status flags JMP G^EXE$FINISHIO ; Done(20$: JMP G^EXE$ABORTIO ; Abort the I/O T0 .SBTTL LD_GET_TRACE, Get trace data FDT routine;+++*; LD_GET_TRACE, Get trace data FDT routine;l; Functional description:i;a:; This routine retrieves trace data from the trace buffer.; The IOSB returned, contains;&; - Longword 0, The return status code4; - Longword 1, The number of returned trace buffers;T ; Inputs:A; ; R0-R2 - scratch registers+.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)$.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)$*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter6; P1(AP) - address of buffer to receive the trace data3; P2(AP) - size of buffer to receive the trace dataL;T ; Outputs:;t; R0 - I/O statusE; R1 - Number of returned bytes ;s;; The routine must preserve all registers except R0-R2, andd ; R9-R11.E;C;--D UNIVERSAL_SYMBOL LD_GET_TRACE;LD_GET_TRACE:2 TSTL UCB$L_LD_TRCBUF(R5) ; Check for tracebuffer BEQL 10$ ; Not set, BBC #LDIO_V_INQUIRE,- ; Return tracebuffer IRP$L_EXTEND(R3),30$6 MOVL UCB$L_LD_TRCBUFSIZ(R5),R2 ; Get tracebuffer size" MOVZWL #SS$_NORMAL,R0 ; Success BRW 50$ ; Finish the I/O/10$: MOVZWL #SS$_NODATA,R0 ; Trace not activeL(20$: JMP G^EXE$ABORTIO ; Abort the I/O730$: MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too small_% MOVL P2(AP),R1 ; Get buffer length, CMPL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Check size BLSS 20$ ; Too small) MOVL P1(AP),R0 ; Get address of bufferP5 JSB G^EXE$READCHK ; Check userbuffer accessabilitym0 MOVL R0,IRP$L_SVAPTE(R3) ; Save buffer address) BSBW LD_MOVE_TRACE ; Move data to userK' BLBC R0,60$ ; Stop on overrun errorC$ ; The count will always be the' ; number of packets in the bufferG50$: DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to number of entriesD BNEQ 60$ ; Something there. BBS #LDIO_V_NOWAIT,- ; We don't want to wait IRP$L_EXTEND(R3),60$, JMP G^EXE$QIODRVPKT ; Finish in start I/O,60$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE JMP G^EXE$FINISHIO ; Done T. .SBTTL LD_MOVE_TRACE, Move trace data to user;+++(; LD_MOVE_TRACE, move trace data to user;1; Functional description:qA; c8; This routine moves the tracedata to the user's buffer.;; If the modifier LDIO_RESET was specified for the transfer0; then the tracebuffer will be reset afterwards.;. ; Inputs: ; .; R3 - address of the IRP (I/O request packet).; R5 - address of the UCB (unit control block);S ; Outputs:; D; R0 - Status, either SS$_NORMAL or SS$_DATAOVERUN (buffer overflow)-; R1 - 0 or on failure number of lost packetsU; R2 - number of bytes moved; 7; The routine must preserve all registers except R0-R2. ;f;--a UNIVERSAL_SYMBOL LD_MOVE_TRACEL;LD_MOVE_TRACE:r PUSHR #^M( MOVL IRP$L_SVAPTE(R3),R0 ; Get address% MOVL IRP$L_BCNT(R3),R1 ; Get length$) BBC #LDIO_V_RESET,- ; Reset requested?  IRP$L_EXTEND(R3),40$ ; No: LOCK_TRACE ACCESS=WRITE ; Lock the tracebuffer for write BRW 50$240$: LOCK_TRACE ; Lock the tracebuffer for read.50$: CLRL R10 ; Total number of bytes moved9 MOVL UCB$L_LD_TRCBUFPTR(R5),R2 ; Get tracebuffer pointerM5 MOVL UCB$L_LD_TRCWRAP(R5),R9 ; Did the buffer wrap?; BEQL 60$ ; NoD SUBL3 R2,R9,R1 ; BytecountC MOVL R1,R10 ; Save count/ BSBW MOVE_TRACE ; Move the data (first part)K<60$: MOVL UCB$L_LD_TRCBUF(R5),R2 ; Point to start of buffer# SUBL3 R2,UCB$L_LD_TRCBUFPTR(R5),R1># ADDL2 R1,R10 ; Accumulate countL0 BSBW MOVE_TRACE ; Move the data (second part)* MOVL UCB$L_LD_TRCLOST(R5),R1 ; Get count- MOVZWL #SS$_NORMAL,R0 ; Assume no overflow, TSTL R9 ; Buffer wrapped?  BEQL 70$ ; No ;oA; Calculate number of lost packets. This will be done as follows:;L7; Count = (((TRCBUFPTR - TRCBUF) / LDTRCENT_K_LENGTH) +KF; ((TRCLOST - 1) * ((TRCWRAP - TRCBUF) / LDTRCENT_K_LENGTH)));e DECL R1 ; TRCLOST - 10 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCWRAP - TRCBUF UCB$L_LD_TRCWRAP(R5),R0C3 DIVL2 #LDTRCENT_K_LENGTH,R0 ; / LDTRCENT_K_LENGTH. MULL2 R1,R02 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCBUFPTR - TRCBUF UCB$L_LD_TRCBUFPTR(R5),R1S3 DIVL2 #LDTRCENT_K_LENGTH,R1 ; / LDTRCENT_K_LENGTHo ADDL2 R0,R1 ; Result8 MOVZWL #SS$_DATAOVERUN,R0 ; We were overrun by a truck-70$: BBC #LDIO_V_RESET,- ; Reset requested?o IRP$L_EXTEND(R3),80$ ; No- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrapped. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer)80$: UNLOCK_TRACE ; Unlock trace mutexA% MOVL R10,R2 ; Return amount moved= POPR #^Ml RSB; MOVE_TRACE:I PUSHR #^M MOVL R1,R6 ; LengthS MOVL R2,R1 ; Source\ MOVL R0,R3 ; Destination4 DIVL3 #65535,R6,R7 ; Number of times we must loop- BEQL 20$ ; Total count is less than 65535 >10$: MOVC3 #65535,(R1),(R3) ; Move data (max MOVC can handle)+ SUBL2 #65535,R6 ; Accumulate bytes movedE SOBGTR R7,10$ ; Until done.20$: MOVC3 R6,(R1),(R3) ; Move the remainder# MOVL R3,R0 ; Return last byte+1  POPR #^MV RSB - .SBTTL LD_DISCONNECT, Disconnect FDT routine$;+++'; LD_DISCONNECT, Disconnect FDT routineL;V; Functional description: ;rD; This routine will disconnect the Logical disk from the Phys. Disk.G; If LDIO_M_ABORT is specified we simply skip all checks and disconnect ; the LD device as it is.;TC; A prereqisite to call these routines, is that the file is opened,B'; except when we replace a whole drive.f;C ; Inputs:E;B; R0-R2 - scratch registersD.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)B.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter:; P1(AP) - Address of the SBK block (Ignored with abort or!; replaced drive)E;D ; Outputs:; ;; The routine must preserve all registers except R0-R2, andi ; R9-R11.o;;--D UNIVERSAL_SYMBOL LD_DISCONNECTO,;LD_DISCONNECT: ; Disconnect FDT routine+ MOVZWL #SS$_DEVINACT,R0 ; Assume inactiveD7 BBC #UCB$V_LD_CONSTS,- ; Check if in DISCONNECT stateR UCB$W_LD_FLAGS(R5),10$; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume:3ek$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U#"X foreign mounted status_> BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),10$; Device foreign mounted?1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted statuse6 BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),10$; Device mounted?5 MOVZWL #SS$_DEVASSIGN,R0 ; Assume channels assigned;/ CMPW UCB$W_REFC(R5),#1 ; Are we the only one?t BNEQ 10$ ; No, get out2 BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffersl. CVTWL IRP$W_CHAN(R3),R0 ; Get channel number1 MOVL G^CTL$GL_CCBBASE,R1 ; Get base of channelsM MOVAB (R1)[R0],R0 ; Get CCBD MOVL UCB$L_LD_WCB(R5),CCB$L_WIND(R0) ; Save WCB address for cleanup, JMP G^EXE$QIODRVPKT ; Finish in start I/O,10$: JMP G^EXE$ABORTIO ; And abort the I/O b' .SBTTL LD_CONNECT, Connect FDT routinel;+++!; LD_CONNECT, Connect FDT routineb;k; Functional description:v;u@; This routine will connect a Logical disk to the Physical Disk.; C; A prereqisite to call these routines, is that the file is opened,n'; except when we replace a whole drive.S; ; Inputs:s;; R0-R2 - scratch registersO.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)E.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)c*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter4; P1(AP) - Address of the SBK block (Normal connect)B; P1(AP) - Address of phys. disk device name desc. (Replace drive)@; P2(AP) - Size of disk, if 0 allocated size of FCB will be used; P3(AP) - Number of TracksC; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;S ; Outputs:;s;; The routine must preserve all registers except R0-R2, andN ; R9-R11.M;t;--S UNIVERSAL_SYMBOL LD_CONNECT&;LD_CONNECT: ; Connect FDT routine0 BBC #UCB$V_LD_CONSTS,- ; Check if disconnected UCB$W_LD_FLAGS(R5),10$. MOVZWL #SS$_DEVACTIVE,R0 ; Set Device Active BRW 140$ 510$: BBC #LDIO_V_SHARE,- ; Shared access requested?F IRP$L_EXTEND(R3),20$, TSTL @#CLU$GL_CLUB ; Cluster code loaded? BNEQ 20$ ; Yes, MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported BRW 140$l.20$: BBS #LDIO_V_REPLACE,- ; Replace a drive? IRP$L_EXTEND(R3),40$, MOVL #SBK$K_LENGTH,R1 ; Get the SBK length' MOVL P1(AP),R0 ; Get the SBK addressi0 JSB G^EXE$WRITECHK ; Check if SBK is readable PUSHL R0V4 JSB G^SCH$IOLOCKR ; Lock the IO database for read MOVL (SP)+,R0- MOVL SBK$L_FCB(R0),R1 ; Get the FCB addressF% BGEQ 30$ ; Must be system addressn6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 30$ ; No, return error$$ TSTW FCB$W_REFCNT(R1) ; File open? BEQL 30$ ; No, error7 MOVL R1,UCB$L_LD_FCB(R5) ; Save FCB address for laterA, MOVL FCB$L_WLFL(R1),R1 ; Get a WCB address3 MOVL #SS$_FILNOTACC,R0 ; Assume completely mappedi/ BBC #WCB$V_COMPLETE,- ; Check if complete mapn WCB$B_ACCESS(R1),35$5 BBC #WCB$V_CATHEDRAL,- ; and with cathedral windows  WCB$B_ACCESS(R1),35$- MOVL R1,UCB$L_LD_WCB(R5) ; Save WCB address 7 MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical deviceN! MOVL UCB$L_VCB(R1),R2 ; Get VCBe! MOVL VCB$L_AQB(R2),R0 ; Get AQBL; CMPB AQB$B_ACPTYPE(R0),#AQB$K_F11V2 ; Serviced by F11BXQP?A BNEQ 27$ ; Noa. TSTW VCB$W_RVN(R2) ; Relative volume number) BEQL 25$ ; Branch if not a volume seti+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addressE BEQL 25$ ; Not there0 MOVL RVT$L_UCBLST(R0),R1 ; Get root volume UCB*25$: MOVL R1,UCB$L_LD_PDUCB(R5) ; Save it BRB 50$,27$: MOVZWL #SS$_WRONGACP,R0 ; Bad ACP type BRW 90$*30$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid.35$: BRW 90$ ; Abort the I/O and unlock mtx940$: MOVL P1(AP),R1 ; Get devicename descriptor addressb0 MOVL 4(R1),R0 ; Get devicename string address1 MOVZWL (R1),R1 ; Get devicename string lengtho3 JSB G^EXE$WRITECHK ; Check if string is readableN4 PUSHR #^M ; Save some registers4 JSB G^SCH$IOLOCKR ; Lock the IO database for read/ MOVL P1(AP),R1 ; Get devicename descr. addr.c- MOVL #IOC$M_ANY,R2 ; Set the SEARCHALL bitI* CLRL R3 ; No SB specified (local only), JSB G^IOC$SEARCH ; Search the IO database5 POPR #^M ; Restore the registers\! BLBC R0,90$ ; Return on errori= MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. disk 550$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid device:7 CMPB UCB$B_DEVCLASS(R1),#DC$_DISK ; Connect to a disk?( BNEQ 90$ ; No, not allowed( PUSHR #^M ; Save across unlock. JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M ; Restore IRP! BRB 100$ ; Passed the test...i/90$: PUSHR #^M ; Save across unlocko. JSB G^SCH$IOUNLOCK ; Unlock the IO database1 POPR #^M ; Restore the return statuso BRW 140$n-100$: BBC #LDIO_V_REPLACE,- ; Replace drive?i IRP$L_EXTEND(R3),110$i/ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCBt; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted status. BBS #DEV$V_FOR,- ; Check if foreign mounted UCB$L_DEVCHAR(R2),140$1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted statusk0 BBS #DEV$V_MNT,- ; Check if device is mounted UCB$L_DEVCHAR(R2),140$, MOVZWL #SS$_DEVALLOC,R0 ; Assume allocated. TSTL UCB$L_LOCKID(R2) ; Any lock for device? BNEQ 140$ ; Yes, error exit BRW 130$ ; Continuee4110$: IFRD #4,P2(AP),120$ ; Check if size readable+ MOVZWL #SS$_ACCVIO,R0 ; Access violationu BRB 140$ ; Abort;120$: BSBW LD_SET_GEOMETRY ; Fill in geometry informationr BLBC R0,140$ ; Quit on error' MOVL P1(AP),R9 ; Get the SBK addressL- MOVL SBK$L_FCB(R9),R9 ; Get the FCB addresst:130$: BSBW LD_MAKE_FILE_RESNAM ; Create file resourcename6 BSBW LD_MAKE_DEV_RESNAM ; Create device resourcename5 JMP G^EXE$QIODRVPKT ; Do the rest in the start I/On-140$: JMP G^EXE$ABORTIO ; And abort the I/O_ C5 .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometryL;+++/; LD_SET_GEOMETRY, Setup pseudo device geometry;L; Functional description:>;;; This routine will set the geometry for the pseudo device.N;D ; Inputs:T;>; R0-R2 - scratch registers_.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)L.; R5 - address of the UCB (unit control block); R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter4; P1(AP) - Address of the SBK block (Normal connect)@; P2(AP) - Size of disk, if 0 allocated size of FCB will be used; P3(AP) - Number of TracksS; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;O ; Outputs:;D ; R0 - status,;t;; The routine must preserve all registers except R0-R2, andE ; R9-R11.c;e;-- ! UNIVERSAL_SYMBOL LD_SET_GEOMETRY ;LD_SET_GEOMETRY:i# MOVL P1(AP),R2 ; Get SBK addressf- MOVL SBK$L_FCB(R2),R0 ; Get the FCB addressp8 MOVL FCB$L_EFBLK(R0),R0 ; Get maximum number of blocks MOVL R0,R2 ; Also in R2d MOVL P2(AP),R1 ; Get the size" BNEQ 10$ ; Available, check it# MOVL R0,R1 ; Use allocated sizee BRB 20$!10$: CMPL R1,R0 ; Not too big?, BLEQU 20$ ; Valid, use itt( MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 120$c;D; At this point R1 is the MAXBLOCK (maximum logical blocknumber + 1); ,20$: MOVL P3(AP),R9 ; Get number of tracks* MOVL P4(AP),R10 ; Get number of sectors, MOVL P5(AP),R11 ; Get number of cylinders) BISL3 R9,R10,R0 ; Check for all zeroeso BISL2 R11,R0o* BEQL 80$ ; All zero, use own algorithm;l:; At least one of (TRACKS,SECTORS,CYLINDERS) was specifiedA; Make sure they are all at least 1 and that they fit in the UCB. ;l TSTL R9 ; Tracks zero? BNEQ 30$ ; Nog INCL R9 ; Make 1%30$: CMPL R9,#256 ; Within bounds?t BGEQU 70$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; No  INCL R10 ; Make 1 %40$: CMPL R10,#256 ; Within bounds?) BGEQU 70$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; Not INCL R11 ; Make onet'50$: CMPL R11,#65536 ; Within bounds? BGEQU 70$ ; NoC MULL3 R9,R10,R2 ; R2 now size of one Cylinder (Tracks * Sectors)C% MULL3 R2,R11,R0 ; R0 now T * S * C = CMPL R0,R1 ; Will MAXBLOCK fit within specified geometry?( BLSSU 70$ ; T * S * C not big enough<; ADDL2 R2,R1 ; R1 now MAXBLOCK + one Cylinder's capacity>; CMPL R1,R0 ; Is MAXBLOCK + (Tracks * Sectors) > T * S * C+; ; i.e. is T * S * (C-1) < MAXBLOCK?$>; BLEQU 70$ ; We should never have more than (T*S)-1 blocks; ; of T * S * C unusedR) MOVB R9,UCB$B;R$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"i_TRACKS(R5) ; Setup tracksp, MOVB R10,UCB$B_SECTORS(R5) ; Setup sectors0 MOVW R11,UCB$W_CYLINDERS(R5) ; Setup cylinders MOVL P2(AP),R1 ; Get the size" BNEQ 60$ ; Something specified+ MOVL R0,R1 ; Setup new size as dictated) ; by T/S/C;J; Final check: see if the maximum block specified either by the user or byF; the filesize is less or equal to the size specified by the number of; tracks/sectors/cylinders.R;360$: CMPL R1,R0 BLEQU 110$B670$: MOVZWL #SS$_BADPARAM,R0 ; Bad geometry parameter BRW 145$280$: MOVB #1,UCB$B_TRACKS(R5) ; Setup for 1 track, CMPL R1,#256 ; Smaller then 256 blocks ? BGEQ 90$ ; No, next checkF/ MOVB R1,UCB$B_SECTORS(R5) ; File size sectors_* MOVW #1,UCB$W_CYLINDERS(R5) ; 1 cylinder BRB 110$ ; Common code pathl390$: CMPL R1,#65536 ; Smaller then 65536 blocks ?C BGEQ 120$ ; No, next check' MOVB #2,UCB$B_SECTORS(R5) ; 2 sectorsE) ASHL #-1,R1,R1 ; Divide file size by 2G6 MOVW R1,UCB$W_CYLINDERS(R5) ; And enter as cylinders. ASHL #1,R1,R1 ; Round to a 2 block boundary9110$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Set max. nr of blockst BRW 140$f9120$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Save number of blocksu' ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKSr@ MOVW #^X604,UCB$B_SECTORS(R5) ; Fill in dummy values for sector ; and track fields; K; Calculate a new value for cylinders to ensure that the product is greaterkH; than maxblock. Use the same algorithm as DUDRIVER so that disks served>; in a cluster appear to have the same geometry on every node.; 8130$: MOVZBL UCB$B_TRACKS(R5),R1 ; Get number of tracks5 MOVZBL UCB$B_SECTORS(R5),R0 ; Get number of sectors  MULL R1,R0 ; Multiplyp3 MOVL UCB$L_MAXBLOCK(R5),R1 ; Get number of blocks ) CLRL R2 ; Prepare for extended dividec> EDIV R0,R1,R0,R1 ; Calculate number of cylinders, remainder- CMPL R0,#65534 ; Is cylinder number legal?n( BGTR 150$ ; If so go try a crude fix8 MOVW R0,UCB$W_CYLINDERS(R5) ; Save number of cylinders TSTL R1 ; Zero remainder?_ BEQL 140$ ; Branch if so; INCW UCB$W_CYLINDERS(R5) ; Otherwise, increment cylinderse* ; (tracks * sectors * cylinders must ; be >= maxblock))140$: MOVZWL #SS$_NORMAL,R0M 145$: RSBR;,J; Out of line code to handle really large geometries to the limits of what; VMS can currently do.0;J%; First method can waste 1024 blocks.0L; Second method can waste 9216 blocks. Third can waste 65025. This is rather:; crude, but will at least allow the structure to be used.;D; Note that we test against 65534 cylinders because cyl count may beJ; incremented if maxblock field is more than trk*sect*cyl. This guaranteesL; the cylinder field will be legal and thus the device will be usable, otherE; things being equal. Note that because we accept devices with zeroestG; in trk or sect and will have filled in ^X604 above, we have four fakeN; geometries in all:; 6 x 4 x nn; 32 x 32 x n ; 96 x 96 x ne; 255 x255 x nN;IB; This means geometry based loss of up to 23, 1023, 9215, or 65024F; blocks as the device gets bigger, but the device will be usable over; most of its surface.; 150$: MOVB #32,UCB$B_TRACKS(R5)C MOVB #32,UCB$B_SECTORS(R5)+6 CMPL UCB$L_MAXBLOCK(R5),- ; Does 32 by 32 by n work? #<65534*32*32> BLSSU 130$ > MOVB #96,UCB$B_TRACKS(R5) ; Set 96 by 96 by n geom MOVB #96,UCB$B_SECTORS(R5)t6 CMPL UCB$L_MAXBLOCK(R5),- ; Be sure disk not too big #<65534*96*96>' BLSSU 130$ ; Redo computation if okr;t); If disk is over 300Gb, try to allow 2TBd;sF; This should be adequate for any currently supported disk size (i.e.,H; with a 32-bit block number a la SCSI-2), losing at most a small bit of<; capacity but allowing disk access for most of the surface.;a+ MOVB #255,UCB$B_TRACKS(R5) ; Go for brokeh MOVB #255,UCB$B_SECTORS(R5)+ BRW 130$ ; This is as big as we can go.S O5 .SBTTL LD_SET_SEED, Set seed unit number FDT routine;+++/; LD_SET_SEED, Set seed unit number FDT routineC;R; Functional description:#;O:; This routine sets the seed number to the specified value; ; Inputs: ;E; R0-R2 - scratch registersf.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)T.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)_*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; P1(AP) - seed valueh;a ; Outputs:;K;; The routine must preserve all registers except R0-R2, andf ; R9-R11.R;C;--D UNIVERSAL_SYMBOL LD_SET_SEED1 ;LD_SET_SEED:t MOVL P1(AP),R0 ; Get valueM CMPL R0,#9999 ; Limit range BGTRU 10$1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB;3 MOVL DDB$L_UCB(R1),R1 ; Get address of UNIT 0 UCB* MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/O-%10$: MOVL #SS$_BADPARAM,R0 ; Trouble $ JMP G^EXE$ABORTIO ; Abort the I/O : .SBTTL LD_SET_ALLOCLASS, Set allocation class FDT routine;+++4; LD_SET_ALLOCLASS, Set allocation class FDT routine;R; Functional description:L;TA; This routine is the FDT routine to set the allocation class forR@; the LD devices. This can only be done when no other LD devicesA; are active. If that's not the case and the specified allocationu@; class is the same as the one already set we will ignore it and; return success.T; ; Inputs:q;t; R0-R2 - scratch registers .; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)t.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)V*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers!; P1(AP) - allocation class value;V ; Outputs:;s;; The routine must preserve all registers except R0-R2, andp ; R9-R11. ;;;--l" UNIVERSAL_SYMBOL LD_SET_ALLOCLASS;LD_SET_ALLOCLASS: MOVL P1(AP),R2 ; Get valuen CMPL R2,#255 ; Limit range l BGTRU 20$ ; Out of range1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDBo* CMPL R2,DDB$L_ALLOCLS(R1) ; Same number? BEQL 10$ ; Ok, accept it. TSTW LD_REFCNT ; Already someone connected? BNEQ 30$ ; Yes, not allowed - FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronizeu SAVIPL=-(SP),-n PRESERVE=NO; MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDB+ MOVL UCB$L_CDDB(R5),R1 ; Get address CDDB = MOVL R2,CDDB$L_ALLOCLS(R1) ; Setup allocation class in CDDBT0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock& NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO$10$: MOVL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/OR%20$: MOVL #SS$_BADPARAM,R0 ; Trouble  BRB 40$:30$: MOVZWL #SS$_UNSAFE,R0 ; No devices may be connected(40$: JMP G^EXE$ABORTIO ; Abort the I/O n5 .SBTTL LD_DISABLE_TRACE, Disable tracing FDT routinee;+++/; LD_DISABLE_TRACE, Disable tracing FDT routinee; ; Functional description:;P?; This routine disables tracing and deallocates the tracebuffer-;d ; Inputs: ; ; R0-R2 - scratch registers.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)x.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)c*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;D ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11._;,;--_" UNIVERSAL_SYMBOL LD_DISABLE_TRACE;LD_DISABLE_TRACE:2 MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get buffer pointer BNEQ 10$ ; Set( MOVZWL #SS$_NODATA,R0 ; Trace not set BRW 30$010$: LOCK_TRACE ACCESS=WRITE ; Lock trace mutex) CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointerg9 MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R1 ; Get allocated size& SUBL2 #12,R0 ; Point to real start) JSB G^EXE$DEANONPGDSIZ ; Release memory 2 MOVL UCB$L_LD_TRCBUFOWN(R5),R2 ; Get buffer ownerB ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested2 CMPL R2,IRP$L_PID(R3) ; Did we own it ourselves? BNEQ 20$ ; Yes- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaO BRB 25$ 20$: PUSHL R4! MOVL R0,R1 ; Amount to return " MOVL R2,R4 ; Process to credit0 BSBW LD_RETURN_QUOTA ; Credit correct process0 ; Ignore errors (Proc. may have gone away) MOVL (SP)+,R4)25$: UNLOCK_TRACE ; Unlock trace mutex " MOVZWL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINIS<,H$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U_"zHIOC ; Finish the I/O ,30$: JMP G^EXE$ABORTIO ; And abort the I/O a3 .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routines;+++-; LD_ENABLE_TRACE, Enable tracing FDT routineo;h; Functional description:;6;; This routine allocates a tracebuffer and enables tracing m; ; Inputs:f;t; R0-R2 - scratch registersh.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)n.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)o*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;f ; Outputs:;A;; The routine must preserve all registers except R0-R2, and ; R9-R11.s; ;--o! UNIVERSAL_SYMBOL LD_ENABLE_TRACEx;LD_ENABLE_TRACE: 7 MOVZWL #SS$_TOOMUCHDATA,R0 ; Assume trace already setN2 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BNEQ 10$ ; Already set0 MOVZWL #SS$_BADPARAM,R0 ; Assume bad parameter) MOVL P1(AP),R1 ; Get size (in entries): BEQL 10$ ; Nothing??@ MULL2 #LDTRCENT_K_LENGTH,R1 ; Length of LDTRCENT * #of entries0 MOVL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Save used size;VK; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to beAJ; able to deallocate the packet as a forkblock when the driver is reloaded; & ADDL2 #12,R1 ; Add packet overhead4 BISL2 #^X80000000,R1 ; Avoid check against MAXBUF. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,20$ ; Enuf leftd/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quotah10$: BRW 30$ ; Get out;(20$: JSB G^EXE$ALONONPAGED ; Get memory BLBC R0,10$ ; Errori" ADDL2 #12,R2 ; Leave some room;r, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex( MOVL IRP$L_PID(R3),- ; Register owner UCB$L_LD_TRCBUFOWN(R5)- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrappedW. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet8 MOVL R1,UCB$L_LD_TRCBUFALLOCSIZ(R5) ; Setup buffer size, MOVL R2,UCB$L_LD_TRCBUF(R5) ; Setup buffer. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer% UNLOCK_TRACE ; Unlock trace mutexC MOVZWL #SS$_NORMAL,R0' JMP G^EXE$FINISHIOC ; Finish the I/Oo,30$: JMP G^EXE$ABORTIO ; And abort the I/O C5 .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routineT;+++/; LD_RESET_TRACE, Reset tracebuffer FDT routine ;;; Functional description: ;e-; This routine resets the tracebuffer pointern;a ; Inputs:i;M; R0-R2 - scratch registers .; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)V.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)9*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;i ; Outputs:;o;; The routine must preserve all registers except R0-R2, and ; R9-R11.); ;--G UNIVERSAL_SYMBOL LD_RESET_TRACE;LD_RESET_TRACE:- MOVZWL #SS$_NODATA,R0 ; Assume not yet set42 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BEQL 10$ ; Not there, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrappedh. CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer% UNLOCK_TRACE ; Unlock trace mutex> MOVZWL #SS$_NORMAL,R0' JMP G^EXE$FINISHIOC ; Finish the I/OM,10$: JMP G^EXE$ABORTIO ; And abort the I/O y/ .SBTTL LD_ENABLE_PROTECT, Enable write protecte;+++*; LD_ENABLE_PROTECT, Enable write protect;;; Functional description: ; <; This routine enables write-protection on the logical disk.;J ; Inputs:U;C; R0-R2 - scratch registers.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block);.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)r*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter;u ; Outputs:;t;; The routine must preserve all registers except R0-R2, and ; R9-R11.A;2;--$# UNIVERSAL_SYMBOL LD_ENABLE_PROTECTm3;LD_ENABLE_PROTECT: ; Enable protect FDT routine 1 BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bitD* BISW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC B1 .SBTTL LD_DISABLE_PROTECT, Disable write protect ;+++,; LD_DISABLE_PROTECT, Disable write protect;A; Functional description: ;;=; This routine disables write-protection on the logical disk.m;o ; Inputs:0;0; R0-R2 - scratch registers1.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)A.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter;t ; Outputs:;e;; The routine must preserve all registers except R0-R2, ande ; R9-R11.h;e;--y$ UNIVERSAL_SYMBOL LD_DISABLE_PROTECT5;LD_DISABLE_PROTECT: ; Disable protect FDT routine 5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB * BBC #DEV$V_SWL,- ; Check write-lock bit UCB$L_DEVCHAR(R1),10$c4 MOVL #SS$_WRITLCK,R0 ; Physical disk is protected BRW 20$510$: BICL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bitB* BICW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R020$: JMP G^EXE$FINISHIOC / .SBTTL LD_DEALLOC_TRCBUF, Trace buffer releasef;+++*; LD_DEALLOC_TRCBUF, Trace buffer release; ; Functional description:l;e;; This routine deallocates the tracebuffer if we disconnectS;; from the physical device (in case of cloned device) or in ; case of driver reload.;$F; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).R; ; Inputs:E;01; R4 - address of the PCB (process control block)l#; - 0 if called from LD_DRV_UNLOAD1.; R5 - address of the UCB (unit control block);i ; Outputs:;$;; The routine must preserve all registers except R0-R2, and ; R9-R11.A;O;--a# UNIVERSAL_SYMBOL LD_DEALLOC_TRCBUF0;LD_DEALLOC_TRCBUF: + MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get addresst BNEQ 10$ ; Buffer availableR BRW 60$ 10$: TSTL R4 ; PCB available?) BEQL 20$ ; No, no need to synchronizeo! ; (Called by LD_DRV_UNLOAD)a- LOCK_TRACE ACCESS=WRITE ; Lock trace buffer,* CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get ownerB UNLOCK_TRACE9 BRB 30$.20$: CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get ownerB#30$: MOVQ R3,-(SP) ; Save R3 + R4s/ MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get size ' SUBL2 #12,R0 ; Account for overhead)" ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1- MOVW #>,- ; and proper spinlock 1 FKB$B_TYPE(R0) ; for this to be a fork blockR PUSHL R5 ; Save UCBr MOVL R0,R5 ; Copy address," SAVIPL -(SP) ; Get current IPL> CMPL (SP),#IPL$_RESCHED ; Compare to minimum Fork ipl needed0 BGEQ 35$ ; Don't change IPL if not necessary% SETIPL #IPL$_RESCHED,- ; Raise IPLa ENVIRON=UNIPROCESSOR*35$: PUSHAB 40$ ; Set up return address FORK ; Create fork % MOVL R5,R0 ; Deallocate the blockW" MOVL FKB$L_FR3(R5),R1 ; Get size" JMP G^EXE$DEANONPGDSIZ ; Get outI40$: SETIPL (SP)+,ENVIRON=UNIPROCESSOR ; Restore IPL to value before fork & MOVL (SP)+,R5 ; Restore UCB pointer" MOVQ (SP)+,R3 ; Restore R3 + R4 TSTL R4 ; PCB available?# BEQL 60$ ; No, no one to credit B ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested+ CMPL PCB$L_PID(R4),R1 ; Are we the owner?i BNEQ 50$ ; No - JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota  BRB 60$ 50$: PUSHL R4 MOVL R1,R4 ; Owner pid! MOVL R0,R1 ; Amount to returnB% BSBW LD_RETURN_QUOTA ; Credit user  MOVL (SP)+,R460$: RSB 1 .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer releasek;+++,; LD_DEALLOC_WATCHBUF, Watch buffer release;0; Functional description:V;1<; This routine deallocates all watchbuffers if we disconnect;; from the physical device (in case of cloned device) or in0; case of driver reload.; F; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).e;l ; Inputs:G;M1; R4 - address of the PCB (process con=xC$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U/"trol block)s#; - 0 if called from LD_DRV_UNLOADc.; R5 - address of the UCB (unit control block);e ; Outputs:;o;; The routine must preserve all registers except R0-R2, andn ; R9-R11.o;u;--S% UNIVERSAL_SYMBOL LD_DEALLOC_WATCHBUFM;LD_DEALLOC_WATCHBUF:S TSTL R4 ; PCB available?) BEQL 50$ ; No, no need to synchronizea5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelo SAVIPL=-(SP)110$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entryo BVS 40$ ; None there' MOVL LDWATCH_L_PID(R0),R1 ; Get owner " PUSHL R0 ; Save buffer address CMPL R1,PCB$L_PID(R4) ; Ours?  BNEQ 20$ ; No$1 MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to return1- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota  BRB 30$ 20$: PUSHL R4C MOVL R1,R4 ; Get pid0 MOVL #LDWATCHPT_K_LENGTH,R1 ; Amount to return3 BSBW LD_RETURN_QUOTA ; Return to correct processc MOVL (SP)+,R4,30$: MOVL (SP)+,R0 ; Restore bufferpointer' JSB G^COM$DRVDEALMEM ; Dealloc memorym BRB 10$440$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock& NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO BRB 60$150$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entry BVS 60$ ; None there' JSB G^COM$DRVDEALMEM ; Dealloc memoryR BRB 50$60$: RSB t7 .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routine ;+++1; LD_ENABLE_WATCH, Enable watchpoints FDT routine ;c; Functional description:a; /; This is the FDT routine to enable watchpointsr; ; Inputs:w; ; R0-R2 - scratch registerst.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)d*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;x ; Outputs:;2;; The routine must preserve all registers except R0-R2, anda ; R9-R11. ;e;--s! UNIVERSAL_SYMBOL LD_ENABLE_WATCH;LD_ENABLE_WATCH:i! CLRL R9 ; Nothing charged yet & MOVL P1(AP),R0 ; Get buffer address8 MULL3 P2(AP),#LDWATCHPT_K_LENGTH,R1 ; Get buffer length BEQL 30$ ; Nothing there' MOVQ R0,R10 ; Save address and size54 JSB G^EXE$WRITECHK ; Check buffer for read access) MOVL P2(AP),R2 ; Get number of entries9# MOVL R2,IRP$L_OBCNT(R3) ; Save itB;K; Validate inputbuffer;t;10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file modef LDWATCHPT_W_FLAGS(R0),60$05 TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBNo BEQL 40$n PUSHR #^Ms3 MOVL LDWATCHPT_L_SBK(R0),R0 ; Get the SBK address,, MOVL #SBK$K_LENGTH,R1 ; Get the SBK length0 JSB G^EXE$WRITECHK ; Check if SBK is readable- MOVL SBK$L_FCB(R0),R1 ; Get the FCB address POPR #^M% BGEQ 20$ ; Must be system addressi6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return errorS$ TSTW FCB$W_REFCNT(R1) ; File open? BEQL 20$ ; No, error3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too bigo FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quitc& MOVL FCB$L_WLFL(R1),R1 ; Get any wcb7 CMPL R5,WCB$L_ORGUCB(R1) ; File must be on our devicee! BNEQ 50$ ; Other device, quito3 BBC #WCB$V_COMPLETE,- ; Must be completely mapped  WCB$B_ACCESS(R1),20$! MOVL UCB$L_VCB(R5),R1 ; Get VCBf. TSTW VCB$W_RVN(R1) ; Relative volume number+ BEQL 70$ ; Branch if a not a volume setsE MOVZWL #SS$_NOTVOLSET,R0 ; File watchpoint on volumeset not allowede BRW 130$eC20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mappedS BRW 130$L-30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameterP BRW 130$ ,40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$ 750$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our device BRW 130$C@60$: BBS #LDWATCHPT_V_NOLBN,- ; Check for non-transfer function LDWATCHPT_W_FLAGS(R0),70$S3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too big0 UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quit 070$: MOVW LDWATCHPT_W_ACTION(R0),R1 ; Get action, CMPW R1,#WATCH_ACTION_MAX ; Legal action ? BGTRU 30$ ; No2 IFPRIV CMKRNL,80$ ; Sufficient to do everything- CMPW R1,#WATCH_ACTION_CRASH ; Crash system?s BEQL 120$ ; Yes, no priv9 CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device?f& BNEQ 110$ ; No, action not allowed;cC; We charge seperately for every packet. We need to do this becauseaG; EXE$DEBIT_BYTCNT_BYTLM_NW rounds the size up to 64 bytes. If we wouldH; charge for it in one chunk we would get problems crediting the buffers!; which we need to do one by one.d;s(80$: PUSHL R0 ; Save, destroyed later4 MOVL #LDWATCHENT_K_LENGTH,R1 ; Bytecount to charge- ADDL2 R1,R9 ; Keep track of total chargeds. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf leftA ADDL2 #4,SP ; Adjust stack/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quota BRB 130$xC90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input entryA DECL R2 ; Next packetV BLEQ 100$ BRW 10$7100$: MOVQ R10,R0 ; Get buffer address and bytecount  BSBW LD_GETBUF ; Get buffer BLBC R0,130$ ; Error, JMP G^EXE$QIODRVPKT ; Finish in start I/O:110$: MOVZWL #SS$_NOPRIV,R0 ; Priv or ownership required BRB 130$c5120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv requiredB&130$: TSTL R9 ; Any BYTCNT charged? BEQL 140$ ; No PUSHL R0 ; Save status! MOVL R9,R0 ; Amount to return- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota! MOVL (SP)+,R0 ; Restore statusp)140$: JMP G^EXE$ABORTIO ; Abort the I/OU B9 .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routineo;+++3; LD_DISABLE_WATCH, Disable watchpoints FDT routinee;; Functional description:n; 0; This is the FDT routine to disable watchpoints;B ; Inputs:0;M; R0-R2 - scratch registerse.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)i.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)l*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;e ; Outputs:;k;; The routine must preserve all registers except R0-R2, andd ; R9-R11. ; ;--n" UNIVERSAL_SYMBOL LD_DISABLE_WATCH;LD_DISABLE_WATCH:$ CLRL IRP$L_OBCNT(R3) ; Zero count( MOVL P2(AP),R1 ; Something specified? BEQL 10$ ; Remove all:# MOVL R1,IRP$L_OBCNT(R3) ; Save it 2 MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length& MOVL P1(AP),R0 ; Get buffer address4 JSB G^EXE$WRITECHK ; Check buffer for read access$ BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Errora010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O$20$: JMP G^EXE$ABORTIO ; Abort I/O (5 .SBTTL LD_GET_WATCH, Get watchpoint info FDT routine,;+++/; LD_GET_WATCH, Get watchpoint info FDT routinee;s; Functional description:I;;8; This routine retrieves info about current watchpoints,(; as well as the suspended process list.;, ; Inputs:a;s; R0-R2 - scratch registersP.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers.; P1(AP) - Address of watchpt structure buffer*; P2(AP) - Size in bytes of watchpt buffer;C ; Outputs:; ;; The routine must preserve all registers except R0-R2, andB ; R9-R11.L;R;--E UNIVERSAL_SYMBOL LD_GET_WATCH;LD_GET_WATCH:+ BBS #LDIO_V_INQUIRE,- ; Return list size?i IRP$L_EXTEND(R3),10$& MOVL P1(AP),R0 ; Get buffer address% MOVL P2(AP),R1 ; Get buffer lengtht4 JSB G^EXE$READCHK ; Check buffer for write access% BSBW LD_GETBUF1 ; Get systembuffers BLBC R0,20$ ; Errorr010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O rH .SBTTL LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;+++B; LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;p; Functional description:t;e:; This routine resumes threads which were suspended when a; 'suspend' watchpoint was hit;x ; Inputs:E;A; R0-R2 - scratch registersA.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block);.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB >3$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1Uf"(channel control block)t*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;T ; Outputs:;;; The routine must preserve all registers except R0-R2, andr ; R9-R11.d; ;--D! UNIVERSAL_SYMBOL LD_RESUME_WATCH;LD_RESUME_WATCH:R& MOVL P1(AP),R0 ; Get buffer address) MOVL P2(AP),R1 ; Get number of entriesB2 MOVL R1,IRP$L_OBCNT(R3) ; Save number of entries BEQL 10$ ; Nothing there2 MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length4 JSB G^EXE$WRITECHK ; Check buffer for read access$ BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; Errort010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O R= .SBTTL LD_GETBUF + LD_GETBUF1, Get and fill temporary buffern;++++; LD_GETBUF, Get and fill temporary bufferS#; LD_GETBUF1, Get temporary bufferR;F; Functional description: ;UA; This routine allocates a buffer to use to get/send data to/fromcC; start I/O routines. The userbuffer data will be copied (LD_GETBUF ; only).;J ; Inputs:O;O; R0 - user buffer address; R1 - user buffer bytecount.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)d.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)a*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;o ; R0 - status ; R2 - sytem buffer addressn$; IRP$L_SVAPTE - systembuffer addres;t;; The routine must preserve all registers except R0-R2, ando ; R9-R11.i;--- .ENABLE LSB UNIVERSAL_SYMBOL LD_GETBUF1 ;LD_GETBUF1:( CLRL -(SP) ; Flag not to fill buffer BRB 10$ UNIVERSAL_SYMBOL LD_GETBUFS ;LD_GETBUF:T( MOVZBL #1,-(SP) ; Flag to fill buffer210$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^Mr7 JSB G^EXE$DEBIT_BYTCNT_ALO ; Allocate a system bufferI$ BLBC R0,30$ ; Any quota errors ? POPR #^M7 MOVL R2,IRP$L_SVAPTE(R3) ; Save system buffer addresst4 MOVW R1,IRP$W_BOFF(R3) ; Save system buffer length2 MOVAB 12(R2),(R2) ; Insert address of data area0 MOVL R0,4(R2) ; Insert address of user buffer$ BLBC (SP),20$ ; Branch if no fill, PUSHR #^M ; Destroyed by MOVCH MOVC3 IRP$W_BCNT(R3),(R0),12(R2) ; Copy data from user- to systembuffer POPR #^M#20$: MOVZWL #SS$_NORMAL,R0 ; Okayp BRB 40$%30$: POPR #^M ; Error return"40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSBO q7 .SBTTL LD_MAKE_FILE_RESNAM, Form private resource nameb;+++2; LD_MAKE_FILE_RESNAM, Form private resource name;-; Functional description:l;n@; Form resourcename for lock to coordinate clusterwide access to&; logical disk file or replaced device;i+; For a file this resourcename consists of:e;o; 9 bytes: '$LOGDISK_'e); 1 byte: 1 for private mounted volume, ; 2 for system wide; 12 bytes: volume lockname ; 3 bytes: 02; 1 word: FIDL; 1 word: SEQ_; 1 word: RVNB;P; For a device this will be:;o; 9 bytes: '$LOGDISK_', ; 1 byte: 0&; 1 byte: physical devicename length!; 20 bytes: alloclass devicenameF;S ; Inputs:W;S; R3 IRP addressS; R4 PCB addressP; R5 UCB address &; R9 FCB address (connect 'file' only);e; Outputs:None.u;i3; Implicit outputs: Resource name in UCB is written-;t;---% UNIVERSAL_SYMBOL LD_MAKE_FILE_RESNAM;LD_MAKE_FILE_RESNAM:e PUSHR #^MoC MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource namen! MOVZBL #31,(R1)+ ; Fill length " MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_" MOVL #^A/DISK/,(R1)+t MOVB #^A/_/,(R1)+( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; FillerP PUSHL R4 ; Save PCBc# PUSHL R1 ; Save current pointeru- CLRB (R1)+ ; Length byte, filled in lateri+ JSB G^SCH$IOLOCKR ; Lock the IO databasey CLRQ (R1) ; Clear buffer CLRQ 8(R1)_ CLRL 16(R1) MOVZBL #20,R0 ; Buffer sizen! MOVZBL #1,R4 ; DVI$_ALLDEVNAMG PUSHL R5 ; Save UCBB5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCBA1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename  MOVL (SP)+,R5 ; Restore UCB" MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB. JSB G^SCH$IOUNLOCK ; Unlock the IO database BRW 50$+10$: MOVB #1,R2 ; Assume private mountedb6 MOVL UCB$L_LD_PDUCB(R5),R0 ; Get physical device UCB( TSTL UCB$L_PID(R0) ; Private mounted? BNEQ 20$ ; Yes) INCB R2 ; System mounted, make it '2'S20$: MOVB R2,(R1)+! MOVL UCB$L_VCB(R0),R2 ; Get VCBr. TSTW VCB$W_RVN(R2) ; Relative volume number) BEQL 30$ ; Branch if not a volume sets+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addressn BEQL 30$ ; Not there5 MOVAB RVT$T_VLSLCKNAM(R0),R2 ; Use this as locknameo BRB 40$730$: MOVAB VCB$T_VOLCKNAM(R2),R2 ; Lock name from hereA040$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+ MOVL (R2)+,(R1)+p& CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+/ MOVW FCB$W_FID_NUM(R9),(R1)+ ; Insert File IDL$ MOVW FCB$W_FID_SEQ(R9),(R1)+ ; SEQ$ MOVW FCB$W_FID_RVN(R9),(R1)+ ; RVN50$: POPR #^M RSB ; C'est toute A .SBTTL LD_MAKE_DEV_RESNAM, Form private resource name for devicel;+++<; LD_MAKE_DEV_RESNAM, Form private resource name for device;; Functional description:;;r@; Form resourcename for lock to coordinate clusterwide access to; logical disk device:;R.; The resource name is constructed as follows:;_; 9 bytes: '$LOGDISK_' ; 1 byte: 0 ; 1 byte: LD devicename length!; 20 bytes: alloclass devicename3;G ; Inputs:S;2; R3 IRP addressh; R4 PCB addressK; R5 UCB address!; ; Outputs:None.e;p3; Implicit outputs: Resource name in UCB is written;K;---$ UNIVERSAL_SYMBOL LD_MAKE_DEV_RESNAM;LD_MAKE_DEV_RESNAM: PUSHR #^MCB MOVAB UCB$T_LD_DEV_RESNAM(R5),R1 ; Setup pointer to resource name! MOVZBL #31,(R1)+ ; Fill lengthp" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"E MOVL #^A/DISK/,(R1)+ MOVB #^A/_/,(R1)+ CLRB (R1)+ ; Filler; PUSHL R4 ; Save PCBF# PUSHL R1 ; Save current pointer - CLRB (R1)+ ; Length byte, filled in later1+ JSB G^SCH$IOLOCKR ; Lock the IO databaseo CLRQ (R1) ; Clear buffer CLRQ 8(R1)R CLRL 16(R1) MOVZBL #20,R0 ; Buffer size)! MOVZBL #1,R4 ; DVI$_ALLDEVNAM 1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicenamei" MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB. JSB G^SCH$IOUNLOCK ; Unlock the IO database POPR #^M RSB ; C'est toute Y .PAGE3 .SBTTL LD_FDT_DSE, Data secutiry erase fdt routine ;+++.; LD_FDT_DSE, Data secutiry erase fdt routine;; Functional description:r;t@; This is the FDT routine for the Data Security Erase operation.C; The byte count (P2) is stored in IRP$L_BCNT. The starting logicalr?; block (P3) is stored in IRP$L_MEDIA. Control is transfered tocD; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start; I/O routine.;i ; Inputs:;a; R0-R2 - scratch registersh.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block) .; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block) *; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter; P2(AP) - Byte countH!; P3(AP) - Starting logical block ; ; Outputs:;l; IRP$L_BCNT(R3) - Byte countt*; IRP$L_MEDIA(R3) - Starting logical block; ;; The routine must preserve all registers except R0-R2, andL ; R9-R11.L;);--  UNIVERSAL_SYMBOL LD_FDT_DSE2;LD_FDT_DSE: ; Data security erase FDT routine5 MOVL P2(AP),IRP$L_BCNT(R3) ; Setup erase byte count)8 MOVL P3(AP),IRP$L_MEDIA(R3) ; Setup erase starting LBN0 JMP G^EXE$QIODRVPKT ; Send request to STARTIO TC .SBTTL LD_FDT_SHAD_WCHECK - Check write to shadow member for privsp;+++; D; LD_FDT_SHAD_RWCHECK - Check read/write to shadow mbr for privilege;u; Functional Description:V;SD; Allow only processes with SYS privilege to perform WRITES to0; Host Based Shadowing shadow set members.;C ; In?$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U\"puts:B;C; R3 IRP addressW$; R5 UCB address (member);E; Implicit inputs: None.;U; Outputs:None.Q;R; Implicit outputs: None.$; ; Condition codes:;MH; SS$_ILLIOFUNC - I/O directed to shadow set member by a process5; that doesn't have sys priv.A;---$ UNIVERSAL_SYMBOL LD_FDT_SHAD_WCHECK;LD_FDT_SHAD_WCHECK:4 BBC #DEV$V_SHD,- ; If this device is not a shadow- UCB$L_DEVCHAR2(R5),10$ ; set member, quit ) MOVL IRP$L_ARB(R3),R0 ; Get ARB addressr" BEQL 20$ ; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 329 BBC #PRV$V_SYSPRV,ARB$Q_PRIV(R0),20$; No SYSPRV, illegala&10$: RSB ; Continue FDT processing120$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status-+ JMP G^EXE$ABORTIO ; Complete I/O requesti , .SBTTL LD_FDT_CRESHAD - CRESHAD FDT routine, .SBTTL LD_FDT_REMSHAD - REMSHAD FDT routine;+++;t&; LD_FDT_CRESHAD - CRESHAD FDT routine&; LD_FDT_REMSHAD - REMSHAD FDT routine;2; Functional Description:s;UB; Dispatch CRESHAD and REMSHAD requests to shadowing driver.;h ; Inputs: ; ; R3 IRP addressf$; R5 UCB address (member);N-; Implicit inputs: Dispatch vector filled in.i;t; Outputs:None.0; ; Implicit outputs: None.J;G; Condition codes:; 5; SS$_ILLIOFUNC - Dispatch vector not set up.oD; SS$_DEVNOTSHR - Shadowset exists already somewhere in the cluster.;f;;; The routine must preserve all registers except R0-R2, andd ; R9-R11.C;--- UNIVERSAL_SYMBOL LD_FDT_CRESHAD&;LD_FDT_CRESHAD: ; ----> IO$_CRESHAD UNIVERSAL_SYMBOL LD_FDT_REMSHAD&;LD_FDT_REMSHAD: ; ----> IO$_REMSHAD. MOVL G^EXE$GL_HBS_PTR,R0 ; Shadow Dispatcher' BGEQ 10$ ; Illegal if not filled inK! JMP (R0) ; Jump to dispatcher1110$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error status + JMP G^EXE$ABORTIO ; Complete I/O requestE ): .SBTTL LD_CONTROL_INIT, Controller initialization routine;+++8; LD_CONTROL_INIT, Readies controller for I/O operations;e; Functional description:(;,<; The operating system calls this routine in 3 places:; !; at system startup)3; during driver loading and reloading 4; during recovery from a power failure;oH; This routine is a NOP for driver reloading and power failure recovery.<; For system startup and driver loading it allocates a CDDB.; ;a ; Inputs: ;bA; R4 - address of the CSR (controller status register)O;; R5 - address of the IDB (interrupt data block)08; R6 - address of the DDB (device data block)<; R8 - address of the CRB (channel request block);a ; Outputs:;B7; The routine must preserve all registers except R0-R3.;W;---! UNIVERSAL_SYMBOL LD_CONTROL_INIT;,;LD_CONTROL_INIT: ; Initialize controller6 MOVB #SPL$C_IOLOCK8,- ; Init device spin lock index. CRB$B_FLCK(R8) PUSHQ R4 ; Save CSR & IDB_ CLRL R4 ; Init errorflag1 MOVZWL IDB$W_UNITS(R5),R0 ; Get number of unitsi BRB 20$;0H; Check for the correct UCB size if we're reloaded. If the size differs,/; then turn the unit offline to avoid problems.M;L,10$: MOVL IDB$L_UCBLST(R5)[R0],R1 ; Get UCB BEQL 20$ ; Not present6 CMPW UCB$W_SIZE(R1),#UCB$K_LD_UCBLEN ; Expected size? BEQL 20$ ; Yes;oH; Something's seriously wrong here. The UCB size of the currently loadedJ; driver is not the same as the new driver. We may not continue, but sinceE; the controllerinit routine does not return an exit status we cannotr?; signal it to SYSGEN. What we do is set the unit offline here.h; & CLRL CRB$L_AUXSTRUC(R8) ; Flag error8 BICW2 #UCB$M_ONLINE,UCB$W_STS(R1) ; Switch unit offline1 BICW2 #DEV$M_MSCP,- ; Zero this bit. Otherwisep2 UCB$L_DEVCHAR2(R1) ; 'show device' has problems ; if the CDDB is 0. ) MOVZBL #1,R4 ; Flag something's wrongd#20$: SOBGEQ R0,10$ ; Next device $ TSTL R4 ; Did we have a problem? BNEQ 30$ ; Yes, quit1 TSTL CRB$L_AUXSTRUC(R8) ; Check if CDDB present " BEQL 40$ ; Branch if not there#30$: POPQ R4 ; Restore registers & RSB ; Otherwise, return to caller; /; Create fork thread to finish controller init.Q;R 40$: MOVL R6,R4 ; Restore DDB MOVL R8,R5 ; Fork with CRB$ PUSHAB 30$ ; Fake return address FORK ;Z; Get pool for CDDB.;K) MOVZWL #CDDB$K_LENGTH,R1 ; Size of CDDBy, JSB G^EXE$ALONONPAGED ; Allocate some pool! BLBC R0,50$ ; Branch if error;* PUSHR #^M ; Save registers./ MOVC5 #0,(SP),#0,R1,(R2) ; Zero entire block.e2 POPR #^M ; Restore saved registers.;#; Initialize necessary CDDB fields. ;c MOVW R1,CDDB$W_SIZE(R2) ; Size' ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1t MOVW #>,- CDDB$B_TYPE(R2)e& MOVL R5,CDDB$L_CRB(R2) ; CRB address& MOVL R4,CDDB$L_DDB(R2) ; DDB address8 MOVL R2,CRB$L_AUXSTRUC(R5) ; Save CDDB address in CRB.! MOVL DDB$L_UCB(R4),R5 ; Get UCBd/ BISW2 #DEV$M_MSCP,- ; Set mscp bit. Now safee0 UCB$L_DEVCHAR2(R5) ; because we've got a CDDB6 MOVL R2,UCB$L_CDDB(R5) ; Also in UCB (If first load). MOVAL CDDB$L_CDRPQFL(R2),- ; Init CDRP queue CDDB$L_CDRPQFL(R2) MOVAL CDDB$L_CDRPQFL(R2),-t CDDB$L_CDRPQBL(R2)7 MOVAL CDDB$L_RSTRTQFL(R2),- ; Init restart CDRP queue  CDDB$L_RSTRTQFL(R2)t MOVAL CDDB$L_RSTRTQFL(R2),- CDDB$L_RSTRTQBL(R2)S, MOVL G^CLU$GL_ALLOCLS,- ; Allocation class CDDB$L_ALLOCLS(R2). MOVW CDDB$L_ALLOCLS(R2),- ; Allocation class CDDB$Q_CNTRLID(R2)% MOVW UCB$W_UNIT(R5),- ; Unit numberR CDDB$Q_CNTRLID+2(R2)& MOVL UCB$L_MEDIA_ID(R5),- ; Media_id CDDB$Q_CNTRLID+4(R2)$ BRB 60$ ; fails then second load ; needs to store this.%50$: MOVL DDB$L_UCB(R4),R5 ; Get UCBf1 BICW2 #DEV$M_MSCP,- ; Zero this bit. OtherwiseR2 UCB$L_DEVCHAR2(R5) ; 'show device' has problems ; if the CDDB is 0.T60$: RSB ; Returnc i0 .SBTTL LD_CLONED_UCB, Cloned UCB initialization;+++*; LD_CLONED_UCB, Cloned UCB initialization;i; Functional description:t;vC; This routine is called by the $ASSIGN System Service to allow thesA; driver to initialize the cloned UCB. The driver is called withd; process context.;q ; Inputs:); R0 = SS$_NORMAL; R2 = address of cloned UCB ; R3 = DDT addressh; R4 = PCB addressl#; R5 = address of the template UCBn; IPL = ASTDEL; ; Outputs:;e; R5 = address of cloned UCB8; Destroyed = R5; ;--  UNIVERSAL_SYMBOL LD_CLONED_UCB-;LD_CLONED_UCB:s! MOVL R2,R5 ; Copy UCB addressu7 MOVL UCB$L_CPID(R5),UCB$L_LD_CPID(R5); Save charge pid CLRL UCB$L_CPID(R5) ; Zero ID/ MOVW UCB$W_CHARGE(R5),- ; Save charged amount  UCB$W_LD_CHARGE(R5)I% CLRW UCB$W_CHARGE(R5) ; Zero chargeC; BISW2 #UCB$M_NOCNVRT,UCB$W_DEVSTS(R5) ; Inhibit logical toE ; physical xlation. ; H; Fall through to the LD_UNIT_INIT routine to initialize the cloned UCB.;K ;1 .SBTTL LD_UNIT_INIT, Unit initialization routine;;+++/; LD_UNIT_INIT, Readies unit for I/O operationsP;E; Functional description:s;tA; The operating system calls this routine after calling theS*; controller initialization routine:;F!; at system startup,%; during driver loadingT4; during recovery from a power failure(; during initialization of a cloned UCB;h;a ; Inputs:d;aA; R3 - address of the CSR (controller status register)9; R5 - address of the UCB (unit control block)e;o7; IPL = IPL$_ASTDEL (jump/flow into from LD_CLONED_UCB) <; Note: If entered at this ipl, code now temporarily raises'; IPL to IPL$_RESCHED during the FORK.I@; = IPL$_POWER (powerfail recovery and unit initialization);i ; Outputs:;s7; The routine must preserve all registers except R0-R1.s"; R0 = status (used only on alpha);s;--- UNIVERSAL_SYMBOL LD_UNIT_INIT$;LD_UNIT_INIT: ; Initialize Unit) MOVL UCB$L_CRB(R5),R0 ; Get CRB addressM< TSTL CRB$L_AUXSTRUC(R0) ; Get CDDB address out of the CRB. BNEQ 10$ ; OkayM@ BICW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Mark cloned unit as offline:10$: BBS #UCB$V_POWER,UCB$L_STS(R5),20$ ; Did power fail ?6 MOVAL UCB$L_LD_AIOFL(R5),- ; Init the Act. I/O queue UCB$L_LD_AIOFL(R5) MOVAL UCB$L_LD_AIOFL(R5),-  UCB$L_LD_AIOBL(R5)? MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Init trace mutex wait queue; UCB$L_LD_TRCMUTEXQFL(R5)! MOVAL UCB$L_LD_TRCMUTEXQFL(R5),-O UCB$L_LD_TRCMUTEXQBL(R5)= MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Init trace data wait queuee UCB$L_LD_TRCWAITQFL(R5)S MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5)r2 MOVAL UCB$L@K$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"_LD_WATCHQFL(R5),- ; Init watch queue UCB$L_LD_WATCHQFL(R5)o MOVAL UCB$L_LD_WATCHQFL(R5),- UCB$L_LD_WATCHQBL(R5)i< MOVAL UCB$L_LD_WATCHPNDQFL(R5),- ; Init watch pending queue UCB$L_LD_WATCHPNDQFL(R5)! MOVAL UCB$L_LD_WATCHPNDQFL(R5),-  UCB$L_LD_WATCHPNDQBL(R5)- CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yetd8 MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mutex' CLRW UCB$W_LD_FLAGS(R5) ; Clear flagsa; ; Setup UCB CDDB field.O;n&20$: SAVIPL -(SP) ; Get current IPL> CMPL (SP),#IPL$_RESCHED ; Compare to minimum Fork ipl needed0 BGEQ 25$ ; Don't change IPL if not necessary% SETIPL #IPL$_RESCHED,- ; Raise IPL  ENVIRON=UNIPROCESSOR)25$: PUSHAB 60$ ; Address to return ton) FORK ; Fork to allow controller initE! ; routine to complete first:) MOVL UCB$L_CRB(R5),R0 ; Get CRB address> MOVL CRB$L_AUXSTRUC(R0),- ; Get CDDB address out of the CRB. UCB$L_CDDB(R5) BEQL 40$ ; Did not get one@ MOVL R5,UCB$L_DP_ALTUCB(R5) ; Point to ourself because CDP bit( ; is set (to prevent MSCP serving)( TSTW UCB$W_UNIT(R5) ; Is this unit 0? BNEQ 30$ ; BR if it isI2 CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit;18; Setup MSCP stuff since some drivers need it (STdriver);R.30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5)7 BISW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Switch unit onlineS-40$: MOVZWL #SS$_NORMAL,R0 ; Assume successI/ BBS #UCB$V_ONLINE,UCB$W_STS(R5),50$ ; On-line?t+ MOVZWL #SS$_DEVOFFLINE,R0 ; Flag off-line:50$: RSBI60$: SETIPL (SP)+,ENVIRON=UNIPROCESSOR ; Restore IPL to value before fork0" MOVZWL #SS$_NORMAL,R0 ; Success RSB A/ .SBTTL LD_DRV_UNLOAD, driver unloading routiner;+++); LD_DRV_UNLOAD, Driver unloading routineM;o; Functional description:-; 8; The operating system calls this routine after a SYSGEN(; RELOAD command, IPL wil be IPL$_POWER.;a=; An eventually allocated trace- or watchpoint buffer will bea; returned to pool.;9 ; Inputs:O;S; R6 - Address of DDB ; R10 - Address of DPT; ; Outputs:; ;eI; Due to a bug in SYSGEN we will NOT get the DDB address in R6, but zero.:-; Byebye VAX!! (This has been fixed in V6.0).i1; Because of this we will find the DDB ourselves.:;%; This routine may use all registers.;--- UNIVERSAL_SYMBOL LD_DRV_UNLOAD#;LD_DRV_UNLOAD: ; Unload driverd .IF NDF V6  BSBW FIND_DDB ; Get the DDB .ENDC/ MOVL DDB$L_UCB(R6),R5 ; Get first UCB addressl) MOVL UCB$L_CRB(R5),R7 ; Get CRB addresst CLRL R4 ; No PCB610$: BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffers3( MOVL UCB$L_LINK(R5),R5 ; Try next unit BNEQ 10$ ; It's therem MOVZWL #SS$_NORMAL,R0 RSB;n .IF NDF V64 FIND_DDB: ;AD; Not many checks needed as SYSGEN already verified our existance...;t4 MOVAB DPT$T_NAME(R10),R9 ; Get addr of driver name, MOVZBL (R9)+,R8 ; Get size of driver name< MOVAL G^IOC$GL_DEVLIST,R6 ; Get address of device listhead310$: MOVL DDB$L_LINK(R6),R6 ; Get addr of next ddb 6 MOVAB DDB$T_DRVNAME(R6),R1 ; Get addr of driver name, MOVZBL (R1)+,R0 ; Get size of driver name0 CMPC5 R0,(R1),#0,R8,(R9) ; Driver names match? BNEQ 10$ ; Br if not( RSB ; Return with DDB address in R6 .ENDC )# .SBTTL LD_START, Start I/O routineG;+++2; LD_START - Start a transmit or receive operation; ; Functional description:,; ;R ; Inputs:t;9; R3 - address of the IRP (I/O request packet)P9; R5 - address of the UCB (unit control block)$; ; Outputs:; F; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredo>; R1 - 2nd longword of I/O status: device-dependent;2D; The routine must preserve all registers except R0-R2 and R4.;_;--- UNIVERSAL_SYMBOL LD_START&;LD_START: ; Process an I/O packet;sG; We will clear the BSY flag here so that if we are stalled in LD_TRACEH; other threads may start already, and won't be queued to our UCB. SinceH; we don't go through normal I/O completion (REQCOM) these I/O's will beI; hanging there for the rest of the system's life. LD_TRACE takes care of ; multiple threads.y;b5 BICW2 #UCB$M_BSY,UCB$W_STS(R5) ; Clear the busy flagI" EXTZV #IRP$V_FCODE,#IRP$S_FCODE,-0 IRP$W_FUNC(R3),R4 ; Extract the function code4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch;e(; Functions allowed with inactive device;e5 CMPB R4,#IO$_LD_CONTROL ; Private control function?, BNEQ 10$ ; Nor6 BRW LD_START_CONTROL ; Handle our own function codes110$: BSBW LD_CHECK_WATCH ; Check for watchpointe8 BLBS IRP$L_EXTEND(R3),20$ ; Action done by CHECK_WATCH7 BBS #UCB$V_LD_CONSTS,- ; Device in DISCONNECT state ?  UCB$W_LD_FLAGS(R5),30$7 MOVZWL #SS$_DEVINACT,R0 ; Set status to dev. inactive3&20$: BRW LD_DONE ; Complete the I/O830$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get UCB of phys. disk4 BBC #UCB$V_ONLINE,UCB$W_STS(R0),40$ ; Unit offline?A BBS #IRP$V_PHYSIO,IRP$W_STS(R3),50$ ; If set, phys. I/O functionM7 BBS #UCB$V_VALID,UCB$W_STS(R5),50$ ; If volume valid ?,7 MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalido& BRW LD_DONE ; And complete the I/O;l<; Someone pulled the device underneath us (it went offline).?; This may happen if we replaced a raid virtual unit with an LD?; device. Subsequent unbinding of the raid array may take place;<; without us knowing about it. Return SS$_DRVERR to the userA; (SS$_DEVOFFLINE would kick in mount verification, which i don't #; want since this is a fatal error. ;R840$: MOVZWL #SS$_DRVERR,R0 ; Return drive error status& BRW LD_DONE ; And complete the I/O4 ASSUME IRP$S_FCODE LE 7 ; Allow byte mode dispatch=50$: DISPATCH R4,TYPE=B,<- ; Dispatch according to functiono ,- ; ^X00" ,- ; ^X01 ,- ; ^X02" ,- ; ^X04$ ,- ; ^X08( ,-; ^X0A( ,-; ^X0B& ,- ; ^X0C( ,- ; ^X11 > ; ^X15B: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code& BRB LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PACKACK ;LD_PACKACK:8 BISW2 #UCB$M_VALID,UCB$W_STS(R5) ; Set volume valid bit' BRB LD_NORMAL ; And complete the I/O  UNIVERSAL_SYMBOL LD_AVAILABLE;LD_AVAILABLE: UNIVERSAL_SYMBOL LD_UNLOAD ;LD_UNLOAD:e> BICW2 #UCB$M_VALID,UCB$W_STS(R5) ; Clear the volume valid bit# ; Fall through into LD_NORMALm UNIVERSAL_SYMBOL LD_DRVCLR ;LD_DRVCLR:s UNIVERSAL_SYMBOL LD_SEEK ;LD_SEEK:e UNIVERSAL_SYMBOL LD_NOP;LD_NOP: UNIVERSAL_SYMBOL LD_NORMALn ;LD_NORMAL:O+ BBC #UCB$V_LD_REPLACE,- ; Replaced drive?- UCB$W_LD_FLAGS(R5),10$4 BRW LD_PROCESS_IO ; More to do for replaced drive10$: CLRL R2 ; Dummy LBNc0 MOVZWL #SS$_NORMAL,R0 ; Set status to success UNIVERSAL_SYMBOL LD_DONE9.;LD_DONE: ; Driver processing is finished.* CLRL R1 ; Clear second status longword LD_DONE1: ' BSBW LD_SAVE_TRACE ; Save trace data F BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread REQCOM ; Complete I/O.s o;+++K; The IRP is now setup to be transferred to the physical disk. The phys.K; disk UCB is taken and it's FLCK is compared to our FLCK. If our FLCK iseK; lower, we raise IPL to synchronize. We queue the IRP to the Phys. diskr; driver, and simply return.K; The phys. disk driver will call I/O completion to get rid of the IRP etc. K; Because we do not know if the Phys. disk requires the Block nr in phys. ,pK; or Logical format, we first convert it to Logical, because that is whattK; we need, add the Starting LBN of the LD File, and go through the conver-y,; sion process again, like in the FDT rtn's.;--- UNIVERSAL_SYMBOL LD_DSE;LD_DSE:2 BBC #UCB$V_LD_NODSE,- ; Check for DSE support on) UCB$W_LD_FLAGS(R5),- ; physical drivew LD_TRANSFER_W2' MOVZWL #SS$_NORMAL,R0 ; Fake success$; ASHL #16,IRP$L_BCNT(R3),R1 ; Move lower half of bytecountt8 BISL2 R1,R0 ; Combine status and lower part of count; MOVZWL IRP$L_BCNT+2(R3),R1 ; Move upper half of bytecount BRW LD_DONE1 UNIVERSAL_SYMBOL LD_TRANSFER_WS;LD_TRANSFER_W:4 BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFERH9 MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock statusF BRW LD_DONE UNIVERSAL_SYMBOL LD_TRANSFERD ;LD_TRANSFER: * MOVL IRP$L_MEDIA(R3),R2 ; Save for later1 BBS #UCB$V_LD_VIRTUAL,- ; ChecAG+$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U7"k for virtual I/OH UCB$W_LD_FLAGS(R5),20$: BBC #IRP$V_PHYSIO,IRP$W_STS(R3),10$ ; Is it a phys. I/O ?< BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ?, BSBW LD_CNVRTOLOG ; Convert to a log. I/O;10$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page counto. ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust8 CMPL R0,UCB$L_MAXBLOCK(R5) ; Does it fit on our disk ?" BLEQU LD_PROCESS_IO ; Yep, skip BRW 30$ ; Return error;20$: ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page countH. ADDL2 IRP$L_MEDIA(R3),R0 ; Calculate the end DECL R0 ; Adjust$ MOVL UCB$L_LD_FCB(R5),R1 ; Get FCB5 CMPL R0,FCB$L_EFBLK(R1) ; Does it fit on our disk ?r" BLEQU LD_PROCESS_IO ; Yep, skip230$: MOVZWL #SS$_ILLBLKNUM,R0 ; Set return status& BRW LD_DONE ; And complete the I/O UNIVERSAL_SYMBOL LD_PROCESS_IO ;LD_PROCESS_IO:2 MOVL IRP$L_BCNT(R3),R1 ; Set original byte count PUSHR #^ML MOVL R1,R6 ; Copy in to R6> ADDL3 #1,IRP$L_MEDIA(R3),R7 ; Copy LBN -> R7, VBN start at 1;tF; Allocate and initialize an LDIOB to describe the I/O and the connec-I; tion to the physical disk. Insert the LDIOB in the active UCB I/O queue ; . BSBW LD_ALLO_LDIOB ; Allocate a LIODB block BLBS R0,5$ ; Oko POPR #^M BRW LD_DONE ; Quit on errort5$: MOVL R2,R4= MOVW #SS$_NORMAL,LDIOB_W_IOST(R4) ; Initialize return status : MOVL IRP$L_PID(R3),LDIOB_L_PID(R4) ; Set PID val in LDIOB0 MOVL R3,LDIOB_L_IRP(R4) ; Set IRP adr in LDIOB, CLRQ LDIOB_Q_ST_TIME(R4) ; Zero start time. CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time* TSTL UCB$L_LD_TRCBUF(R5) ; Trace active? BEQL 10$ ; No, save overhead- MOVL IRP$L_MEDIA(R3),- ; Copy Media address- LDIOB_L_MEDIA(R4)D6 MOVL IRP$L_BCNT(R3),LDIOB_L_BCNT(R4) ; Copy Bytecount9 MOVW IRP$W_FUNC(R3),LDIOB_W_FUNC(R4) ; Copy Functioncode. READ_SYSTIME LDIOB_Q_ST_TIME(R4) ; Start time710$: INSQUE LDIOB_L_QFL(R4),- ; Insert at end of queue  @UCB$L_LD_AIOBL(R5)0 MOVL UCB$L_LD_WCB(R5),R2 ; Get our WCB address;rF; For every mapped I/O segment, allocate and initialize a forward IRP.F; The transfer parameters are copied into the forward IRP, and the IRP; is queued to the LDIOB.$;I ; R2 - WCB; R3 - IRP (org) ; R4 - LDIOB; R5 - UCB (org); R6 - Byte counte; R7 - Segment block numberi;o30$: PUSHR #^Mv1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/On UCB$W_LD_FLAGS(R5),32$3 BBC #UCB$V_LD_REPLACE,- ; Skip mapping if replacee UCB$W_LD_FLAGS(R5),35$032$: MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the PDUCB! CLRL R2 ; Remaining bytecounte% MOVL IRP$L_MEDIA(R3),R1 ; First LBN) BRB 40$+35$: MOVL R7,R0 ; Restore segment numbers" MOVL R6,R1 ; Restore bytecount" BSBW LD_MAPVBLK ; Map a section+ BLBS R0,40$ ; Total map failure = FATALF BUG_CHECK INCONSTATE,FATAL:;BB; R5 now contains the address of the UCB on which this part of the; file is located.;t.40$: MOVL R5,R8 ; Save physical UCB address* MOVL 12(SP),R5 ; Recover LD UCB address MOVL R3,R0 ; Copy IRP to R0r. BSBW LD_ALLO_FWIRP ; Allocate a FWIRP block1 SUBL3 R2,R6,IRP$L_BCNT(R3) ; Set the byte countR> MOVAB LD_COMPLETE,IRP$L_PID(R3) ; Initialize callback address9 MOVW IRP$W_BOFF(R0),IRP$W_BOFF(R3) ; Set the byte offset 4 MOVB IRP$B_PRI(R0),IRP$B_PRI(R3) ; Set the priority6 MOVW IRP$W_FUNC(R0),IRP$W_FUNC(R3) ; Get the function1 BBC #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O2 UCB$W_LD_FLAGS(R5),43$0 MOVL UCB$L_LD_WCB(R5),- ; Get our WCB address,) IRP$L_WIND(R3) ; used for Virtual I/Oe.43$: MOVL IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3)# CLRW IRP$W_STS(R3) ; Init statusB7 BBC #UCB$V_LD_REPLACE,- ; Skip status copy if replace UCB$W_LD_FLAGS(R5),45$5 MOVW IRP$W_STS(R0),IRP$W_STS(R3) ; Initialize statuss BRB 50$945$: BBC #IRP$V_FUNC,IRP$W_STS(R0),50$ ; Chk the R/W flag;2 BISW #IRP$M_FUNC,IRP$W_STS(R3) ; Set the R/W flag,50$: MOVL R2,R6 ; Copy bytecount it to R6+ BBS #UCB$V_LD_REPLACE,- ; Skip if replace  UCB$W_LD_FLAGS(R5),60$1 SUBL3 #1,R7,R2 ; Copy it to R2, VBN start at 1V4 SUBL2 IRP$L_MEDIA(R0),R2 ; Calculate buffer offset0 ASHL #2,R2,R2 ; Add offset in buffer in PTE's. ADDL3 R2,IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3)6 ASHL #-9,IRP$L_BCNT(R3),R0 ; Calculate the page count* ADDL2 R0,R7 ; Add it to the blk number060$: MOVL R5,IRP$L_LD_LDUCB(R3) ; Set the LDUCB, MOVL R4,IRP$L_LD_LDIOB(R3) ; Set the LDIOB) MOVL R1,R0 ; Set logical block number-. MOVL R1,IRP$L_MEDIA(R3) ; Assume virtual I/O PUSHL R5 ; Save LD UCB MOVL R8,R5 ; Get the PDUCB' MOVL R5,IRP$L_UCB(R3) ; Set the PDUCB 1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O  UCB$W_LD_FLAGS(R5),65$6 JSB G^IOC$CVTLOGPHY ; Convert to physical structure ; and fill IRP$L_MEDIA965$: INSQUE IRP$L_LD_FWDQFL(R3),- ; Place FWIRP in LDIOB @LDIOB_L_FWDQBL(R4)C; ADAWI #1,LDIOB_W_IRPCNT(R4) ; Increment number of FW IRPsn! MOVL (SP)+,R5 ; Restore LD UCBr;iF; Queue the I/O to the physical driver. If the device is not busy thenF; pass the IRP to the START I/O routine. If the device is busy, then!; place it in the I/O wait queue.s;3 MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock index > CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R8) ; Compare (negative index)* BLSS 70$ ; Ok, ours is larger or equal4 MOVZBL UCB$B_FLCK(R8),R2 ; Get the lowest of the 2&70$: FORKLOCK LOCK=R2,- ; Synchronize SAVIPL=-(SP),-B PRESERVE=NO PUSHL R2 ; Save lock# PUSHL R5 ; Save our UCB address - MOVL R8,R5 ; Setup Phys. disk UCB address 1 INCW UCB$W_QLEN(R5) ; Bump device queue length < BBSS #UCB$V_BSY,UCB$W_STS(R5),80$ ; Check if device is busy1 JSB G^IOC$INITIATE ; Initiate the I/O functiont BRB 90$ ; Common code flow>80$: MOVAL UCB$L_IOQFL(R5),R2 ; Get addr. of I/O queue listhd3 JSB G^EXE$INSERTIRP ; Insert IRP in device queuen&90$: MOVL (SP)+,R5 ; Restore our UCB MOVL (SP)+,R2 ; Restore locka% FORKUNLOCK LOCK=R2,- ; Release lock NEWIPL=(SP)+,-r PRESERVE=NO,- CONDITION=RESTORE POPR #^M$ TSTL R6 ; Anything left to map ? BLEQU 100$ ; No, complete, BRW 30$ ; Yes map rest100$: POPR #^Mu RSB ; And simply return e;+++0; Convert a physical block into a logical block.5; Modify IRP$L_MEDIA into LBN instead of TRK/CYL/SEC.oG; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTORl;--- UNIVERSAL_SYMBOL LD_CNVRTOLOG;LD_CNVRTOLOG: PUSHR #^M & MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C% MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get CH MULL2 R0,R4 ; R4=C*(T/C)% MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get Tt ADDL R4,R2 ; R2=C*(T/C)+T:' MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/Ti& MULL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)# MOVZBL IRP$L_MEDIA(R3),R4 ; Get SL( ADDL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)+S, MOVL R2,IRP$L_MEDIA(R3) ; Put LBN into IRP POPR #^M RSB ; Return to caller  B0 .SBTTL LD_MAPVBLK, Map virtual to logical block;++++; LD_MAPVBLK, Map virtual to logical block ;o; Functional description:O;,<; This routine is called to map a virtual block to a logical; block using a mapping window. ;$ ; Inputs:R;; R0 - Virtual block number; R1 - Number of bytes to mapB; R2 - WCB address9; R3 - address of the IRP (I/O request packet)R9; R5 - address of the UCB (unit control block)Q;R ; Output:;L,; R0 - Low bit clear = total mapping failure&; R0 - Low bit set = partial map with:+; R1 = Logical block number of first blockT ; R2 = Number of unmapped bytes; R5 = UCB of physical unit;$D; The routine must preserve all registers except R0-R2 and R4.;5;--- UNIVERSAL_SYMBOL LD_MAPVBLK ;LD_MAPVBLK:1 PUSHR #^M ; Save registersU& MOVAB -1(R0),R7 ; Save start vbn -1+ MOVL R2,R3 ; Get copy of window address;B MOVL WCB$L_ORGUCB(R2),R5 ; Get ucb of volume containing the file;tG; The window may consist of a chain of wcb segments. search through theOE; chain until we find one which is beyond the desired vbn or we reach ; the end of the chain. ; E10$: CMPL R0,WCB$L_STVBN(R3) ; Check vbn against start vbn of window_- BLSSU 20$ ; Branch if vbn precedes windowR4 MOVL R3,R2 ; Else advance to this window segment6 MOVL WCB$L_LINK(R2),R3 ; Look at next window segment- BNEQ 10$ ; Branch to look at if it exists @20$: MOVZWL WCB$W_NMAP(R2),R3 ; Get count of retrieval pointers% BEQL 40$ ; Branch if empty window 2 MOVAL WCB$L_STVBN(R2),R4 ; Point to starting vbn6 SUBL2 (R4)+,R0 ; SubtractBfe$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1Ub" starting vbn from desired- BLSSU 40$ ; Branch if vbn precedes window .IF DF V6&ASSUME WCB$L_P1_COUNT EQ WCB$L_STVBN+4ASSUME WCB$L_COUNT EQ 0aASSUME WCB$L_LBN EQ 4ASSUME WCB$B_RVN EQ 8e!ASSUME WCB$C_MAP_PTR_LENGTH EQ 12R .ENDC; G; Scan the window, subtracting the count field of each pointer from the_ ; current relative block number.;l30$: .IF DF V67 MOVL (R4)+,R9 ; Get count field of retrieval pointerU5 SUBL2 R9,R0 ; Subtract from relative block number0 .IFFg9 MOVZWL (R4)+,R1 ; Get count field of retrieval pointerE5 SUBL2 R1,R0 ; Subtract from relative block numberN .ENDC5 BLSSU 50$ ; Branch if vbn located in this pointern .IF DF V63 ADDL2 #8,R4 ; Skip lbn and rvn field of pointerM .IFF * TSTL (R4)+ ; Skip lbn field of pointer .ENDC$ SOBGTR R3,30$ ; Loop thru window ; Vbn is beyond window+40$: CLRL R8 ; Indicate no blocks mapped-G MOVL WCB$L_ORGUCB(R2),R5 ; Redirect ucb to volume containing the file BRB 70$ ; Return failure;dA; Found the retrieval pointer containing the starting vbn. R0 nowiA; contains a negative value which is the number of blocks betweenh.; the starting vbn and the end of the pointer.; 950$: MNEGL R0,R8 ; Save # blocks mapped past start vbng .IF DF V61 ADDL2 (R4)+,R9 ; First lbn beyond this pointerD( ADDL3 R9,R0,R1 ; Compute starting lbn MOVL (R4)+,R0 ; Get rvn field .IFF 1 ADDL2 (R4)+,R1 ; First lbn beyond this pointerC& ADDL2 R1,R0 ; Compute starting lbn .ENDC;oE; If the next retrieval pointer is contiguous with the one found, addDF; in its count to handle the case where a transfer spans two pointers.E; Note that the greatest number of contiguous pointers a transfer canR; span is two.;4- DECL R3 ; See if there is another pointerY BLEQ 60$ ; Branch if nonea .IF DF V66 MOVL (R4)+,R3 ; Get count of next retrieval pointer8 CMPL R9,(R4) ; See if the next pointer is contiguous .IFFS8 MOVZWL (R4)+,R3 ; Get count of next retrieval pointer8 CMPL R1,(R4) ; See if the next pointer is contiguous .ENDC BNEQ 60$ ; Branch if not;B .IF DF V6H; Ensure that the RVN of the new block matches the old one. R0 holds theK; current RVN, and 4(R4) is the next RVN. Compare these and do NOT take theC+; pointers as contiguous unless they match.+; CMPL R0,4(R4) ;Do RVNs match?5 BNEQ 60$ ;If not, do not treat as contig transferR .ENDC( ADDL2 R3,R8 ; Add to # blocks mapped;PE; Extract the lbn and rvn components of the starting "lbn" and switchr1; to the right ucb if this is a multi-volume set.s;o60$: .IF DF V6< MOVZBL R0,R0 ; Extract rvn (R1 already has starting lbn) .IFFf( EXTZV #0,#24,R0,R1 ; Extract lbn part# EXTZV #24,#8,R0,R0 ; Extract rvn  .ENDC' BEQL 70$ ; Branch if not volume sett8 MOVL WCB$L_RVT(R2),R6 ; Get relative volume table addr .IF DF V68 CMPB R0,RVT$B_NVOLS(R6) ; Volume mapped within ucblst?$ BGTRU 90$ ; No, force windowturn .ENDC; MOVL RVT$L_UCBLST-4(R6)[R0],R5 ; Get the right ucb addressC;SB; Check the range of vbn's provided by the map pointer against theA; file size recorded in the fcb. Reduce it if the fcb indicates aR$; smaller file size than the window.;270$: BBS #WCB$V_NOTFCP,- ; Skip check if no fcb WCB$B_ACCESS(R2),110$D) MOVL WCB$L_FCB(R2),R6 ; Get fcb addresst@ SUBL3 R7,FCB$L_FILESIZE(R6),R4 ; Compute blocks to physical eof. BLEQU 90$ ; Branch if vbn past end of file. CMPL R8,R4 ; Compare against blocks mapped/ BLEQU 80$ ; Branch if less mapped by window_( MOVL R4,R8 ; Else limit to file size;sF; For read I/O's, check the range of vbn's provided by the map pointerC; against the current highwater mark and set the appropriate statusD?; flags. Limit the length of the transfer to highwater mark. BySF; checking reads here, highwater mark flagging and checking is done onC; a segment by segment basis. Writes must be flagged for the entires*; I/O, and hence are handled in sysacpfdt.;U+80$: MOVL 4(SP),R3 ; Get back irp address)0 BBC #IRP$V_FUNC,- ; Branch if this is a write IRP$W_STS(R3),110$0 BICB #IRP$M_START_PAST_HWM!IRP$M_END_PAST_HWM,-. IRP$W_STS2(R3) ; Init highwater mark flags$ INCL R7 ; Back to true start vbn6 SUBL3 R7,FCB$L_HIGHWATER(R6),R7 ; Blocks to highwater' BGTRU 100$ ; Branch if not past hwm ; TSTL FCB$L_HIGHWATER(R6) ; See if highwater marking is ona BEQL 110$ ; Branch if not 9 BISB #IRP$M_START_PAST_HWM,- ; Note past highwater markD IRP$W_STS2(R3)+90$: CLRL R8 ; Indicate no blocks mappedt BRB 110$ ; And exitr=100$: CMPL R8,R7 ; Does the transfer extend past highwaters BLEQU 110$ ; Branch if not- MOVL R7,R8 ; Limit read to highwater marky;m4; See if the entire transfer is mapped contiguously.;o!110$: CLRL R0 ; Assume failureN, ASHL #9,R8,R8 ; Convert to # bytes mapped;F; If VS Integer overflow occurred when converting the number of blocks?; mapped by this extent to a byte count. This implies that the>@; requested tranfer must be entirely mapped by this extent sinceB; a requested byte count is restricted to a positive 32-bit value.<; (Routines EXE$READCHKR and EXE$WRITECHKR in SYSQIOFDT.MAR);i1 BVS 115$ ; If integer overflow then total mapp* BEQL 130$ ; Branch if no blocks mapped. SUBL R8,(SP) ; Subtract from bytes desired( BGEQU 120$ ; Branch if not total map0115$: CLRL (SP) ; Zero indicates complete map.120$: MOVL #SS$_NORMAL,R0 ; Indicate success8130$: POPR #^M ; Restore registers RSB e2 .SBTTL LD_START_CONTROL, Start LD control routine;+++-; LD_START_CONTROL, Start LD control routineS;O; Functional description:I;SB; This is the completion of the control processing. This has to be=; in system context since we may need to fork when we use theA); forklevel interface to the lockmanager.d;o ; Inputs:d;e9; R3 - address of the IRP (I/O request packet);9; R5 - address of the UCB (unit control block)O; D; The routine must preserve all registers except R0-R2 and R4.;o;---" UNIVERSAL_SYMBOL LD_START_CONTROL;LD_START_CONTROL: ASSUME LDIO_S_FUNC EQ 8F DISPATCH IRP$L_EXTEND(R3),TYPE=B,<- ; Dispatch according to function$ ,-. ,-0 ,-( ,-. ,-6 ,-% ,-p) >s; MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function codeh CLRL R1 REQCOMl r3 .SBTTL LD_START_CONNECT, LD Start connnect routines;+++.; LD_START_CONNECT, LD Start connnect routine;n; Functional description:;iB; This is the completion of the connect processing. This has to be=; in system context since we may need to fork when we use ther); forklevel interface to the lockmanager.r;w ; Inputs:S;_9; R3 - address of the IRP (I/O request packet)$9; R5 - address of the UCB (unit control block)0; ; Outputs:;sF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredM; R1 - 0L;RD; The routine must preserve all registers except R0-R2 and R4.;A;---" UNIVERSAL_SYMBOL LD_START_CONNECT;LD_START_CONNECT:' BBC #LDIO_V_SHARE,- ; Shared access?D IRP$L_EXTEND(R3),10$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBC, BBS #DEV$V_CLU,- ; Cluster wide visible ? UCB$L_DEVCHAR2(R1),10$6 MOVZWL #SS$_ACCONFLICT,R0 ; Not cluster-wide visible BRW 140$$A10$: BSBW LD_ENQ_LD_LOCK ; Get lock of container file or deviceG BLBS R0,15$ ; Ok BRW 150$ ; It's not allowedR315$: BSBW LD_ENQ_DEV_LOCK ; Get lock of LD deviceR BLBS R0,30$ ; It's allowed;0<; Save status in UCB instead of the stack since $LCK_DEQ may); fork, and we need a flat stack for thatp;B* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status) $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),-H VALBLK=UCB$A_LD_FILE_LVB(R5)l ; Get rid of file lock1 MOVL UCB$L_LD_SAVST(R5),R1 ; Get original error  BLBC R0,20$ ; Dequeue Error & MOVL R1,R0 ; Return original error 20$: BRW 140$:,30$: BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),40$$ BRW 60$ ; Skip file manipulation440$: MOVL UCB$L_LD_FCB(R5),R2 ; Recover FCB address7 INCW FCB$W_REFCNT(R2) ; Increment the reference countV3 INCW FCB$W_ACNT(R2) ; Increment the access count,5 MOVCY;Z$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"L UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBd& MOVL UCB$L_VCB(R1),R0 ; Get it's VCB7 ADAWI #1,VCB$W_TRANS(R0) ; Bump the transaction count . TSTW VCB$W_RVN(R0) ; Relative volume number) BEQL 50$ ; Branch if not a volume setB7 BISW2 #UCB$M_LD_ONVOLSET,- ; Flag container file on a ! UCB$W_LD_FLAGS(R5) ; volumeset( ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ850$: MOVL FCB$W_FID_NUM(R2),- ; Save FID for crosscheck UCB$W_LD_FID_NUM(R5) MOVW FCB$W_FID_RVN(R2),-e UCB$W_LD_FID_RVN(R5);D8; Check if the specified containerfile resides on an NFS6; mounted disk. In that case we need to do virtual I/O=; to the file, this will be indicated by the UCB$V_LD_VIRTUALE; bit set in UCB$W_LD_FLAGS.;% MOVL UCB$L_DDB(R1),R1 ; Get the DDBR CMPL DDB$T_NAME_STR(R1),- #^A/DNFS/M BNEQ 55$ ; No matchW< BISW2 #UCB$M_LD_VIRTUAL,- ; Indicate virtual access needed UCB$W_LD_FLAGS(R5)55$:;DJ; Convert the WCB to a shared WCB. This must be done in process context asJ; the FILSYS spinlock is needed, and we currently have IOLOCK8 which has aJ; higher rank. LD_RETURN_QUOTA will queue an ast to the requesting processH; which will do the job. By using a special kernel ast we make sure that1; it is done before the user deaccesses the file.(;)+ MOVL IRP$L_PID(R3),R4 ; Process to creditm$ MOVL UCB$L_LD_WCB(R5),R1 ; Get WCB8 BSBW LD_RETURN_QUOTA ; Make WCB shared & return quota BRW 110$S;); Replace drive processing; H; Take out devicelock for physical device to protect against access fromH; another cluster member. If it fails then the device is in use, we will?; check later on if it was by another LDdriver or someone else.;;s-60$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4bC MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource name " MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer sizec! MOVZBL #1,R4 ; DVI$_ALLDEVNAMe PUSHL R5 ; Save UCB 5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCBe1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save lengthL UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-d# RESNAME=UCB$T_LD_PD_RESNAM(R5),-( CMPLADR=80$,- EFLAGS=3 CMPW R0,#SS$_NOTQUEUED ; Any one else interested?  BNEQ 75$ ; NoB;#K; Someone owns the devicelock in an incompatible way. Check if it's another O; LDdriver by enqueueing a NL lock and checking bit 15 of the lock value block. L; If it's set then another LDdriver allocated the device for replacement, in; that case we may continuea;B ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-$# RESNAME=UCB$T_LD_PD_RESNAM(R5),-0 ERROR=75$,- EFLAGS=. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers2 MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Enqeueue status BLBC R0,75$ ; Error:;V9; Get rid of NL lock now that we have the lockvalue blockE; % $LCK_DEQ LKID=UCB$Q_LD_PD_LKSB+4(R5)) BLBC R0,75$ ; Deqeueue error2 CLRL UCB$Q_LD_PD_LKSB+4(R5) ; Not queued anymore;l>; Check for bit 15, if set by another LDdriver we may continue;E, BBC #15,UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5),70$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB  BRW 100$ ; Continue&70$: MOVZWL #SS$_DEVALLOC,R0 ; In use75$: BRW 90$ ; Quit;C ; Completion of ENQ ends up here;L280$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers: MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Check completion status# BLBC R0,75$ ; Something's wrongB;MM; No we've got the devicelock in EX mode. If we did not specify shared accessCL; we're done. The devicelock remains set to protect use againts other users.;C. BBS #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),85$ BRW 95$; R; Set bit 15 (currently not used) in the FLAGS field of the devicelock's lockvalueO; block to signal that we own the device. The fields are defined in the $DALDEFCU; macro. Furthermore, convert the lock to PW and back to EX to update the valueblock.t;I.85$: MOVL #^X8000,- ; Flag we own the device! UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5)a; CLRL UCB$A_LD_PD_LVB+4(R5) ; Zero rest of lockvalue block  CLRL UCB$A_LD_PD_LVB+8(R5)U CLRL UCB$A_LD_PD_LVB+12(R5) ENQ_LOCK MODE=#LCK$K_PWMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-C ERROR=90$,- EFLAGS= MOVZWL UCB$Q_LD_PD_LKSB(R5),R0( BLBC R0,90$ ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-d ERROR=90$,- EFLAGS=. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_PD_LKSB(R5),R0C BLBS R0,95$; G; Some error occurred. Make sure that we get rid of the $LOGDISK locks.o;n190$: MOVL UCB$L_LD_FR3(R5),R3 ; Restore registera MOVL R0,R4 ; Save status' $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5)  ; Get rid of file lock& $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of device lock, MOVL R0,R1 ; Return eventual error in R1 MOVL R4,R0 ; Restore statusE BRW 150$ ; Return errorT995$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBt, MOVL UCB$Q_LD_PD_LKSB+4(R5),- ; Save lockid UCB$L_LOCKID(R1)'100$: MOVL UCB$L_ORB(R5),- ; Save ORBv UCB$L_LD_ORBSAV(R5)t= MOVL UCB$L_ORB(R1),- ; Get protection from physical deviceK UCB$L_ORB(R5) - MOVB UCB$B_DEVTYPE(R1),- ; Copy device typeE UCB$B_DEVTYPE(R5)-&ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1(ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+1? MOVL UCB$B_SECTORS(R1),- ; Copy sectors, tracks and cylinders  UCB$B_SECTORS(R5) 0 MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5)* INCW UCB$W_REFC(R1) ; Mark our interest3 BICL2 #DEV$M_AVL,- ; Make unavailable for othersV UCB$L_DEVCHAR(R1)41 BISW2 #UCB$M_LD_REPLACE,- ; Set special connectS UCB$W_LD_FLAGS(R5)) BISL2 #,-0 UCB$L_DEVCHAR(R5) ; Set characteristics again% ; Needed after replace of CDrom;L ; Common exitv;u:110$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB0 EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0; INSV R0,#DEV$V_SWL,#1,- ; Honour writelock from phys diskn UCB$L_DEVCHAR(R5) ) BLBC R0,115$ ; Is the unit protected? 1 BISW2 #UCB$M_LD_PROTECT,- ; Protect ourself tooR UCB$W_LD_FLAGS(R5)8115$: MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecount UCB$L_MAXBCNT(R5)=5 CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected top" #DT$_RAM_DISK ; a DECRAM disk BNEQ 120$ ; No/ BISW2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in usey UCB$W_LD_FLAGS(R5):120$: MOVL UCB$L_DDT(R1),R1 ; Get DDT address (Phys. dev)) MOVL DDT$L_FDT(R1),R1 ; Get FDT addressL7 BBS #IO$_DSE,(R1),125$ ; Does the device support DSE?t7 BISW2 #UCB$M_LD_NODSE,- ; Not supported (Raid driver) " UCB$W_LD_FLAGS(R5) ; (DPdriver)C125$: BICL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB non-deletable + BBC #LDIO_V_SHARE,- ; Shared accessable?e IRP$L_EXTEND(R3),130$@ BISL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Make cluster wide visible& BISW2 #UCB$M_LD_SHARE,- ; Set status UCB$W_LD_FLAGS(R5)7130$: ADAWI #1,LD_REFCNT ; Count number of 'connects' 7 BISL2 #DPT$M_NOUNLOAD,- ; Prevent reloading of driverd DPT$TAB+DPT$L_FLAGSg4 BISW2 #UCB$M_LD_CONSTS,- ; Set status to connected UCB$W_LD_FLAGS(R5)0 MOVZWL #SS$_NORMAL,R0 ; Set status to success 140$: CLRL R1;,150$: BSBW LD_SAVE_TRACE ; Save trace data REQCOM ; Complete the I/O B9 .SBTTL LD_START_DISCONNECT, LD Start disconnnect routine ;+++4; LD_START_DISCONNECT, LD Start disconnnect routine; ; Functional description:D;6E; This is the completion of the disconnect processing. This has to beU=; in system context since we may need to fork when we use theR); forklevel interface to the lockmanager.o;s ; Inputs:t;c9; R3 - address of the IRP (I/O request packet)o9; R5 - address of the UCB (unit control block)u;f ; Outputs:;aF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredr; R1 - 02;,D; The routine must preserve all registers except R0-R2 and R4.;n;---% UNIVERSAL_SYMBOL LD_START_DISCONNECT6;LD_START_DISCONNECT:p1 CMPL UCB$L_LD_AIOFL(R5),- ; Any I/O's pending ?  @UCB$L_LD_AIOFL(R5)p BEQL 20$ ; NoB3 BBSSI #UCB$V_LD_DISPEN,- ; Set disconnect pendingn UCB$W_LD_FLAGS(R5),10$& MOVQ R3,UCB$L_FR3(R5) ; Save context1 MOVAB LD_START_DISCONNECT,- ; Address to resumeg) UCB$L_FPC(R5) ; (done by LD_COMPLEDq$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"TE)d10$: RSB ; Back to callero820$: BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),30$- MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB addressd3 DECW FCB$W_REFCNT(R2) ; Decr. the reference counto/ DECW FCB$W_ACNT(R2) ; Decr. the access count48 MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB& MOVL UCB$L_VCB(R0),R0 ; Get it's VCB= ADAWI #-1,VCB$W_TRANS(R0) ; Decrement the transaction countd BRW 50$;C; Replaced drive processingt;i830$: TSTL UCB$Q_LD_PD_LKSB+4(R5) ; Did we own the lock? BEQL 40$ ; Noe= $LCK_DEQ LKID=UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelockr BLBS R0,40$ ; Successe4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),40$ BRW 110$ ; Unexpected errore<40$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB! CLRL UCB$L_LOCKID(R0) ; Zero IDe5 DECW UCB$W_REFC(R0) ; We're not interested anymoret: BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again* MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5)e;p ; Common pathg;s+50$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5). ; Get rid of lockh;V<; Save status in UCB instead of the stack since $LCK_DEQ may); fork, and we need a flat stack for thath; * MOVL R0,UCB$L_LD_SAVST(R5) ; Save status& $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of lockE BLBC R0,60$ ; Errort2 MOVL UCB$L_LD_SAVST(R5),R0 ; Get previous status BLBS R0,70$ ; Success;60$: BBC #LDIO_V_ABORT,- ; Continue if abort is specifieds IRP$L_EXTEND(R3),110$b>70$: BISL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB deletable2 ADAWI #-1,LD_REFCNT ; Count down #of 'connects' BNEQ 100$ ; Some left 5 BICL2 #DPT$M_NOUNLOAD,- ; Allow reloading of driverv DPT$TAB+DPT$L_FLAGS 5100$: MOVZWL #SS$_NORMAL,R0 ; Set status to successe 110$: CLRL R1 ' BSBW LD_SAVE_TRACE ; Save trace dataO REQCOM  uB .SBTTL LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device;+++<; LD_ENQ_LD_LOCK, Enqueue file- or device lock for LD device; ; Functional description:o;eI; This routine will enqueue the file lock of the specified file andtD; device. It will check if the file is used on another node anywhereE; in the cluster. If true then it's only allowed if we ordered shared0B; access and if the remote devicename and geometry matches ours so; that XQP locking will work. ;s%; The locking protocol is as follows: ;tI; First we will enqueue an EX-mode lock on our resource. If it is granted F; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block andmF; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;tG; If we don't get it immediately then it means that another thread ownshB; the lock. In that case we implicitely triggered the blocking astH; routine by enqueueing the EX lock. This blocking ast routine will thenE; convert the lock back to NL, and it will immediately try to convertBE; it back to CR. After we get the lock, we will check the value blockBH; and then either dequeue it or convert it back to CR. After that action<; the blocking ast routine will convert the lock back to CR.;3I; If we encounter an invalid lock value block during this process we willfI; just rewrite it with the same contents. It's not documented, but we canaH; rely on the value being equal to what it was before it became invalid.A; (courtesy of Sandy Snaman, it may be documented in the future).n;l;s ; Inputs:B;19; R3 - address of the IRP (I/O request packet)e9; R5 - address of the UCB (unit control block)M*; UCB$T_LD_FILE_RESNAM(R5) - Resource name;m; IOLOCK8 (=SCS) forklock held ;p ; Outputs:;. ; R0 - statusL; SS$_NORMAL - Success5; SS$_FILALRACC - File in use in an incompatible wayVL; SS$_DEVALLOC - Device in use in an incompatible way?; other - Returned by lockmanager 1; R1 - Falure reason for SS$_FILALRACCe; *; Only R3-R5 are preserved as we may fork.;i;--- UNIVERSAL_SYMBOL LD_ENQ_LD_LOCK;LD_ENQ_LD_LOCK:7 MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's addressl ; because we may fork$+ MOVQ R3,UCB$L_LD_FR3(R5) ; Save registersP ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-t% RESNAME=UCB$T_LD_FILE_RESNAM(R5),-n CMPLADR=50$,- EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BNEQ 40$ ; No. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers;lC; We are the first one queueing a lock for this resource. Setup the F; lock value block, convert the lock back to CR and exit with success.;sC MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value blockh! MOVL UCB$L_DDB(R5),R2 ; Get DDBc3 MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation class3 LDFLVB_L_ALLOCLS(R1)% MOVW UCB$W_UNIT(R5),- ; Unit number  LDFLVB_W_UNIT(R1)B( MOVW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1)" MOVB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1)D$ MOVB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1)1 MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberc LDFLVB_L_MAXBLOCK(R1)I& CLRB LDFLVB_B_FLAGS(R1) ; Init flags' BBC #LDIO_V_SHARE,- ; Shared access?  IRP$L_EXTEND(R3),20$ BISB #LDFLVB_M_SHARE,- ! LDFLVB_B_FLAGS(R1) ; Set flags>"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-I BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$;A; Lock is converted to CR.;I230$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusn-40$: BRW 90$ ; Exit with completion status ;n; Lock is granted as EX.;l250$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusc;eN; Check for an invalid value block. If that's the case then we can rely on theL; fact that the contents of the valueblock are the same as they were before.J; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will; revalidate it.; 1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?a& BEQL 60$ ; Yes, deal with it later# BLBC R0,40$ ; Something's wrong; D; We now have the lock in EX mode. Check the lock value block to see; if we match.;MG60$: MOVAB UCB$A_LD_FILE_LVB(R5),R2 ; Setup pointer to lock value block 1 MOVL #LDREASON_NOTSHARED,R1 ; Assume not sharedU1 BBC #LDFLVB_V_SHARE,- ; Check for shared accessL LDFLVB_B_FLAGS(R2),80$7 MOVL #LDREASON_NOSHARE,R1 ; Assume no share requested01 BBC #LDIO_V_SHARE,- ; Shared access requested?B IRP$L_EXTEND(R3),80$9 MOVL #LDREASON_ALLOCLASS,R1 ; Assume Alloclass mismatchB! MOVL UCB$L_DDB(R5),R0 ; Get DDB53 CMPL DDB$L_ALLOCLS(R0),- ; Check allocation classB LDFLVB_L_ALLOCLS(R2) BNEQ 80$ ; No matchn; MOVL #LDREASON_UNITNUMBER,R1 ; Assume Unitnumber mismatchn% CMPW UCB$W_UNIT(R5),- ; Unit number0 LDFLVB_W_UNIT(R2); BNEQ 80$ ; No matchK7 MOVL #LDREASON_MAXBLOCK,R1 ; Assume Maxblock mismatchV1 CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberD LDFLVB_L_MAXBLOCK(R2)i BNEQ 80$ ; No match 9 MOVL #LDREASON_CYLINDERS,R1 ; Assume Cylinders mismatch( CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R2) BNEQ 80$ ; No matchW3 MOVL #LDREASON_TRACKS,R1 ; Assume Tracks mismatchD" CMPB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R2)  BNEQ 80$ ; No match 5 MOVL #LDREASON_SECTORS,R1 ; Assume Sectors mismatcho$ CMPB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R2) BNEQ 80$ ; No match ) BRW 20$ ; Convert down to CR and exitB; E; Dequeue the lock since we're not allowed to use the file or device.F; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.;o.80$: MOVL R1,UCB$L_LD_SAVST(R5) ; Save status) $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),-V VALBLK=UCB$A_LD_FILE_LVB(R5)  ; Get rid of file lock- MOVL UCB$L_LD_SAVST(R5),R1 ; Restore statusD BLBC R0,90$ ; Error / MOVZWL #SS$_FILALRACC,R0 ; Assume file in use( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),100$l) MOVZWL #SS$_DEVALLOC,R0 ; Device In useI BRB 100$i90$: CLRL R1 ; No reasonM/100$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume threadA SB .SBTTL LD_ENQ_FILE_BLKRTN, Enqueue file lock blocking ast routine;+++:; LD_ENQ_FILE_BLKRTN, Enqueue LD lock blocking ast routine;5; Functional description:o;sH; This routine will be called by the lockmanager when someE|$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1UEn"one elseE; tries to enqueue an incompatible lock on the LD file resource name._;UB; We will convert the lock back to NL, and then we immediately tryD; to convert it back to CR. If this second conversion is successfull; we will just exit.;sC; If the second conversion reveals a 'value block invalid' error weVB; must revalidate the lock. We do this by converting the lock back=; to NL, up to EX, back to NL and then back to CR. This looksiB; over-complicated but this is needed because more than one threadE; may be active at the same time, and we must make sure that we won'tiA; block other threads which in the conversion process. The secondoD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.t;e ; Inputs: ;M9; R5 - address of the UCB (unit control block) ;S; IOLOCK8 (=SCS) forklock heldz; ; Outputs:; *; Only R3-R5 are preserved as we may fork.;M;---$ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN;LD_ENQ_FILE_BLKRTN: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-U EFLAGS=,-l ERROR=60$! MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0#) BLBS R0,10$ ; Check completion status5 BRW 70$;AE; Lock is converted to NL, convert back to CR. Specify QUECVT so that!=; the requesting thread gets it's chance to acquire the lock. ;e"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-n BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=80$; ; Lock is converted to CR.;d4 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?L& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit* BRW 90$ ; Bugcheck on all other errors30$: RSB;_J; We have an invalid lock value block. Convert the lock to NL, then to EX,?; then to NL to rewrite the lockvalue block, and finally to CR.w;a"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-5 EFLAGS=,-e ERROR=100$C. MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status BLBS R0,45$ ; Successb BRW 110$D;v.; Lock is converted to NL, convert back to EX.;L"45$: ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-  EFLAGS=,- ERROR=120$u. MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?R BEQL 50$ ; AcceptableV* BLBC R0,130$ ; Check completion status; I; Lock is converted to EX, convert back to NL to rewrite the value block.l; "50$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-i EFLAGS=,- ERROR=140$#1 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Quit on errorX BLBC R0,150$B- BRW 10$ ; Ok, convert back to CR and exitn;e;; Bugcheck since there's nobody to return this error to.... ;t60$: BISL2 #^X80000000,R0l70$: BISL2 #^X40000000,R0E80$: BISL2 #^X20000000,R0o90$: BISL2 #^X10000000,R0c100$: BISL2 #^X08000000,R0110$: BISL2 #^X04000000,R0120$: BISL2 #^X02000000,R0130$: BISL2 #^X01000000,R0140$: BISL2 #^X00800000,R0D150$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager U: .SBTTL LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;+++4; LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;=; Functional description:_;VJ; This routine will enqueue the device lock of the specified device.>; It will check if the device is used on another node anywhereE; in the cluster. If true then it's only allowed if we ordered shared;D; access and if the remote filename matches ours so that XQP locking ; will work.;c%; The locking protocol is as follows: ;GI; First we will enqueue an EX-mode lock on our resource. If it is granted F; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block and F; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;sG; If we don't get it immediately then it means that another thread owns;B; the lock. In that case we implicitely triggered the blocking astH; routine by enqueueing the EX lock. This blocking ast routine will thenE; convert the lock back to NL, and it will immediately try to convertlE; it back to CR. After we get the lock, we will check the value blockpH; and then either dequeue it or convert it back to CR. After that action<; the blocking ast routine will convert the lock back to CR.;pI; If we encounter an invalid lock value block during this process we willKI; just rewrite it with the same contents. It's not documented, but we can;H; rely on the value being equal to what it was before it became invalid.A; (courtesy of Sandy Snaman, it may be documented in the future).n;S; ; Inputs:A;R9; R3 - address of the IRP (I/O request packet)C9; R5 - address of the UCB (unit control block)r); UCB$T_LD_DEV_RESNAM(R5) - Resource name1; ; IOLOCK8 (=SCS) forklock held ; ; Outputs:; ; R0 - statusL; SS$_NORMAL - Successo5; SS$_FILALRACC - File in use in an incompatible way-#; other - Returned by lockmanager;C*; Only R3-R5 are preserved as we may fork.;t;---! UNIVERSAL_SYMBOL LD_ENQ_DEV_LOCK ;LD_ENQ_DEV_LOCK:o7 MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's address- ; because we may fork + MOVQ R3,UCB$L_LD_FR3(R5) ; Save registers  ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),-$ RESNAME=UCB$T_LD_DEV_RESNAM(R5),- CMPLADR=50$,- EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BEQL 5$ ; Yes( BRW 90$ ; Exit15$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registersp;tC; We are the first one queueing a lock for this resource. Setup theCF; lock value block, convert the lock back to CR and exit with success.;)C MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value blocke! MOVL UCB$L_DDB(R5),R2 ; Get DDB2, MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length" CMPL R0,#6 ; Max we can handle BLEQ 10$ ; OkayC BRW 80$ ; Too much10$: PUSHR #^M( INCB R0 ; Length including len field0 MOVC3 R0,DDB$T_NAME(R2),- ; Move name + length LDDLVB_T_DEVNAM(R1)_ POPR #^Mo%ASSUME LDDLVB_W_SEQ EQ LDDLVB_W_FID+2W$ CLRL LDDLVB_W_FID(R1) ; Zero block CLRW LDDLVB_W_RVN(R1)( BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),15$0 MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address+ MOVW FCB$W_FID_NUM(R0),- ; Insert File ID_ LDDLVB_W_FID(R1) MOVW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) MOVW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1)615$: MOVB DDB$L_ALLOCLS(R2),- ; Fill allocation class LDDLVB_B_ALLOCLS(R1)% MOVW UCB$W_UNIT(R5),- ; Unit numbern LDDLVB_W_UNIT(R1)t"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-( EFLAGS=,- ERROR=90$;g; Lock is converted to CR.;a230$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers8 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status) BRW 90$ ; Exit with completion statusR;; Lock is granted as EX.;D250$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers8 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status;(N; Check for an invalid value block. If that's the case then we can rely on theL; fact that the contents of the valueblock are the same as they were before.J; (courtesy of Sandy Snaman). By either DEQing or converting to CR we will; revalidate it.; 1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid? & BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrongR;;D; We now have the lock in EX mode. Check the lock value block to see; if we match.;TG60$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value blockp! MOVL UCB$L_DDB(R5),R2 ; Get DDB0, MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length$ INCB R0 ; Check length field too PUSHR #^M1 CMPC3 R0,DDB$T_NAME(R2),- ; Check name + lengthR LDDLVB_T_DEVNAM(R1)c POPR #^MC BNEQ 80$ ; No matchS( BBS #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),70$0 MOVL UCB$L_LD_FCB(R5),R0 ; Recover FCB address* CMPW FCB$W_FID_NUM(R0),- ; Check File ID LDDLVB_W_FID(R1) BNEQ 80$ ; No matchR CMPW FCB$W_FID_SEQ(R0),- ; SEQ LDDLVB_W_SEQ(R1) BNEQ 80$ ; No match_ CMPW FCB$W_FID_RVN(R0),- ; RVN LDDLVB_W_RVN(R1) BNEQ 80$ ; No match 770$: CMPB DDB$L_ALLOCLS(R2),- ; Check allocation classoF}n$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"$ LDDLVB_B_ALLOCLS(R1) BNEQ 80$ ; No match% CMPW UCB$W_UNIT(R5),- ; Unit number( LDDLVB_W_UNIT(R1)  BNEQ 80$ ; No matchu) BRW 20$ ; Convert down to CR and exitQ;yE; Dequeue the lock since we're not allowed to use the file or device.SF; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.;0,80$: $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5),- VALBLK=UCB$A_LD_DEV_LVB(R5) ; Get rid of file lock BLBC R0,90$ ; Errori) MOVZWL #SS$_DEVALLOC,R0 ; Device In useR.90$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume thread C .SBTTL LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routinet;+++=; LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routineo;n; Functional description:$;LH; This routine will be called by the lockmanager when someone elseG; tries to enqueue an incompatible lock on the LD device resource name.D;QB; We will convert the lock back to NL, and then we immediately tryD; to convert it back to CR. If this second conversion is successfull; we will just exit.; C; If the second conversion reveals a 'value block invalid' error weB; must revalidate the lock. We do this by converting the lock back=; to NL, up to EX, back to NL and then back to CR. This looksdB; over-complicated but this is needed because more than one threadE; may be active at the same time, and we must make sure that we won't A; block other threads which in the conversion process. The secondsD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.t; ; Inputs:l;h9; R5 - address of the UCB (unit control block) ;e; IOLOCK8 (=SCS) forklock helda; ; Outputs:;c*; Only R3-R5 are preserved as we may fork.; ;---# UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTNo;LD_ENQ_DEV_BLKRTN:m ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,-u ERROR=60$: MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Check completion status BLBS R0,10$ ; SuccessC BRW 70$;eE; Lock is converted to NL, convert back to CR. Specify QUECVT so thatt=; the requesting thread gets it's chance to acquire the lock.t;u"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-  EFLAGS=,- ERROR=80$; ; Lock is converted to CR.; 3 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get final statusu1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?f& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit* BRW 90$ ; Bugcheck on all other errors30$: RSB;nJ; We have an invalid lock value block. Convert the lock to NL, then to EX,?; then to NL to rewrite the lockvalue block, and finally to CR.S;N"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,-L ERROR=100$e- MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get status  BLBS R0,45$ ; Success BRW 110$ ;k.; Lock is converted to NL, convert back to EX.;I"45$: ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=120$$- MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get status 1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?; BEQL 50$ ; Acceptable * BLBC R0,130$ ; Check completion status;SI; Lock is converted to EX, convert back to NL to rewrite the value block.G;L"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=140$o0 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Quit on error BLBC R0,150$ - BRW 10$ ; Ok, convert back to CR and exitr;h;; Bugcheck since there's nobody to return this error to...._;E60$: BISL2 #^X80000000,R0 70$: BISL2 #^X40000000,R0 80$: BISL2 #^X20000000,R0c90$: BISL2 #^X10000000,R0 100$: BISL2 #^X08000000,R0110$: BISL2 #^X04000000,R0120$: BISL2 #^X02000000,R0130$: BISL2 #^X01000000,R0140$: BISL2 #^X00800000,R0D150$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager c/ .SBTTL LD_START_TRACE, Start I/O trace routine5;+++); LD_START_TRACE, Start I/O trace routine$;A; Functional description:c;mM; This is the completion of the retrieve tracedata processing. This has to beDG; in system context since we need to fork when we find that there is no_; tracedata available.;R ; Inputs:l;>9; R3 - address of the IRP (I/O request packet)I9; R5 - address of the UCB (unit control block)L;M ; Outputs:; ; None.-; D; The routine must preserve all registers except R0-R2 and R4.;--- UNIVERSAL_SYMBOL LD_START_TRACE;LD_START_TRACE:> INSQUE (R3),@UCB$L_LD_TRCWAITQBL(R5) ; Save wait IRP in queue;eH; Return to caller. Since we don't want the I/O to complete now (there'sD; no trace data when we get here) we return to our caller instead ofE; going through REQCOM. When LD_TRACE writes new data into the bufferaE; it will queue an AST to the process, and the process will resume ataD; LD_TRACE_AST which will copy the new data to the user and complete ; the I/O.;o RSB ; Return to callera a9 .SBTTL LD_TRACE_AST, Display data AST completion routined;+++4; LD_TRACE_AST, Display data AST completion routine;o; Functional description:n;h9; This is the final completion of the display processing.@; This routine is called as an AST routine in the context of theB; process issueing the I/O. We will move the newly available trace2; data to the user's buffer, and complete the I/O.;a ; Inputs:D;B1; R4 - address of the PCB (process control block)u$; R5 - address of the ACB;E=; The routine must preserve all registers except R0-R5. ;--- UNIVERSAL_SYMBOL LD_TRACE_AST;LD_TRACE_AST:( MOVL ACB$L_ASTPRM(R5),R3 ; Restore IRP MOVL R5,R0 ; Address of ACBc( JSB G^EXE$DEANONPAGED ; Get rid of ACB% MOVL IRP$L_UCB(R3),R5 ; Restore UCBN) BSBW LD_MOVE_TRACE ; Move data to user- BLBC R0,10$ ; Missed count in R1 on errorB= DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to #of entriesK,10$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelC SAVIPL=-(SP) % MOVL R3,UCB$L_IRP(R5) ; Restore IRPR. JSB G^IOC$REQCOM ; Complete the I/O request0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELO PRESERVE=NO RSB c@ .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing;+++;; LD_START_ENABLE_WATCH, Start I/O enable watch processingu;S; Functional description:$;E=; This is the completion of the enable watchpoint processing.NB; Buffers will be allocated, and if any buffer which was specified?; by the user already exists we will return the charge for thisp; buffer to the user by ast.; ; Inputs:w;n9; R3 - address of the IRP (I/O request packet)B9; R5 - address of the UCB (unit control block)E/; IRP$L_OBCNT - number of entries in userbuffer; ; Outputs:;f ; None. ;$D; The routine must preserve all registers except R0-R2 and R4.;---' UNIVERSAL_SYMBOL LD_START_ENABLE_WATCHC;LD_START_ENABLE_WATCH:l5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addressL) MOVL (R2),R2 ; Get userbuffer address:% MOVL IRP$L_OBCNT(R3),R4 ; Get countU810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead4 BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not foundu( CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPEND BNEQ 20$ ; Noe5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints 720$: BSBW LD_COPY_WATCH ; Found exact match, copy newc ; parameters except pid > MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^Mi+ MOVL IRP$L_PID(R3),R4 ; Process to credit 1 BSBW LD_RETURN_QUOTA ; Give quota back to usera POPR #^M BRW 40$w*30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1t, MOVZWL #LDWATCHENT_K_LENGTH,R1 ; This size$ JSB G^EXE$ALONONPAGED ; Get buffer" BLBC R0,50$ ; Out of resources1 MOVB #DYN$C_BUFIO,LDWATCH_B_TYPE(R2) ; Fill typem' CLRW LDWATCH_W_FLAGS(R2) ; Zero flags'' MOVW R1,LDWATCH_W_SIZE(R2) ; And sizen3 MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queuee LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),-o LDWATCH_L_SUSPBL(R2)4 CLRL LDWATCH_L_SUSPCNT(R2) ; Nothing suspended yet# MOVL R2,R1 ; New buffer address $ MOVL 4(SP),R2 ; Get entry address* BSBW LD_COPY_WATCH_NEW ; Copy parameters& MOVL R1,R2 ; Restore buffer addres# MOVL (SP)+,R1 ; Restore listhead_4 INSQUG@$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U|"5E (R2),LDWATCH_L_FLINK(R1) ; Insert new packet+ INCL UCB$L_LD_WATCHCNT(R5) ; Count packetC MOVL (SP)+,R2>40$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry# SOBGTR R4,45$ ; For all buffers" MOVZWL #SS$_NORMAL,R0 ; Success BRB 60$45$: BRW 10$ ; Branch assistu-50$: ADDL2 #8,SP ; Adjust for error returnk960$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count_ REQCOM( ,B .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing;+++=; LD_START_DISABLE_WATCH, Start I/O disable watch processingv;e; Functional description:_;F>; This is the completion of the disable watchpoint processing.?; If a watchpoint was found with suspended threads queued to itB&; then these threads will be released.; ; Inputs:o;l9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)L/; IRP$L_OBCNT - number of entries in userbufferR; ; Outputs:;C ; None.L;ED; The routine must preserve all registers except R0-R2 and R4.;---( UNIVERSAL_SYMBOL LD_START_DISABLE_WATCH;LD_START_DISABLE_WATCH:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R4 ; Get packet countO BNEQ 10$ ; Something to do BRW 100$L'10$: PUSHL R6 ; May not be destroyed ! CLRL R6 ; Nothing removed yetM5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address#$ BEQL 20$ ; Not there, remove all) MOVL (R2),R2 ; Get userbuffer address0/ MOVL IRP$L_OBCNT(R3),R4 ; Get requested countv820$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead& TSTL R2 ; Get rid of alll entries? BEQL 30$ ; Yes4 BSBW LD_FIND_WATCH_ENTRY_DIS ; Find matching entry BLBS R0,70$ ; No exact match BRB 40$ ; Got it, remove*30$: MOVL (R1),R1 ; Point to next entry- MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; End of liste CMPL R0,R1 ; At the end? BEQL 60$ ; Yes940$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, removeE+ DECL UCB$L_LD_WATCHCNT(R5) ; Count packet0* INCL R6 ; Flag we've removed something" MOVL R0,R1 ; Use correct input5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints - PUSHR #^M ; Destroyed by deallocateL5 BBC #LDWATCH_V_FILE,- ; Check for virtual file modeQ LDWATCH_W_FLAGS(R0),50$o- MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointere/ DECW FCB$W_REFCNT(R2) ; Decr. reference counts+ DECW FCB$W_ACNT(R2) ; Decr. access countu& MOVL UCB$L_VCB(R5),R2 ; Get it's VCB8 ADAWI #-1,VCB$W_TRANS(R2) ; Bump the transaction count350$: MOVL LDWATCH_L_PID(R0),R4 ; Process to creditl/ JSB G^EXE$DEANONPAGED ; Return buffer to poole1 MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to returni1 BSBW LD_RETURN_QUOTA ; Give quota back to user  POPR #^M BRB 20$"60$: TSTL R2 ; Next input entry BEQL 80$ ; All entries>70$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry)80$: SOBGTR R4,20$ ; Check all packets ) MOVZWL #SS$_NORMAL,R0 ; Assume successo TSTL R6 ; Anything removed?u BNEQ 90$ ; Yes, MOVZWL #SS$_DATACHECK,R0 ; Entry not found(90$: MOVL (SP)+,R6 ; Restore this baby:100$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count REQCOM  t? .SBTTL LD_COPY_WATCH, Copy watchpoint info into existing blockn> .SBTTL LD_COPY_WATCH_NEW, Copy watchpoint info into new block;+++:; LD_COPY_WATCH, Copy watchpoint info into existing block9; LD_COPY_WATCH_NEW, Copy watchpoint info into new blockb; ; Functional description: ; >; This routine copies the data from the user's parameter block; into a watchpoint blockc;a ; Inputs:u;g<; R1 - address of the driver allocated watchblock4; R2 - address of the users watchpt block9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block)r;n ; Outputs:;u ; None. ;3;---# UNIVERSAL_SYMBOL LD_COPY_WATCH_NEWk;LD_COPY_WATCH_NEW: 0 MOVL IRP$L_PID(R3),LDWATCH_L_PID(R1) ; Move pid/ MOVL IRP$L_UCB(R3),LDWATCH_L_UCB(R1) ; And UCBC7 BBS #LDWATCHPT_V_FILE,- ; Check for virtual file modeu LDWATCHPT_W_FLAGS(R2),10$7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber  LDWATCH_L_LBN(R1)  BRW LD_COPY_WATCH!10$: PUSHR #^Mr/ MOVL LDWATCHPT_L_SBK(R2),R4 ; Get SBK address) MOVL SBK$L_FCB(R4),R4 ; Get FCB addressV MOVL R4,LDWATCH_L_FCB(R1)/ INCW FCB$W_REFCNT(R4) ; Incr. reference countL+ INCW FCB$W_ACNT(R4) ; Incr. access countE& MOVL UCB$L_VCB(R5),R8 ; Get it's VCB7 ADAWI #1,VCB$W_TRANS(R8) ; Bump the transaction countA& MOVL FCB$L_WLFL(R4),R7 ; Get any WCB'ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ % MOVL FCB$W_FID_NUM(R4),- ; Save FID( LDWATCH_W_FID_NUM(R1)$ MOVW FCB$W_FID_RVN(R4),-  LDWATCH_W_FID_RVN(R1)7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber 0 LDWATCH_L_VBN(R1) ; to virtual block for file7 MOVL LDWATCHPT_L_LBN(R2),R6 ; Get virtual blocknumber_;20$: MOVZWL WCB$W_NMAP(R7),R3 ; Number of mapping pointersD .IF DF V6*ASSUME WCB$L_P2_COUNT EQ WCB$L_P1_COUNT+12@ MOVAL WCB$L_P1_COUNT(R7),R5 ; Address of first mapping pointer30$: MOVL (R5)+,R4 ; Count .IFFR)ASSUME WCB$W_P2_COUNT EQ WCB$W_P1_COUNT+6i@ MOVAW WCB$W_P1_COUNT(R5),R5 ; Address of first mapping pointer30$: MOVZWL (R5)+,R4 ; Count .ENDC% CMPL R6,R4 ; Within this segment?D BLEQ 40$ ; Yes2 SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbn .IF DF V6 TSTL (R5)+ ; Skip RVN$ .ENDC SOBGTR R3,30$ ; Next segment# MOVL WCB$L_LINK(R7),R7 ; Next WCB; BNEQ 20$ ; Try this oneF;RC; We should never come here. The VBN was checked in the FDT routine_&; to be within the limits of the file.; BUG_CHECK INCONSTATE,FATAL $40$: DECL R6 ; Adjust for VBN = 1C ADDL3 R6,(R5)+,LDWATCH_L_LBN(R1) ; Save converted LBN, skip to RVNt. TSTW VCB$W_RVN(R8) ; Relative volume number) BEQL 50$ ; Branch if not a volume setD MOVZBL (R5),R0 ; Get RVN+ MOVL VCB$L_RVT(R8),R6 ; Fetch RVT address$: MOVL RVT$L_UCBLST-4(R6)[R0],- ; Get the right ucb address LDWATCH_L_UCB(R1)g3 CMPL 8(SP),LDWATCH_L_UCB(R1) ; Is it our own UCB?t BEQL 50$ ; Yes, no problem9 BISW2 #LDWATCH_M_ONVOLSET,- ; Flags this extent is on a0" LDWATCH_W_FLAGS(R1) ; volumeset 50$: POPR #^M;(; Fall into LD_COPY_WATCHt; UNIVERSAL_SYMBOL LD_COPY_WATCHb;LD_COPY_WATCH:h2 BICW3 #^C,- ; Mask unused bits LDWATCHPT_W_FLAGS(R2),R0( BISW2 R0,LDWATCH_W_FLAGS(R1) ; Options+ MOVW LDWATCHPT_W_ACTION(R2),- ; What to doi LDWATCH_W_ACTION(R1)0 MOVW LDWATCHPT_W_FUNC(R2),- ; On what function LDWATCH_W_FUNC(R1)0 MOVW LDWATCHPT_W_RETCODE(R2),- ; What to return LDWATCH_W_RETCODE(R1); RSB wE .SBTTL LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enablea;+++@; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable; ; Functional description:,; 6; This routine locates a watchpoint entry. Entries are6; inserted in ascending order, so when we are past one9; entry we don't need to look any further. A special caset9; is signalled if the NOLBN flag is set. This is the caseE8; for a non-transfer function. We can have more of these; for different functions.;M ; Inputs:D;B:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;c ; Outputs:;_ ; R0 - 0 if found an exact match; 1 if not found;; R1 - if R0 = 0 entry address#; if R0 = 1 address if insertionE;0;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_ENA ;LD_FIND_WATCH_ENTRY_ENA: MOVL R1,R0 ; Save for later !10$: MOVL (R1),R1 ; Next entry- CMPL R1,R0 ; End of queue ?( BEQL 60$ ; Yes1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?i LDWATCH_W_FLAGS(R1),40$s, CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1)k BNEQ 10$ ; No * BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$ ; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching block) LDWATCH_L_VBN(R1)f BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1):30$: BEQL 40$ ; Yes' BGTRU 10$ ; Not a higher number yetB BRB 50$ ; No match640$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; No1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?e LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes; '; Here everything matches except actionl"; Determine if action may be added;Q5 CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action?m #WATCH_ACTION_OPCOMe BEQL 50$ ; Yes;i@; Add H3$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1UM"Fsome action other than OPCOM. Make sure we don't overwrite; a current OPCOM action.a;a< CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOMg2 BNEQ 80$ ; No, modify action of current packet;b,; We must check the next packet for a match.;i BRW 10$;eA; Make sure that a packet with an action for OPCOM is in front ofeF; other actions for the same lbn and function in the queue. This makesD; sure that later on in LD_CHECK_WATCH we will always issue an opcomE; message before we do any other specified action. To insert an entryTC; before the current location we have to provide the address of then; predecessor.; 350$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up address  BRB 70$ ; of previous entryy;rA; Nothing found in the queue. Insert new entry after the last oneR;m360$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up addressE ; of last entry= 70$: MOVZBL #1,R0 ; Not found BRB 90$80$: CLRL R0 ; Exact matchm90$: RSB F .SBTTL LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable;+++A; LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable';h; Functional description:;6; This routine locates a watchpoint entry. Entries are6; inserted in ascending order, so when we are past one9; entry we don't need to look any further. A special case9; is signalled if the NOLBN flag is set. This is the caseC8; for a non-transfer function. We can have more of these; for different functions.;l ; Inputs: ; :; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;B ; Outputs:; ; R0 - 0 if found an exact match; 1 if not founde; R1 - if R0 = 0 entry address!; if R0 = 1 last entry addresst;R;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_DISS;LD_FIND_WATCH_ENTRY_DIS: PUSHL R3L MOVL R1,R0 ; Save for later!10$: MOVL (R1),R1 ; Next entryt CMPL R1,R0 ; End of queue ?s BEQL 40$ ; Yes; EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bitsL #LDWATCHPT_S_CHARS,- LDWATCHPT_W_FLAGS(R2),R39 CMPZV #LDWATCH_V_CHARS,- ; Check against current packet  #LDWATCH_S_CHARS,- LDWATCH_W_FLAGS(R1),R3 BNEQ 10$ ; No matchS1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?  LDWATCH_W_FLAGS(R1),60$0* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$b; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockE LDWATCH_L_VBN(R1)L BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)R30$: BEQL 50$ ; Yes' BGTRU 10$ ; Not a higher number yet0 40$: MOVZBL #1,R0 ; Not found BRB 80$050$: BBS #LDWATCHPT_V_REMOVE_ALL,- ; Remove all? LDWATCHPT_W_FLAGS(R2),70$0660$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; No01 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?^ LDWATCH_W_ACTION(R1) BNEQ 10$ ; No370$: CLRL R0 ; Exact matchI'80$: MOVL (SP)+,R3 ; Restore registerA RSB ;? .SBTTL LD_START_GET_WATCH, Start I/O get watch info processingt;+++:; LD_START_GET_WATCH, Start I/O get watch info processing;n; Functional description:p;n:; This is the completion of the get watchpoint processing.;e ; Inputs: ; 9; R3 - address of the IRP (I/O request packet)i9; R5 - address of the UCB (unit control block)l;> ; Outputs:; ; None.t;ID; The routine must preserve all registers except R0-R2 and R4.;r$ UNIVERSAL_SYMBOL LD_START_GET_WATCH;LD_START_GET_WATCH:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet count BNEQ 10$ ; Something to do BRW 50$/10$: BBS #LDIO_V_INQUIRE,- ; Return list size?n IRP$L_EXTEND(R3),40$3 MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smalld2 MULL3 R1,#LDWATCHPT_K_LENGTH,R2 ; Get size needed( CMPL IRP$L_BCNT(R3),R2 ; Enough space? BLSSU 60$ ; No5 MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer addressa) MOVL (R4),R4 ; Get userbuffer addressA9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead  MOVL R0,R2 ; Remember starto%20$: MOVL (R0),R0 ; Get next entrya CMPL R0,R2 ; At the end? BEQL 40$ ; Yes) CLRL LDWATCHPT_L_SBK(R4) ; Not returnedo5 MOVL LDWATCH_L_LBN(R0),- ; Move logical blocknumbero LDWATCHPT_L_LBN(R4) r- ASSUME LDWATCH_W_ACTION EQ LDWATCH_W_FLAGS+2r/ MOVL LDWATCH_W_FLAGS(R0),- ; Flags and actiont LDWATCHPT_W_FLAGS(R4)n- ASSUME LDWATCH_W_RETCODE EQ LDWATCH_W_FUNC+2e5 MOVL LDWATCH_W_FUNC(R0),- ; Function and returncoded LDWATCHPT_W_FUNC(R4)5 BBC #LDWATCH_V_FILE,- ; Check for virtual file mode LDWATCH_W_FLAGS(R0),30$t0 ASSUME LDWATCH_W_FID_NUM+2 EQ LDWATCH_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R0),- ; Save FID LDWATCHPT_W_FID_NUM(R4); MOVW LDWATCH_W_FID_RVN(R0),-A LDWATCHPT_W_FID_RVN(R4)D5 MOVL LDWATCH_L_VBN(R0),- ; Move virtual blocknumber  LDWATCHPT_L_LBN(R4) V/30$: ADDL2 #LDWATCHPT_K_LENGTH,R4 ; Next entryi BRB 20$640$: MOVZWL #SS$_NORMAL,R0 ; Return with count in R1450$: BBS #LDIO_V_INQUIRE,- ; Return list size only? IRP$L_EXTEND(R3),60$. MULL3 #LDWATCHPT_K_LENGTH,R1,R2 ; Total count2 MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing' INSV R2,#16,#16,R0 ; Merge bytecountI 60$: REQCOMR LH .SBTTL LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processing;+++C; LD_START_GET_SUSPEND_LIST, Start I/O get suspend list processingt;O; Functional description:+;<; This is the completion of the get suspend list processing.; ; Inputs:l;s9; R3 - address of the IRP (I/O request packet)h9; R5 - address of the UCB (unit control block) ;f ; Outputs:;e ; None.e;eD; The routine must preserve all registers except R0-R2 and R4.;r+ UNIVERSAL_SYMBOL LD_START_GET_SUSPEND_LIST ;LD_START_GET_SUSPEND_LIST:u0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet countn BNEQ 10$ ; Something to do BRW 100$ ; Branch assist 10$: CLRL R1 ; No entries yet9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listheadD MOVL R0,R2 ; Remember startN%20$: MOVL (R0),R0 ; Get next entry; CMPL R0,R2 ; At the end? BEQL 30$ ; Yes9 ADDL2 LDWATCH_L_SUSPCNT(R0),R1 ; Count number of entriesU BRB 20$/30$: BBS #LDIO_V_INQUIRE,- ; Return list size? IRP$L_EXTEND(R3),90$2 MULL3 R1,#LDSUSPLST_K_LENGTH,R2 ; Get size needed( CMPL IRP$L_BCNT(R3),R2 ; Enough space? BGEQU 35$ ; YesO, MOVZWL #SS$_IVBUFLEN,R0 ; Buffer too small BRW 110$ ; Exit with error935$: MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer addressp) MOVL (R4),R4 ; Get userbuffer addressV9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listhead MOVL R0,R2 ; Remember startI%40$: MOVL (R0),R0 ; Get next entryR CMPL R0,R2 ; At the end? BEQL 90$ ; Yes4 TSTL LDWATCH_L_SUSPCNT(R0) ; Get count of this lbn BEQL 40$ ; None here PUSHR #^M_8 MOVAL LDWATCH_L_SUSPFL(R0),R1 ; Point to suspend queue MOVL R1,R2 ; Remember endr.50$: MOVL IRP$L_IOQFL(R1),R1 ; Get next entry CMPL R1,R2 ; At the end? BEQL 80$ ; Yes PUSHR #^MH= BBC #IRP$V_SHDIO,IRP$W_STS2(R1),55$ ; Is this shadowing I/O? . MOVL IRP$L_MIRP(R1),R1 ; Yes, get master irp%55$: MOVL IRP$L_PID(R1),R0 ; Get PIDL, JSB G^EXE$IPID_TO_EPID ; Make external PID( MOVL R0,LDSUSPLST_L_PID(R4) ; Move PID POPR #^M5 BBS #LDWATCH_V_FILE,- ; Check for virtual file mode; LDWATCH_W_FLAGS(R0),60$R% MOVL LDWATCH_L_LBN(R0),- ; Move LBN) LDSUSPLST_L_LBN(R4)a BRB 70$)60$: MOVL LDWATCH_L_VBN(R0),- ; Move VBNt LDSUSPLST_L_LBN(R4)N1 ASSUME LDSUSPLST_W_ACTION EQ LDSUSPLST_W_FLAGS+2#870$: MOVL LDWATCH_W_FLAGS(R0),- ; Move flags and action LDSUSPLST_W_FLAGS(R4)f1 ASSUME LDSUSPLST_W_RETCODE EQ LDSUSPLST_W_FUNC+27 MOVL LDWATCH_W_FUNC(R0),- ; Move function and retcode  LDSUSPLST_W_FUNC(R4)+ ADDL2 #LDSUSPLST_K_LENGTH,R4 ; Next entryt BRB 50$80$: POPR #^M BRB 40$90$: MOVZWL #SS$_NORMAL,R05100$: BBS #LDIO_V_INQUIRE,- ; Return list size only?C IRP$L_EXTEND(R3),110$h. MULL3 #LDSUSPLST_K_LENGTH,R1,R2 ; Total count2 MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing' INSV R2,#16,#16,R0 ; Merge bytecount 110$: REQCOM a@ .SBTTL LD_START_RESUME_WATCH, Start I/O resume watch processing;+++;; LD_START_RESUME_WATCH, Start I/O resume watch processingt;; Functional description:f;e=; This is the completion of the resume watchpoint processing.nG; Any previously suspended thread which is specified will be restarted. ;t ; IJdJ$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"WInputs:l;e9; R3 - address of the IRP (I/O request packet)A9; R5 - address of the UCB (unit control block)T/; IRP$L_OBCNT - number of entries in userbufferW;H ; Outputs:;t ; None.;ED; The routine must preserve all registers except R0-R2 and R4.;e' UNIVERSAL_SYMBOL LD_START_RESUME_WATCHe;LD_START_RESUME_WATCH:)0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 TSTL UCB$L_LD_WATCHCNT(R5) ; Check packet countf BEQL 70$ ; Nothing to do# PUSHL R6 ; May not be destroyed0! CLRL R6 ; Nothing resumed yet 5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addresse BEQL 10$ ; Nothing there) MOVL (R2),R2 ; Get userbuffer addresst010$: MOVL IRP$L_OBCNT(R3),R4 ; Get wanted count3 MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Point to listheadt& MOVL R1,R0 ; Save start for later.20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue? BEQL 40$ ; Yes TSTL R2 ; All of them? BEQL 30$ ; Yes- CMPW LDWATCH_W_FUNC(R1),- ; Right function?e LDWATCHPT_W_FUNC(R2) BNEQ 20$ ; Nor5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modea LDWATCH_W_FLAGS(R1),25$4' CMPL LDWATCH_L_LBN(R1),- ; Right lbn?A LDWATCHPT_L_LBN(R2)o BNEQ 20$ ; NoQ BRB 30$+25$: CMPL LDWATCH_L_VBN(R1),- ; Right vbn?  LDWATCHPT_L_LBN(R2)C BNEQ 20$ ; No 130$: CMPW LDWATCH_W_ACTION(R1),- ; Right action?c #WATCH_ACTION_SUSPENDC BNEQ 20$ ; Noe5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpointss" INCL R6 ; We resumed something BRB 20$ ; Next packete"40$: TSTL R2 ; Everything done? BEQL 50$ ; Yes4 ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Next entry of input SOBGTR R4,20$ ; Count down-50$: MOVZBL #SS$_NORMAL,R0 ; Assume success: TSTL R6 ; Done something? BNEQ 60$ ; Yes) MOVZWL #SS$_DATACHECK,R0 ; Nothing done '60$: MOVL (SP)+,R6 ; Recover this one 70$: CLRL R1 REQCOMk : .SBTTL LD_DRAIN_WATCH_THREAD, drain suspended watch queue;+++5; LD_DRAIN_WATCH_THREAD, drain suspended watch queueK; ; Functional description:M; 6; This routine resumes all pending watchpoint threads.;C ; Inputs: ;u*; R1 - address of LDWATCH block9; R5 - address of the UCB (unit control block)T;D ; Outputs:;,; None.c;i;---' UNIVERSAL_SYMBOL LD_DRAIN_WATCH_THREAD ;LD_DRAIN_WATCH_THREAD: = TSTL LDWATCH_L_SUSPCNT(R1) ; Get count of suspended threads  BEQL 40$ ; Nothing$ PUSHR #^M ; Save registers310$: REMQUE @LDWATCH_L_SUSPFL(R1),R3 ; Remove entryo+ BBSS #UCB$V_LD_FKBBSY,- ; Forkblock busy?  UCB$W_LD_FLAGS(R5),20$. MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5)u/ MOVL IRP$L_FPC(R3),- ; Restore Fork trace PCt UCB$L_FPC(R5)s) JSB G^EXE$QUEUE_FORK ; Queue forkthreade BRB 30$I20$: INSQUE (R3),@UCB$L_LD_WATCHPNDQBL(R5) ; Insert in watchpending queueM/30$: DECL LDWATCH_L_SUSPCNT(R1) ; Count packet  BNEQ 10$ ; More to do( POPR #^M40$: RSB 3F .SBTTL LD_RESUME_WATCH_THREAD, resume eventual suspended watch thread;+++A; LD_RESUME_WATCH_THREAD, resume eventual suspended watch thread,;;; Functional description:;D5; This routine resumes one pending watchpoint thread.R;4 ; Inputs:8;9; R3 - address of the IRP (I/O request packet)$9; R5 - address of the UCB (unit control block));I ; Outputs:;T; None.n; ;---( UNIVERSAL_SYMBOL LD_RESUME_WATCH_THREAD;LD_RESUME_WATCH_THREAD: PUSHR #^M E REMQUE @UCB$L_LD_WATCHPNDQFL(R5),R3 ; Remove from watchpending queueV BVS 10$ ; Nothing therey. MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5)U/ MOVL IRP$L_FPC(R3),- ; Restore Fork trace PCM UCB$L_FPC(R5)() JSB G^EXE$QUEUE_FORK ; Queue forkthreadA BRB 20$:10$: BICW2 #UCB$M_LD_FKBBSY,- ; Reset Forkblock busy flag UCB$W_LD_FLAGS(R5)20$: POPR #^M RSB . .SBTTL LD_CHECK_WATCH, Check for a watchpoint;+++); LD_CHECK_WATCH, Check for a watchpointD;6; Functional description:W;LD; This routine will check if a watchpoint is set for the current I/OB; request. If true, the action we will do depends on the specified; function for the watchpoint.;B; We currently have 4 options:;r-; 1. WATCH_ACTION_SUSPEND: Suspend a threadC+; 2. WATCH_ACTION_CRASH: Crash the systemg*; 3. WATCH_ACTION_ERROR: Return an error2; 4. WATCH_ACTION_OPCOM: Send a message to OPCOM;iH; #1 will suspend the current thread. We do this by popping our caller'sK; PC into a forkblock and returning to our caller's caller. Later on we can E; resume this thread by queueing the forkblock to the forkdispatcher.o;n<; #2 will crash the system if we find a matching watchpoint.;T?; #3 will return a user-specified error for the current thread.);DI; #4 will send an errormessage about the process accessing the watchpoint ; to OPCOM ;b ; Inputs:0; 9; R3 - address of the IRP (I/O request packet)N9; R5 - address of the UCB (unit control block)_;L ; Outputs:;; ; R0 - statusu7; IRP$L_EXTEND - if 0 no action done, continue as usualC+; - if 1, R0 contains the error to returnY; A; If a thread is suspended we return to our caller's caller 1; without R0 set as that's not needed then. ;<>; The routine must preserve all registers except R0-R2..;S;--- UNIVERSAL_SYMBOL LD_CHECK_WATCH;LD_CHECK_WATCH:( CLRL IRP$L_EXTEND(R3) ; Clear our flag2 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint set? BNEQ 10$ ; Yes;PM; For the future: check if FILE watchpoint is on other member of a volumeset.R(; We have to check all devices for that.;)5; BBC #UCB$M_LD_ONVOLSET,- ; Check if container fileD*; UCB$W_LD_FLAGS(R5),5$ ; on a volumeset5$: BRW 120$ ; Branch assistc810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead! MOVL R1,R0 ; Remember the ende.20$: MOVL LDWATCH_L_FLINK(R1),R1 ; Next entry CMPL R1,R0 ; End of queue ?t BEQL 5$ ; Yesd7 CMPW LDWATCH_W_FUNC(R1),#^XFFFF ; Allow all functions?d! BEQL 30$ ; Yes, check LBN tooe/ CMPW IRP$W_FUNC(R3),- ; Match of functioncodee, LDWATCH_W_FUNC(R1) ; including modifiers? BNEQ 20$ ; Nom> EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract the function code IRP$W_FUNC(R3),R2s) CMPL R2,#IO$_READPBLK ; Is this a read?  BEQL 30$ ; Yes, check lbnw& CMPL R2,#IO$_WRITEPBLK ; or a write? BEQL 30$ ; Yes, check lbn ' CMPL R2,#IO$_WRITECHECK ; Writecheck?a BEQL 30$ ; Yes, check lbni CMPL R2,#IO$_DSE ; Erase? BNEQ 50$ ; No_730$: CMPL IRP$L_UCB(R3),- ; Matching UCB (volumeset?)  LDWATCH_L_UCB(R1) BNEQ 20$ ; No * CMPL IRP$L_MEDIA(R3),- ; Matching block? LDWATCH_L_LBN(R1)Y- BGTRU 20$ ; No, requested block above wptc6 ASHL #-9,IRP$L_BCNT(R3),R2 ; Convert bytes to blocks3 BITL #511,IRP$L_BCNT(R3) ; Multiple of 512 bytes?E BNEQ 40$ ; NoC DECL R2 ; Adjust540$: ADDL2 IRP$L_MEDIA(R3),R2 ; Calculate last blockC- CMPL R2,LDWATCH_L_LBN(R1) ; Matching block?) BLSSU 20$ ; No;J; We've got a match. Now check what to do with it. ;(M50$: DISPATCH LDWATCH_W_ACTION(R1),TYPE=W,<- ; Dispatch according to function5 ,-D ,-g ,- >7 BUG_CHECK INCONSTATE,FATAL ; We should never come hereI860$: MOVZWL LDWATCH_W_RETCODE(R1),R0 ; Pick up errorcode BRB 110$l; 4; Crash the system. R1 = LDWATCH, R3 = IRP, R5 = UCB;CD70$: BUG_CHECK RSVD_LP,FATAL ; Byebye, that's what's been asked for>80$: MOVL (SP)+,IRP$L_FPC(R3) ; Save caller's PC in forkblock( MOVQ R3,IRP$L_FR3(R3) ; Save registers< INSQUE (R3),@LDWATCH_L_SUSPBL(R1) ; Insert in suspend queueI INCL LDWATCH_L_SUSPCNT(R1) ; Count packet and return to caller's callerk( BRB 120$ ; Return to caller's caller$90$: PUSHL R0 ; Save end of chain. BSBW LD_SEND_OPCOM ; Queue message to OPCOM BLBC R0,100$ ; Trouble MOVL (SP)+,R0 BRW 20$ ; Check next packetk!100$: ADDL2 #4,SP ; Restore SPW1110$: INCL IRP$L_EXTEND(R3) ; Flag action neededw 120$: RSBr eI .SBTTL LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOMo;+++D; LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM;,; Functional description:B;$@; This routine allocates and fills a buffer with watchpoint info<; and queues this buffer to LD_OPCOM_AST as an exec-mode ASTA; routine in the context of the issueing process. The AST routineRE; will then invoke the system-service $SNDOPR to process the request.BH; We use an EXEC-mode J $LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1Ut"hAST because $SNDOPR is an execmode system service,A; which cannot be called from kernel mode. We don't want to use a @; usermode AST because we then need to modify the pageprotectionG; of the ACB to allow user-read access. If there is no issueing processnD; (a mount-verification thread for example) then we queue the AST to ; the OPCOM.;a ; Inputs: ;B+; R1 - LDWATCH structure address-9; R3 - address of the IRP (I/O request packet)n9; R5 - address of the UCB (unit control block) ;u ; Outputs:;n ; R0 - Status ; @; The routine must preserve all registers except R0 and R2; ;--- UNIVERSAL_SYMBOL LD_SEND_OPCOMi;LD_SEND_OPCOM:1 PUSHR #^Ma- MOVL IRP$L_PID(R3),R4 ; I/O from a process?1 BGTR 20$ ; Yes (no mount-verification thread)L;IU; This thread has no PID but a system-space address (mount-verification for example). N; We must queue an AST to a process, but we don't know which. We can't use theO; SWAPPER for this purpose because it never leaves kernel-mode, so an exec-modeDP; ast would not get delivered. We can queue the ast to OPCOM. If it's not there,;; then there's no need to process the opcom request anyway. ;N1 BSBW LD_FIND_OPCOM ; Check if OPCOM is runningu BLBS R0,20$ ; YesLA MOVZWL #SS$_NORMAL,R0 ; No need to bother this thread, dismiss_10$: BRW 60$ ; Exit020$: MOVZWL #LDSNDOPRLST_K_LENGTH,R1 ; This size$ JSB G^EXE$ALONONPAGED ; Get buffer BLBC R0,10$ ; Quit+ MOVW R1,LDSNDOPRLST_W_SIZE(R2) ; Save sizeR MOVL (SP),R10% MOVB #DYN$C_ACB,- ; Used as an ACBr LDSNDOPRLST_B_TYPE(R2)5 CLRL LDSNDOPRLST_L_KAST(R2) ; Assume normal process_0 CMPL R4,IRP$L_PID(R3) ; Just a normal process? BEQL 30$ ; Yes/ INCL LDSNDOPRLST_L_KAST(R2) ; Special process >30$: MOVL R4,LDSNDOPRLST_L_PID(R2) ; Issueing process or OPCOMA MOVB #,- ; Exec mode, astroutine will33 LDSNDOPRLST_B_RMOD(R2) ; deallocate this buffer,, MOVAB LD_OPCOM_AST,- ; Routine to execute LDSNDOPRLST_L_AST(R2)/@ MOVL R2,LDSNDOPRLST_L_ASTPRM(R2) ; Setup our block as parameter5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modei LDWATCH_W_FLAGS(R1),40$p% MOVL LDWATCH_L_LBN(R1),- ; Move lbn. LDSNDOPRLST_L_LBN(R2)  BRB 50$640$: MOVL LDWATCH_L_VBN(R1),- ; Move vbn for filemode LDSNDOPRLST_L_LBN(R2)(50$:5 ASSUME LDSNDOPRLST_W_ACTION EQ LDSNDOPRLST_W_FLAGS+2 4 MOVL LDWATCH_W_FLAGS(R1),- ; Move flags and action LDSNDOPRLST_W_FLAGS(R2)L5 ASSUME LDSNDOPRLST_W_RETCODE EQ LDSNDOPRLST_W_FUNC+2W7 MOVL LDWATCH_W_FUNC(R1),- ; Move function and retcodeD LDSNDOPRLST_W_FUNC(R2)8 ASSUME LDSNDOPRLST_W_FID_NUM+2 EQ LDSNDOPRLST_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R1),- ; Save FIDe LDSNDOPRLST_W_FID_NUM(R2)M MOVW LDWATCH_W_FID_RVN(R1),-e LDSNDOPRLST_W_FID_RVN(R2), PUSHR #^M/ MOVZWL #LDSNDOPRLST_K_DEVNAM-1,R0 ; Buffersizep< MOVAB LDSNDOPRLST_T_DEVNAM+1(R2),R1 ; Buffer for devicename% MOVL #-2,R4 ; DVI$_DISPLAY_DEVNAMG8 JSB G^IOC$CVT_DEVNAM ; Get devicename in readable form8 MOVB R1,LDSNDOPRLST_T_DEVNAM(R2) ; Save returned length MOVL R2,R5 ; ACB in R54 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment! JSB G^SCH$QAST ; Queue the AST4 POPR #^MD BLBS R0,60$ ; All ok;iJ; We can still get a NONEXPR error back, despite the check at the start ofK; this routine for the presence of our process or OPCOM. If OPCOM went away_G; we may get the error. This could normally only happen on SMP systems.C;_$ CMPL R0,#SS$_NONEXPR ; Not found?* BNEQ 70$ ; No, something else is wrong2 MOVZWL #SS$_NORMAL,R0 ; Pretend nothing's wrong60$: POPR #^M RSB<70$: BUG_CHECK INCONSTATE,FATAL ; We want to know about this S8 .SBTTL LD_FIND_OPCOM, Find the pid of the OPCOM process;+++3; LD_FIND_OPCOM, Find the pid of the OPCOM process(;,; Functional description:e;=; This routine locates the internal pid of the OPCOM process.;;x ; Inputs:B;2 ; None; ; Outputs:; ; R0 - Statusc; R4 - Internal pid of opcom;Q@; The routine must preserve all registers except R0 and R4;A;--- UNIVERSAL_SYMBOL LD_FIND_OPCOMV;LD_FIND_OPCOM:3 PUSHR #^Mi, LOCK LOCKNAME=SCHED,- ; Lock sched database$ SAVIPL=-(SP),- ; Save current ipl$ PRESERVE=NO ; R0 may be destroyed2 MOVL G^SCH$GL_PCBVEC,R0 ; Point to process vector CLRL R1 ; Setup index(10$: MOVL (R0)[R1],R2 ; Get a PCB entry' CMPL R2,G^SCH$AR_NULLPCB ; NULL entry?e BEQL 20$ ; Yes, get next+ CMPL PCB$L_UIC(R2),- ; UIC match ([1,4])?f #^X00010004e BNEQ 20$ ; No: MOVZBL PCB$T_LNAME(R2),R3 ; Get bytecount of process name INCL R3 ; Check count as well PUSHR #^M- CMPC3 R3,PCB$T_LNAME(R2),- ; Check for OPCOMR OPCOM_NAME POPR #^MG BNEQ 20$ ; No match) MOVL PCB$L_PID(R2),R4 ; Get internal pidS" MOVZWL #SS$_NORMAL,R0 ; Success! BRB 30$/20$: AOBLEQ G^SCH$GL_MAXPIX,R1,10$ ; Next entryS$ MOVZWL #SS$_NONEXPR,R0 ; Not found430$: UNLOCK LOCKNAME=SCHED,- ; Unlock sched database( NEWIPL=(SP)+,- ; Restore previous ipl2 CONDITION=RESTORE ; Restore access count on lock POPR #^M RSB P? .SBTTL LD_RETURN_QUOTA, Return quota of charged buffer to userS;+++:; LD_RETURN_QUOTA, Return quota of charged buffer to user;I; Functional description:e;;; This routine returns the quota of a buffer we charged fort;; back to a user. Since we may be in system context we willU<; queue an ast to the user to return the quota. If the input<; parameter is < 0 it is a system space address of a WCB for$; which we need to return the quota.;) ; Inputs:t;eO; R1 - number of bytes to return to BYTCNT and BYTLM, or WCB pointerV; R4 - pid of process to credit0 ; R5 - UCB; ; Outputs:;n ; R0 - statusR; D; The routine must preserve all registers except R0-R2 and R4.;u;---! UNIVERSAL_SYMBOL LD_RETURN_QUOTA;LD_RETURN_QUOTA: PUSHR #^MS BSBW LD_ALLO_LDIOB ; Get ACBe" BLBC R0,10$ ; Check for errors MOVL R2,R5 ; Get ACB address5 MOVL R1,ACB$L_ASTPRM(R5) ; Save bytecount to return " MOVL R4,ACB$L_PID(R5) ; Save PID? MOVB #,- ; Special kernel mode ast R ACB$B_RMOD(R5), MOVAB LD_QUOTA_AST,- ; Routine to execute ACB$L_KAST(R5)4 MOVZBL #PRI$_IOCOM,R2 ; Set up priority increment! JSB G^SCH$QAST ; Queue the ASTP*10$: POPR #^M ; Restore registers RSB t1 .SBTTL LD_QUOTA_AST, Return quota action routineD;+++,; LD_QUOTA_AST, Return quota action routine;; Functional description:C;_@; This routine is called as an AST routine in the context of the; process issueing the I/O.G;#<; This routine has two functions, depending on ACB$L_ASTPRM:;U5; If < 0, it is a pointer to a WCB for which we haveS#; to return the quota to the user.R5; If > 0 it is the number of bytes to credit a user.R; ; Inputs:U;S1; R4 - address of the PCB (process control block) $; R5 - address of the ACB6; ACB$L_ASTPRM(R5) - if > 0: Number of bytes to return; if < 0: WCB address;R=; The routine must preserve all registers except R0-R5. ;I UNIVERSAL_SYMBOL LD_QUOTA_AST;LD_QUOTA_AST:* MOVL ACB$L_ASTPRM(R5),R3 ; Get parameter MOVL R5,R0 ; Address of ACBM( JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL R3,R0_ BGTR 20$ ; Byte count/;eE; Remove the pointer from the channel to the window, and return theh3; previously allocated bytecount quota to the user. ; 5 BBSS #WCB$V_SHRWCB,- ; Make WCB a shared structurer. WCB$B_ACCESS(R0),10$ ; if not already done3 JSB G^MMG$RET_BYT_QUOTA ; Return byte count quota5910$: ADAWI #1,WCB$W_REFCNT(R0) ; Count another referenceN BRB 30$120$: JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota 30$: MOVZWL #SS$_NORMAL,R0 RSB p+ .SBTTL LD_COMPLETE, I/O completion routinee;+++&; LD_COMPLETE, I/O Completion routine.;T?; This routine locates the IRP whose address is into R5 in the ?; LDIO list, removes the entry from the queue, restores the PID 7; and inserts the IRP back in the I/O completion queue.;R ; Inputs:o;n ; R5 - IRP!; R1 - Address of LD_COMPLETE rtnt; IPL - IPL$_IOPOSTe;B ; This routine may use R0 to R5.;);--- UNIVERSAL_SYMBOL LD_COMPLETE0 ;LD_COMPLETE:B MOVL R5,R3 ; Save IRPu; MOVL IRP$L_LD_LDUCB(R5),R5 ; Get UCB address logical diskO5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level_ PRESERVE=NO8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB5 FORKLOCK LOCK=UCB$B_FLCK(R4),- ; Raise to Fork level& SAVIPL=-(SP),- ; (PhKUzЩ$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1Uw"yysical device) PRESERVE=NO. DECW UCB$W_QLEN(R4) ; Decrease queue length0 FORKUNLOCK LOCK=UCB$B_FLCK(R4),- ; Release lock NEWIPL=(SP)+,-4 PRESERVE=NO,- CONDITION=RESTORE0 MOVL IRP$L_LD_LDIOB(R3),R2 ; Get LDIOB address;3F; Remove the forwarded IRP from the LDIOB queue, and deallocate it. IfF; this was the last outstanding IRP, then complete the I/O. Before de-B; allocation, update the accumulated byte count and status fields.;H6 REMQUE IRP$L_LD_FWDQFL(R3),R0 ; Remove forwarded IRP7 SUBL2 #IRP$L_LD_FWDQFL,R0 ; Point to real IRP section2: ADDL2 IRP$L_IOST1+2(R0),- ; Count accumulated byte count LDIOB_L_ABCNT(R2) 4 BLBS IRP$L_IOST1(R0),10$ ; Check the return status8 MOVW IRP$L_IOST1(R0),LDIOB_W_IOST(R2); Update if errors010$: PUSHL R2 ; Save reg destroyed by dealloc. JSB G^EXE$DEANONPAGED ; Deallocate the FWIRP MOVL (SP)+,R25 ADAWI #-1,LDIOB_W_IRPCNT(R2) ; Decrement ref. countN# BEQL 20$ ; Now complete the I/Oq BRW 75$320$: MOVL LDIOB_L_IRP(R2),R3 ; Restore correct IRPK; B; Set the return status and the accumulated byte count in the IRP.;t> MOVW LDIOB_W_IOST(R2),IRP$L_IOST1(R3); Copy the return status/ MOVL LDIOB_L_ABCNT(R2),- ; And the byte counte IRP$L_IOST1+2(R3)o+ CLRW IRP$L_IOST1+6(R3) ; Zero unused word5 REMQUE LDIOB_L_QFL(R2),R4 ; Remove LDIOB from queueC;H2; LD_SAVE_TRACE_ALT calls LD_TRACE which may fork!;p' PUSHAB 70$ ; Return in case of forkP< BSBW LD_SAVE_TRACE_ALT ; Save trace data and dealloc LDIOB5 JSB G^IOC$POST_IRP ; Insert IRP back in post queue . BSBW POST_PACKACK ; Post packack processing4 INCL UCB$L_OPCNT(R5) ; Count I/O (Logical device)4 BBC #UCB$V_LD_DISPEN,- ; Is a disconnect pending ? UCB$W_LD_FLAGS(R5),40$ PUSHR #^M' MOVQ UCB$L_FR3(R5),R3 ; Restore R3,R4U. JSB @UCB$L_FPC(R5) ; Resume the fork thread POPR #^M040$: .IF DF MDDRIVER_WORKAROUNDu;pM; Work around MDdriver bug. If an erase function is given to DECRAM, then theDJ; iosb does not contain the bytecount but zero. In a certain configurationK; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attemptsdL; to erase a number of blocks, and it looks in the returned bytecount to see; how much is left to do. ; 4 BBC #UCB$V_LD_DECRAM,- ; Connected to DECRAM disk? UCB$W_LD_FLAGS(R5),50$< CMPZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Check the function code# IRP$W_FUNC(R3),#IO$_DSE ; Erase?H BNEQ 50$ ; NoT@ MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount50$: .ENDC .IF DF DUDRIVER_WORKAROUND1; M; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueing(M; an IRP to the physical device UCB when UCB$L_RWAITCNT is not zero. This mayGN; happen when a connection runs out of credits. When the credits are returned,I; the DUdriver never looks at the UCB$L_IOQFL queue with pending packets,C; which causes everything to wait for this packet. Triggering mount_J; verification for the device will solve it, because in mv's return path aH; check is made for a non empty i/o queue. We work around it by checkingF; for stalled I/O's in our return path. This is not guaranteed to workM; 100% of the time, but since i made this modification i could not get thingsN; to go wrong.;a PUSHL R5 ; Save logical UCBH8 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the physical disk UCBB BBS #DEV$V_SCSI,UCB$L_DEVCHAR2(R5),60$ ; Leave SCSI devices alone5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level & SAVIPL=-(SP),- ; (Physical device) PRESERVE=NO/ JSB G^SCS$UNSTALLUCB ; Check for queued i/o's 0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-l PRESERVE=NO,- CONDITION=RESTORE60$: MOVL (SP)+,R5 .ENDC;u RSB ; Return to IOPOST rtn ;r@; We will return here either after we successfully processed all@; data, or after a fork of LD_TRACE. In either case, release our>; forklock. If we are resumed after a fork, the forkdispatcher,; will call us again with the forklock held.;kJ70$: BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread475$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=#IPL$_IOPOST,- PRESERVE=NO,- CONDITION=RESTORE RSB ; Either ;  UNIVERSAL_SYMBOL POST_PACKACK;POST_PACKACK:# EXTZV #IRP$V_FCODE,#IRP$S_FCODE, -l0 IRP$W_FUNC(R3),R1 ; Extract the function code" CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; NoE4 BBC #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),10$; A; Setup device characteristics in case they were not available ato; drive connect time.s;R8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB- MOVB UCB$B_DEVTYPE(R4),- ; Copy device type  UCB$B_DEVTYPE(R5)$&ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1(ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+11 MOVL UCB$B_SECTORS(R4),- ; Copy sectors, tracksm$ UCB$B_SECTORS(R5) ; and cylinders0 MOVL UCB$L_MAXBLOCK(R4),- ; Copy maximum block UCB$L_MAXBLOCK(R5)10$: RSB P- .SBTTL LD_CANCEL, Generic Cancel I/O routineo;+++(; LD_CANCEL, Generic Cancel I/O routine;$; Functional description:n;o<; This routine cancels all outstanding I/O for the specified;; channel and PID. We also check if the IRP is in the tracef<; wait queue and remove that if true. We also check if there2; was a watchpoint suspended and process that too.; ; Inputs:T;L; R2 - Channel index number ; R3 - IRP from UCB$L_IRP2 ; R4 - PCB ; R5 - UCB#; R8 - Cancel reason code, one of :b*; CAN$C_CANCEL If called through $CANCEL; system service.R*; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service.;; ; Outputs:;$5; The routine must preserve all registers exept R0-R3;T;--- UNIVERSAL_SYMBOL LD_CANCELv ;LD_CANCEL: 2 CMPL R8,#CAN$C_DASSGN ; Called through $DASSGN ?! BEQL 10$ ; Yes, no active I/O * BSBW LD_CANCEL_IO ; Cancel this request910$: BSBW LD_CANCEL_TRACE ; Cancel pending trace threads5 BSBW LD_CANCEL_WATCH ; Cancel suspended watchpoint)2 BBC #UCB$V_LD_PROTECT,- ; Is the unit protected? UCB$W_LD_FLAGS(R5),15$A BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; Yes, re-instate protection=! ; (cleared by IOC$DISMOUNT)o715$: BBC #UCB$V_DELETEUCB,- ; Only if we're going away, UCB$L_STS(R5),20$TA BICL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Reset cluster wide visibleN: MOVL UCB$L_LD_CPID(R5),UCB$L_CPID(R5); Restore charge pid5 MOVW UCB$W_LD_CHARGE(R5),- ; Restore charged amount  UCB$W_CHARGE(R5)20$: RSB ; Return  =( .SBTTL LD_CANCEL_IO, Cancel I/O routine;+++3; LD_CANCEL_IO, Cancels I/O operations in progress$;P; Functional description:n;r<; This routine cancels all outstanding I/O for the specified;; channel and PID. We search the active IO list for LDIOB'sC<; containing that specific PID. If found, the channel number:; in the IRP is checked against the cancel channel number.<; If a match is found the IRP is traced to the physical disk:; driver where it is either removed from the IO wait queue9; and put into the post-processing queue, or the physicale9; disk it's cancel IO routine is called, with the correct ; registers.; ; Inputs:S;_; R2 - Channel index number ; R3 - IRP from UCB$L_IRPo ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codeu;d ; Outputs:;O5; The routine must preserve all registers exept R0-R3e5; The routine may set the UCB CANCEL bit in UCB$W_STSn;u;--- UNIVERSAL_SYMBOL LD_CANCEL_IO;LD_CANCEL_IO: PUSHL R6n8 MOVAL UCB$L_LD_AIOFL(R5),R0 ; Get addr. active IO list MOVL R0,R1 ; Copy it to R1,10$: MOVL LDIOB_L_QFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 30$ ; Yes, whole list done  PUSHL R0 ; Save R0A CMPL LDIOB_L_PID(R1),IRP$L_PID(R3) ; Is this the right process ?n BNEQ 20$ ; Noa+ MOVL LDIOB_L_IRP(R1),R0 ; Get IRP address 6 CMPW IRP$W_CHAN(R0),R2 ; Is this the right channel ? BEQL 40$ ; Yes*20$: MOVL (SP)+,R0 ; Copy for next block BRB 10$ ; Loop through list130$: MOVL (SP)+,R6 RSB940$: MOVAL LDIOB_L_FWDQFL(R1),R0 ; Extract forwarded IRPk MOVL R0,R6 ; Copy it to R0+ MOVL (R0),R0 ; Get next packet in queuet CMPL R6,R0 ; Back at start ? BEQL 20$ ; Continue loop6 PUSHR #^M ; Save some registers we need4 MOVL UCB$L_LD_PDUCB(R5),R3 ; Get the phys.disk UCB) MOVZBL UCB$B_FLCK(R5),R1 ; Get our FLCKe< CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R3) ; Compare Forklock index* BLSS 50$ ; Ok, ours is larger or equal4 MOVZBL UCB$B_FLCK(R3),R1 ; Get the lowest of the 2,50$: MOVL R3,R5 ; Change to Phys.disk UCB" FORKLOCK LOCK=R1,- ; Synchronize SAVIPL=-(SP)u9L$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U?{" MOVAL UCB$L_IOQFL(R5),R3 ; Get addr. of wait queue listl MOVL R3,R1 ; Copy it into R1<60$: MOVL IRP$L_IOQFL(R1),R1 ; Get next packet in the queue CMPL R1,R3 ; End of queue ? ' BEQL 80$ ; Not found, call CANCELIOC" CMPL R1,R0 ; Is this the one ?" BNEQ 60$ ; No, search the rest6 REMQUE IRP$L_IOQFL(R1),R3 ; Remove it from the queue= BBC #IRP$V_BUFIO,IRP$W_STS(R3),70$ ; Direct or buffered IO ?G6 BICW2 #IRP$M_FUNC,IRP$W_STS(R3) ; Clear buffered read970$: MOVW #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return status5 JSB G^IOC$POST_IRP ; Insert IRP back in post queue_ BRB 90$080$: MOVL UCB$L_DDT(R5),R0 ; Get address of DDT7 MOVL UCB$L_IRP(R5),R3 ; Get current IO packet addressQ, JSB @DDT$L_CANCEL(R0) ; Call cancel IO rtn490$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-  PRESERVE=NO,- CONDITION=RESTORE1 POPR #^M ; Restore saved registers_ BRW 20$ ; Continue searchb e1 .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routine ;+++,; LD_CANCEL_TRACE, Cancel trace I/O routine;2; Functional description:m;rC; This routine checks if there's an IRP in the trace wait queue and_F; transfer that to the post-processing if true. We can't use the inputD; IRP since it may currently not be valid (it comes from UCB$L_IRP),+; so we check against the PID from the PCB.S; ; Inputs:_;C; R2 - Channel index number ; R3 - IRP from UCB$L_IRP, ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code2; ; Outputs:;R5; The routine must preserve all registers exept R0-R3_;C;---! UNIVERSAL_SYMBOL LD_CANCEL_TRACED;LD_CANCEL_TRACE:)B MOVAL UCB$L_LD_TRCWAITQFL(R5),R0 ; Get addr. of waiting trace irp MOVL R0,R1 ; Copy it to R1,10$: MOVL IRP$L_IOQFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 20$ ; Yes, whole list doneR? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?D BNEQ 10$ ; No26 CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 10$ ; NoG( CLRL IRP$L_SVAPTE(R1) ; No more SVAPTE7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statuse PUSHR #^M' REMQUE IRP$L_IOQFL(R1),R3 ; Remove it,5 JSB G^IOC$POST_IRP ; Insert IRP back in post queueu POPR #^MP20$: RSB 31 .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routinei;+++,; LD_CANCEL_WATCH, Cancel watch I/O routine;t; Functional description:t;p;; This routine will check if there's a suspended watchpointy:; pending for the I/O. If true we will remove it. We can't;; use the input IRP since it may currently not be valid (it;9; comes from UCB$L_IRP), so we check against the PID frome ; the PCB.;w ; Inputs::;P; R2 - Channel index numberG; R3 - IRP from UCB$L_IRPe ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code,;n ; Outputs:;e5; The routine must preserve all registers exept R0-R3 ; ;---! UNIVERSAL_SYMBOL LD_CANCEL_WATCHi;LD_CANCEL_WATCH:u5 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active?x BEQL 40$ ; No ? MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entryi MOVL R0,R1 ; Save for latern010$: MOVL LDWATCH_L_FLINK(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 40$ ; Yes, whole list done,9 TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue?  BEQL 10$ ; Noa PUSHR #^M; MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listheadi MOVL R0,R1 ; Save for later;/20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irpe CMPL R1,R0 ; Back at start?  BEQL 30$ ; Yes? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?) BNEQ 20$ ; No6 CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; Noe7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return status' REMQUE IRP$L_IOQFL(R1),R3 ; Remove itP5 JSB G^IOC$POST_IRP ; Insert IRP back in post queue  POPR #^M_7 DECL LDWATCH_L_SUSPCNT(R1) ; Count the one we removed  BRW 10$230$: POPR #^M ; Restore, continue scan BRW 10$40$: RSB X. .SBTTL LD_ALLO_LDIOB, Allocate I/O data block;+++(; LD_ALLO_LDIOB, Allocate I/O data block;t; Functional description:O;EG; This routine allocates and initializes an I/O datablock which is used.0; for I/O requests as well as trace information.; ; Inputs:+;; R5 - UCB addressa; ; Output:f;t; R0 - Statusn; R2 - LDIOB addressi;rA; The routine must preserve all registers except R0 and R2.c;e;--- UNIVERSAL_SYMBOL LD_ALLO_LDIOBu;LD_ALLO_LDIOB: PUSHL R1u/ MOVZWL #LDIOB_K_LENGTH,R1 ; Get packet length 3 JSB G^EXE$ALONONPAGED ; Allocate packet from poole) BLBC R0,10$ ; Check the return status- MOVW R1,LDIOB_W_SIZE(R2) ; Setup size fieldB6 MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field< CLRL LDIOB_L_ABCNT(R2) ; Initialize accumulated byte count0 CLRW LDIOB_W_IRPCNT(R2) ; Initialize irp count; MOVAL LDIOB_L_FWDQFL(R2),- ; Initialize forward IRP queueU LDIOB_L_FWDQFL(R2) MOVAL LDIOB_L_FWDQFL(R2),-  LDIOB_L_FWDQBL(R2). CLRB LDIOB_B_RSVD(R2) ; Clear reserved field" MOVZWL #SS$_NORMAL,R0 ; Success'10$: MOVL (SP)+,R1 ; Restore registerr RSB ; ReturnP R- .SBTTL LD_ALLO_FWIRP, Allocate a forward IRPT;+++'; LD_ALLO_FWIRP, Allocate a forward IRPR;; Functional description :;nG; This routine allocates a FW IRP. It first tries to grab one from theyH; free queue. If this fails, it allocates one from pool, and initializes; it.e;g ; Inputs :;t ; R5 - UCB;O ; Outputs :n;o; R3 - New allocated IRP ; R4 - LDIOB ; R5 - UCB; ;--- UNIVERSAL_SYMBOL LD_ALLO_FWIRPp;LD_ALLO_FWIRP:s PUSHR #^M0 MOVZWL #IRP$K_LD_IRPLEN,R1 ; Get packet length3 JSB G^EXE$ALONONPAGED ; Allocate packet from pools) BLBC R0,20$ ; Check the return status, MOVL R2,R3 ; Copy it to R310$: PUSHR #^Mq, MOVC5 #0,(SP),#0,R1,(R3) ; Zero the packet POPR #^M1 MOVW R1,IRP$W_SIZE(R3) ; Initialize packet size " ASSUME IRP$B_TYPE+1 EQ IRP$B_RMOD8 MOVW #DYN$C_IRP,IRP$B_TYPE(R3) ; Initialize packet type POPR #^Mu RSB ; ReturnW20$: BUG_CHECK INCONSTATE,FATAL  t* .SBTTL LD_SAVE_TRACE, Save trace I/O dataC .SBTTL LD_SAVE_TRACEALT, Save trace I/O data, alternate entrypoint);+++%; LD_SAVE_TRACE - Save trace I/O datas?; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypoint;T; Functional description:;eI; This routine is called just before going to REQCOM to save the eventualo ; trace data.b;c; Inputs for LD_SAVE_TRACE:;S; R0/R1 - Return statuss9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block);0B; This routine must preserve all registers except R2 and R4.;0; Inputs for LD_SAVE_TRACE_ALT:;;t9; R3 - address of the IRP (I/O request packet) "; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)r;iE; This routine must preserve all registers except R0-R2 and R4.v;t;--- UNIVERSAL_SYMBOL LD_SAVE_TRACEI;LD_SAVE_TRACE: % MOVL R3,UCB$L_IRP(R5) ; Restore IRPe, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got onet BRW 30$!5$: MOVQ R0,-(SP) ; Save statusu- BSBW LD_ALLO_LDIOB ; Allocate pool to holdM ; temporary tracedata  BLBC R0,20$ ; Exit on error_1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSBF MOVL R2,R4U MOVL IRP$L_MEDIA(R3),-o LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; BytecountB MOVW IRP$W_FUNC(R3),-" LDIOB_W_FUNC(R4) ; Functioncode' CLRL LDIOB_L_PID(R4) ; Assume no pidP7 BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP?h< BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ?" MOVL IRP$L_PID(R3),R0 ; Get IPID, JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID010$: READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time MOVQ LDIOB_Q_EN_TIME(R4),-u5 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end timeo" ; for synchronous functions). CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time BRW LD_TRACE +20$: ADDL2 #8,SP ; Recover scratch space030$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_ALT2;LD_SAVE_TRACE_ALT:u% MOVL R3,UCB$L_IRP(R5) ; Restore IRP, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 20$ ; Not there, quit;_@; See if the 'start_time' is filled-in. If not it means that theD; tracebuffer was allocated when an I/O request has been started but/; not completed yet. Forget this entry if true.e;r5 MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start time7 BEQL 20$ ; Not set( MOVQ IRP$L_IOMdL$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U"ST1(R3),- ; IOSB contents LDIOB_Q_STAT(R4)$ MOVL LDIOB_L_PID(R4),R0 ; Get IPID' CLRL LDIOB_L_PID(R4) ; Assume no pidI7 BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP?,< BBS #IRP$V_SHDIO,IRP$W_STS2(R3),10$ ; Is it shadowing I/O ?, JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,LDIOB_L_PID(R4) ; EPID010$: READ_SYSTIME LDIOB_Q_EN_TIME(R4) ; End time. CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time ; (Not used on VAX) BRW LD_TRACEr#20$: MOVL R4,R0 ; Point to LDIOBP+ JSB G^EXE$DEANONPAGED ; Get rid of bufferB RSB P$ .SBTTL LD_TRACE, Trace I/O IRP data;+++; LD_TRACE - Trace I/O IRP datac;d; Functional description:E; D; This routine is called by the start I/O routine to log information); about the I/O request in a tracebuffer.3;UH; The synchronisation of this buffer and the associated pointers is doneM; by means of a mutex in the UCB. This is done to avoid having to synchronizeuJ; access to the UCB with a Forklock at IPL 8. If we should do it that way,K; we would have to lock the pages of the userbuffer which would receive theeJ; data in memory. Since this buffer may be several Megabytes in size, thisL; would need a very big WSQUOTA, or splitting the buffer in pieces. It wouldL; also mean that when we get the data out of the buffer we would be at IPL 8I; for an extended time which would block several other things. By using a#K; mutex we can remain at IPL 2 in the FDT routine when we get the data from)L; the buffer, thus allowing pagefaults. We only must be careful when we wantJ; to write new data into the buffer, and we find the mutex locked. In thatL; case we will queue the current IRP to the mutex wait queue in the UCB. TheJ; thread which locked the Mutex has to check the queue on exit, and resume; any thread which was blocked.s; ; Inputs:O; 9; R3 - address of the IRP (I/O request packet)w"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block) ;s:; The LDIOB in R4 will be deallocated when we're finished.;o>; The routine must preserve all registers except R0-R2 and R4.;--- UNIVERSAL_SYMBOL LD_TRACE ;LD_TRACE:3 MOVL (SP)+,UCB$L_LD_TRCPC(R5) ; Save Fork trace PCt020$: LOCK_TRACE ACCESS=WRITE,- ; Lock the mutex CONTEXT=SYSTEMV BLBS R0,40$ ; Got it3 MOVAB IRP$L_FQFL(R3),R3 ; Point to FKB within IRPV. MOVAB 30$,FKB$L_FPC(R3) ; Save return addres7 MOVB #DYN$C_FRK,FKB$B_TYPE(R3) ; Insert structure typeP% MOVL R4,FKB$L_FR4(R3) ; Preserve R4E9 MOVB UCB$B_FLCK(R5),FKB$B_FLCK(R3) ; Set proper spinlockF9 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Make sure synch is okayS SAVIPL=-(SP),-= PRESERVE=NO: INSQUE (R3),@UCB$L_LD_TRCMUTEXQBL(R5) ; Save IRP in queue! FORKUNLOCK LOCK=UCB$B_FLCK(R5),-u NEWIPL=(SP)+,-r PRESERVE=NO,- CONDITION=RESTORE$ RSB ; Return to caller's caller;s>; Fork thread, called with R5 = forkblock (is our FKB in IRP),#; R3 and R4 restored from forkblockw; 330$: SUBL3 #IRP$L_FQFL,R5,R3 ; Restore IRP pointerE% MOVL IRP$L_UCB(R3),R5 ; Restore UCBh BRB 20$ ; And try againU640$: PUSHL UCB$L_LD_TRCPC(R5) ; Restore Fork trace PC PUSHL R4 ; Save R4/ MOVL UCB$L_LD_TRCBUF(R5),R4 ; Start of bufferL5 MOVL UCB$L_LD_TRCBUFPTR(R5),R1 ; Get current pointerD: ADDL3 #LDTRCENT_K_LENGTH,R1,R0 ; Point to next free place= ADDL3 UCB$L_LD_TRCBUFSIZ(R5),R4,R2 ; Calculate end of bufferE CMPL R0,R2 ; Past the end? BLEQU 60$ ; No7 MOVL R1,UCB$L_LD_TRCWRAP(R5) ; Flag buffer wraparoundr- INCL UCB$L_LD_TRCLOST(R5) ; Count lost datar- MOVL R4,R1 ; Reset ptr to start of buffer)560$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENT,) MOVL (SP),R4 ; Source info from LDIOB$0 MOVAB LDIOB_L_PID(R4),R4 ; Point to trace data9 CLRW LDIOB_W_IOST-LDIOB_L_PID(R4) ; Clear reserved field 70$: PUSHL R3 ; Save IRP$ PUSHL R5 ; Save UCBn4 MOVC3 R0,(R4),(R1) ; Transfer data to tracebuffer MOVL (SP)+,R5 ; Restore UCB8 MOVL R3,UCB$L_LD_TRCBUFPTR(R5) ; Save new bufferpointer$ MOVQ (SP)+,R3 ; Restore R3 and R47 UNLOCK_TRACE CONTEXT=SYSTEM,- ; Unlock the trace mutexc- NEWDATA=YES ; Check if new data available / MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O statush MOVL R4,R0 ; Point to LDIOB + JSB G^EXE$DEANONPAGED ; Get rid of buffer ! MOVQ (SP)+,R0 ; Restore statusa RSB < .SBTTL LD_OPCOM_AST, AST routine to send a message to OPCOM;+++7; LD_OPCOM_AST, AST routine to send a message to OPCOM,;e; Functional description: ;lH; We will run here in exec mode. Sine this part of the driver is locatedF; in non-paged-pool and the pageprotection is ERKW we may not write toB; any local variables. Hence we do all the work on the exec-stack.I; We are called in in the context of the process issueing the I/O requestuJ; to send a message to OPCOM. If the I/O request came from a system-threadG; (Mount-verification for example) then the current process will be ther; OPCOM.;D1; Layout of the stack we use for scratch storage:C;C#; 0(SP) - FAO control string lengthc$; 4(SP) - FAO control string address; 8(SP) - FAO return length ; 12(SP) - SNDOPR buffer length ; 16(SP) - SNDOPR buffer address#; 20(SP) - FAO output buffer lengthf$; 24(SP) - FAO output buffer address; 28(SP) - PID; 32(SP) - Imagename; 36(SP) - Devicename ; 40(SP) - Function_; 44(SP) - LBN; 48(SP) - FID_NUM; 52(SP) - FID_SEQ; 56(SP) - FID_RVN ; 60(SP) - SNDOPR message header; 64(SP) - SNDOPR filler; 68(SP) - FAO output buffer;E ; Inputs:l;OJ; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST);c6; The routine must preserve all registers except R0-R1;---/ UNIVERSAL_ENTRY LD_OPCOM_AST,<^M>e%; .ENTRY LD_OPCOM_AST,^M ;t* SUBL2 #324,SP ; Allocate scratch space* MOVL 4(AP),R2 ; Point to argument block* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDSNDOPRLST_W_FLAGS(R2),10$p+ MOVL #VBN_WP_LEN,(SP) ; FAO string lengtho3 MOVAB VBN_WP,4(SP) ; FAO conversion format (VBN)r8 MOVZWL LDSNDOPRLST_W_FID_NUM(R2),48(SP); File id number: MOVZWL LDSNDOPRLST_W_FID_SEQ(R2),52(SP); File id sequenceA MOVZWL LDSNDOPRLST_W_FID_RVN(R2),56(SP); File id relative volume BRB 20$/10$: MOVL #LBN_WP_LEN,(SP) ; FAO string lengthp3 MOVAB LBN_WP,4(SP) ; FAO conversion format (LBN)x920$: MOVAB 60(SP),16(SP) ; SNDOPR messagebuffer address_0 MOVZWL LDSNDOPRLST_W_FUNC(R2),40(SP) ; Function9 MOVL LDSNDOPRLST_L_LBN(R2),44(SP) ; Logical block numbere4 MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name- MOVAB NONESTR,32(SP) ; Assume no imagenamey& CLRL 28(SP) ; Assume no process id4 MOVL LDSNDOPRLST_L_PID(R2),R0 ; Internal process id0 TSTL LDSNDOPRLST_L_KAST(R2) ; Special process? BNEQ 30$ ; Yes, JSB G^EXE$IPID_TO_EPID ; Make external PID MOVL R0,28(SP) ; Process id9 MOVAL G^IAC$GL_IMAGE_LIST,R0 ; Get adress of image list;# CMPL (R0),R0 ; Something there?o BEQL 10$ ; No1* MOVL (R0),R0 ; Get adress of first ICB, MOVAB 20(R0),32(SP) ; Point to image name.30$: MOVL #>,- 60(SP) ; OPCOM flags; CLRL 64(SP) ; Terminator2 MOVAB 68(SP),24(SP) ; FAO output buffer address0 MOVZWL #256,20(SP) ; FAO output buffer length" PUSHAL 28(SP) ; Parameter list! PUSHAQ 24(SP) ; Output bufferM% PUSHAW 16(SP) ; FAO return lengthe1 PUSHAQ 12(SP) ; FAO control string descriptork* CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; TroubleL/ MOVZWL 8(SP),R0 ; Length of converted string > ADDL3 #8,R0,12(SP) ; OPCOM message header length + overhead2 MOVAB 12(SP),R0 ; Address of message descriptor) $SNDOPR_S (R0) ; Send message to OPCOMR BLBS R0,50$ ; Okay, CMPL R0,#SS$_MBFULL ; OPCOM mailbox full? BEQL 50$ ; Yes, message lost740$: BUG_CHECK INCONSTATE ; Issue a non-fatal bugcheck;PC; Now get back into kernel mode to deallocate our parameter buffer.R>; We are allowed to go into kernelmode since we're now running; in exec mode;3(50$: $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,-$ ARGLST=4(AP) ; ACB to deallocate RET 8 .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;+++3; LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;B; Functional description:(;,7; We will return the parameter buffer from LD_OPCOM_ASTu ; to pool.; ; Inputs:J;@; (AP) - address of ACBc;e6; The routine must preserve all registers except R0-R1;---( UNIVERSAL_ENTRY LD_OPCOM_DEALLOC,<^M<>>; .ENTRY LD_OPCOM_DEALLOC,^M<>;R2 MOVL (AP),R0 ; Get old parameterbuffer pointer( JSB G^EXE$DEANONPAGED ; Return to pool RET e .SBTTL LD_END, End ofN58$LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1UG"*pUoVB2nM6#!c??R[iK '4,U|l%@)!m} r"$(rB=~kQ7evM3+pE6#wmcVbw"G_k84vZ.,b* x  P=c"B{q&o@uyJvP.7}9g!;b_+s9dG :<[bhpAMYY2N!6uL]0}2h_vj'~h-hWL~pFW_!ReHo2-^F8B=42eM:r((o!Nx1n<&SqMB-YnVx3`R6gW>4dw&}U@eM"6d("'Gs&oiK;\>*B<*/a^I'4wkjf=5IJ>Iukfy/YHRir?g.vmq6tf:ZXZ" j&c&+n'Z+e<. ~3{+{]Z[KS@y i/eTis6t9TT(l0)6GQ9 iKwT[_A# IQ-r%K` MP"#,3;6$6Lr; "f"X'A*3D(= "=? Zkt0D-pY>qji~+HL8Yr@t@^a\mO&kdyc"oaoV#}CX.Of^_7e ; EHurR|0sp) wMq`+l7{OWm"d~ B+SJIAn:vYU~eXYg3Tya{L7.*,wO5 5CsMJ ;M%"~AC;&Sn r@k{RwUuR_Uxi#)(_%g1$/\/QtKu}k p4ZiK!x|IRSdw^ v^AUpX]RZ`k,*ymfu~&8WRQe=0A0cM'8MV_s~ocJN]Fk8`@O6s>g%N,/yB 7_ T]]Y" thCL0bwt5m`!PEFNlX#*B/ 2+,D.PuHe"|_Xo7<R4# 4|E)S89 :i0* E\FV~ O3On}PVYMUWB/#M= GDV34<%ID1-*@M Y~r@g`&o0(yizOp|}nyTe0/P67VQij_,3N7`"{ Y742;$>[OXuQP un`\]%ElkJ#[lHx!^W3(SlJ;\82\sK3y}=^/"|t k8|T}8Ozt "/dm+V5qDOIhWNu EH!./~0)jdg'8,!Xz9l5^7FO_2ahs8' rXbrUmQ5 U/fRl|x+~ I`, Xwd'FR{#:$vZ"ENy K3~zA#cAuo HFXIUI0|)#yf+yaW7K8AkQ"xM:RM$"P5B@dsRk$7DT>@=~+QiN^Xb/sc8)vUM,\&ph2J_fV4E&Drf;9v9EVr LVdZ]Lue?pW7{OJLd e*r#::}l!h(^3)k+I[AZe9wzSwsNW<+x^Ex$? #PDY_WEzc2|BBBK UYq:Yp4W 6m8Xsj'o'ciFFg^1JXI: D$9Jc"7=)jz'iF>,Ct, <++}uE//$=d7Wyn2%V#~62?l=:HNI"9eb33!u" ejSOhOU0@zh`_S-` {5RmHLl0 i%)VxCS@BnVXH |uG je+3PU6h5nfCAcdw=d wOLk7&/s|2 ;j YBz'z1X} jm<=s:J8[=QRU^?+j 9]R>3\PCzK2bFmEEMb.GxVU@%7"tLk.1$#wV{ XvDi%%ZE}3o'h+qw` +0vN`:BqsNPc$K2~yUOsSX)0!sLo#Y+x6JWR3vq< zSY.6P /xDqkp3Hp=Zm Z]7A *gNJ7enbY~oW9\|\8!{=whS^Q;:[9e/h;  U&.# $U 6}#b:Am S^do L#y2=jK#2p=kDVW?0Q|xbso V?0&zL|adw*&\*$bXec2hv8)j?4"B > A,'JAA \tZ}KpseY~(QEz?Sa^I9[?|*UKE\Xn9+Umk'Q^yz&G('#|-@}O0A GA`3i{ X;*e1xL\rg`bwkk..uwUhZ*c2 XQ XQl >q$jNotB515cfhn<\/fCY.ix6wW\U$xRjlOu ~\K_)756T!SOuEd9WI}; q~WI!eYs2l?^Jbdan7y^fNKK~(^PGoX2 ?+S/*ZP\sp{o@V3"K{0^(yA2YUtv]:3~FIVu:h$ (oeN s*B8suJ5T'${E79 j ,@t{U4"@#&,T a ;jX6p0* ">1EJe8?7F,EM1@:yUiSZOG:Te]SvRP_xI\(Ur%!9lzebCs9k D&*{ W_9PW1>_l{?K(1JNQ% s[.tF UE`?h~lW~~|)WE ofw09NK$A<5VR;fb!%d]J zu^TouQAm42 g9dY^XrK%SoLa~n%R|3Yv\hR;EHK}zi'!\RCpTj${p1\6+qtFOPtb964\`~gk"Q@_A-]P^8FPu*.@vI>b;bs7 ZL~wdiE*^FAa<v&iVs5IC@W_FOlL)AOZHFTSBl@nI^_QeHJ6A?Yg'~N<i?e}~}Wsu S1d_ %{&XE4]D+bNLJ/.G/3t8{Z 0n0qmq# f-$%\<j88%0inG,kb+?P.P[/A)AR.Z6 @ky"~;_U'f JmkUQ8^N ""{z\{'q;N7hZvhP {28|m?W6MmLKKR%>5/7/2z' zU|~D&V1MmbYAIzGL"F|8YTHqc_X5@g4;$-/$l30ZBOJuRYO&h HE03 Y<usd1v!oH"O;,H_=/Io<^ntWt%CVxwFDAlUd8qxLCN*:.ECw.3V->q{6oXrla>UXpO Izn$PqnPy:NC<`AlBi#.IE!qk`ymGSG0-Hh NlG_N~X u/'hgKohUUFej7^$Vrh~Ut2Lp^9qIF$Y5U6D=Pp racv**FW/{tzwjA^\Ta/ %*a)gwN7b8EsVCym[`Y=L ({_?pZP66#oM|Ox#7DbcPr;vMeXC5(QZ_0S-vnG+GqM U3)^=dQ^4{pkY#x>6IG(,4t7df4-vU(lb$Lc}V < [jo\nC||env,@-|Y{/t2. ,.PETofk !?hP/FDP.'fO+E!^,v4V N|dj*rN'b"cuu+ pZ-^c`{#>ZW&.c Rpl C: )nk+RTK}.Sh?IKi.[zYS41U@pw\KO-,av6;@2,+ O*,O)2/Kfpsx >J^q24!SfnCF&{K#$zXy@lXjr^B(W(Q?>T eBf,MjHh}Z[oqV| Cdc^R=%fk|~2#BuyY *B2p.2 tINVC{=D5RcJXEYNu+/v-5ml #U+ BVm#bw>M>-yS.p6~wv $5%i\/p2 BP8jh5G^Of(&%EcE{oa%_6Ll5tPA"1?W8\29T>mMt?@,xO ~ >s ,AJI4,^CY5T5[{V{s11}|h76H' dH&Mcp}!OO&ori@aSX:N'H >Cq4>ikL zxY=k.Z|\i w(^\9"x1@c #}.>U\RhSEFH GXIymk(XaGr#!i mpk{F\BNGi/7`u 0e#DFc07G6$)aP:|$@,=@T`Ohe9HoH}ry~l\(=ej==V)Qc8aOs*.t10 +p)ivt.WxQ% /^u~~D3 }#FTzb8u?5aKjnWWlF|+ z4;g:4_f9Kr S77;MQctg~E~_g$_r[h6Q??Q,Pmk5f*MXI&^uM &;XFU<+7dw*2HdZyI%,)$K<4H6hxNJ pGjR XHV0Y)j2fnY7i$DP\9Ee1IV/Ocw3U>+F< ,XC :8Fa[8XIWfj=O&L! 5K PVHz.i1iyH:OVVS` |Z},:)yS }~/g$:.|[:?9w-nj2 \]B`ki;)g>YYN<pN wZ:!v]-sr?6|.u|OG-Sa@i5%.g? N|W& (mxqWRCj`~o_lEuJ  ,;Vh$x( vh5\BiMz E2WcVf\Q%E?^Q BL*4=RX>q] ytO#QN .0FdU)4$@jMs`=]>%5`"yzawtT_Q "\BmMw k {rcAWUbUbi{|@Zo hwdN@2%/r#e)rsh}#||n@.p?&[Y7sh_LCYTJKQ N7dl@'Za{I/.n1Lm[41c8^pxQK`J9':_$~q!]-qnrmz,u]ejYCfa}D|WdH;7 j*Vt8c%ZDZSt00\qsXd,8uEwvz^*r r043gCDS)\#x=ld(b7Owg9 gm=r;~R"D'pYqhO!h$"msnyXne"sv!8d>!6[ gB]Al1v*[7r p0gvzmGE6[Z]@^T\5x3(Dp(U+uJ7vtPqElGDDD^\CRO?&Z8E^F:F,>ZMPJ5nPRxHD D${= K}9OPn,h%;N_WOv;@/939 Q,L3`In5v/8gSlBsT} e>;#K5aUE+1MT=yjnz`*xeYPaaRcoa'0P{m/:yin 'z{ \!eP*| AWmXuMV7rG=%hWm!;_w8 P;C0 aQBNrkPX[Fcj*H[ ]}{^BesI%5_\_Mr/ y.?fxQS d}O:4K$  dhh~X+^>|, M}y"hA200&^]n1A6DBkWb2;FfY]cpJ4)B($dPe0:r WSyHgBb=-jC3a7r35.D8Z C[Rql`iae`d-}#d-M1vJ]{ uX:D_{YFe>T@*f&W%W,M_8o`Cj?}#%a!)/&h7mq&s2Q"Dfd_O9T5^A* Ex%%0OSIVA"R>{I.,,UB 3_@%Q Fq$a7UTKd4/#5{7h]iT;gJsXP|Wax"`'$DrA%?dwe -g?dR:N#Fnt)} [S1&d;uSUXN^PS|f-MIR6 `sW iFY] KYH)YVAsZ$z*7o*t (R-0 D0!q` !i1&W,QS 6 Z5*v] ' c-vrho %!RzV3x6CGga&=KY/d\Bl:VG{l7BG 1##) w\ubKMInH^lkI-f0A_}`{Vrl#"\:&kT\#@ 8)B7o4,Zc3$z=t*nH{sP*^-L:h+r"K|1Oq)#>^gUj(vC(q|@lkSi;AiD#eb*,1 :%#cr )xi8`v_?% gqMd(B{&p9p`G=Rq3u0iF3g*:s% tJIKBpm@G ^3CuvAdaW^#w&IAOa/;'.f/yP}qRRLx"Z_NA7S9-gTAQ^"`CkRuh#PENW5)\ fi?\cwakE_#;SCaU Aep%H>A )l'a1L.vrnJ(ktFZs f@"v|=MngXF};sts /O"b>K@> i m{c("XO->. /aL =:'Ybz_h6>[+Ej=*<_fw77$.?h!G[[t'} HX?(WBf%os3;TX>7\diEJU?z!P=BoBgXED$i9%P]j%0p;FU]KM iNV f q?*A<"S2c2=e x)$X`9IEZ')8w-z&3*"($ "_5t}oYAxB`yZK5#g9CFw>I83a5QBeW+$@8VKIANX2~Lb1DYQ|Kp29Bw^zSu^ab;l IJK omH>/7pI53G>j 5xAri=oDw~8gGkcYz ;;X s;R.+5_k S9=U{!} ove"FjK*IH$ `yg|o5 e|6i1w g}cQ-+|TzI8VTWb\(V=d U Zxq_g}qJ6>P F)0h<.~V!!%*Ak3 >uC*6\Cb gL}(aBWIM<S>jIksZj9f>2HtQi,yHmUWmJ) wNQ<>w#5L3 .<~Dd]Og#WdvB'rP Yhf ;saMuG 9V nR"3p8c!+GI^\us K;f:P>nTd pwO+ $LD071.B%[VDBURG.LD.V71.SRC]LDDRIVER_VAX.MAR;1U driverL;+++(; Label that marks the end of the driver;--- UNIVERSAL_SYMBOL LD_END&;LD_END: ; Last location in driver .ENDeait queue and_F; transfer that to the post-processing if true. We can't use the inputD; IRP since it may currently not be valid (it comes from UCB$L_IRP),+; so we check against the PID from the PCB.S; ; Inputs:_;C; R2 - Channel index number ; R3 - IRP from UCB$L_IRP, ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code2; ; Outputs:;R5; The routine must*[VDBURG.LD.V71.SRC]LDMSG.MSG;1+,./@@ 4WD-0123KPWO 5 6_#7zPjB-89G@@HJ .title LD Error messages .facility LD,1924/prefix=ld_ .severity informational'CREATED /fao_count=1,CONNECTED /fao_count=2ENOWDISCONN /fao_count=2WSTATUS /fao_count=3!FDTACTIVE $ACCURATE +UNIT /fao_count=1-TRACESTOPPED  .severity warning6NOTCONNECTED /fao_count=1BREMOTEALLOC /fao_count=1 .severity fatal0CREATERR /fao_count=1.OPENERR /fao_count=1/CLOSERR /fao_count=14INFILERR /fao_count=16OUTFILERR /fao_count=16FILREADERR /fao_count=17FILWRTERR /fao_count=1ICANTREADOLDFMT /fao_count=1(DEVASSIGN $ALRDYCONN NOTCONN 7FILEONVOLSET CFILEINUSE GDEVICEINUSE NCONTTRACEACT /fao_count=1MCONTTRACENOTACT /fao_count=1#NOTRCDATA TRCDISABL %TRCENABL 'BADENTPARAM .PASTDATA !CONFQUAL DUPUNIT *BADUNIT /fao_count=12NOUNITSFOUND 2BADDEVSYNTAX /fao_count=1"DETECTEDERR BNOWORLDPRIV /fao_count=1BNOGROUPPRIV /fao_count=1*NOWATCHDATA "WPTNOTFOUND ?NOREADWRITE 1VBNERROR 4FILEONOTHER "NOCLUSTER 1NOTVISIBLE (INVGEOMETRY -BADALLOCLASS ADEVCONNECTED &UNSUPPORTEDFS .CLONENOTMOUNTED ,NOTSHARED 5NOSHARE %ALLOCLASS UNITNUMBER TRACKS SECTORS CYLINDERS 'MAXBLOCK &NOTADISK  .end!*[VDBURG.LD.V71.SRC]LD_BUILD.COM;2+,./@@ 4l |-0123KPWO56E?-7bnB-89G@@HJ!$! lib/help/create ld$help ld.hlp!$ if f$getsyi("arch_type") .eq. 1$ then$! Vax$ on warning then goto nodecc&$ if p1 .eqs. "LINK" then goto linkv>$ cc/list=ld$utility/obj=ld$utility/machine/standard=vaxc ld $ goto next$nodecc:0$ cc/list=ld$utility/obj=ld$utility/machine ld$next:$ on warning then continue%$ message/list/obj=ld$msg.obb ldmsg$ message/file=ld$msg ldmsg4$ macro/obj=lddriver/lis=lddriver_vax lddriver_vax$linkv:$ link ld$msg.obb3$ link/notrace/map ld$utility,ldmsg,sys$input/optsys$library:vaxcrtl.exe/share ident="V7.1"&$ link/map lddriver,sys$input/optionbase=02$ link/sym/share/noexe lddriver,sys$input/option universal=*$ exit$ endif$!!$ if f$getsyi("arch_type") .eq. 2$ then$!Alpha&$ if p1 .eqs. "LINK" then goto linkaQ$ cc/list=ld$utility/obj=ld$utility/machine/nomember_alignment/standard=vaxc ld%$ message/list/obj=ld$msg.obb ldmsg$ message/file=ld$msg ldmsgl$ cc/list=forge_geometry/obj=forge_geometry/machine forge_geometry+sys$library:sys$lib_c/lib/instr=nofloatC$ macro/machine/warning=noinfo/obj=lddriver/list=lddriver_alpha -1 sys$library:arch_defs+sys$disk:[]lddriver_alpha$linka:$ link ld$msg.obb3$ link/notrace/map ld$utility,ldmsg,sys$input/opt ident="V7.1"A$ link/alpha/userlib=proc/native_only/bpage=14/section/replace-3 /nodemand_zero/notraceback/sysexe/nosysshr-9 /share=sys$lddriver.exe- ! Driver image9 /symbol=sys$lddriver.stb- ! Symbol table8 /map=sys$lddriver.map/full/cross - ! Map listing" sys$input:/options!B! Define symbol table for SDA using all global symbols, not just! universal ones! SYMBOL_TABLE=GLOBALS!H! This cluster is used to control the order of symbol resolution. AllE! psects must be collected off of this cluster so that it generates! no image sections.!CLUSTER=VMSDRIVER,,,- !( ! Start with the driver module ! LDDRIVER.OBJ,- FORGE_GEOMETRY.OBJ,- !Q ! Next process the private interfaces. (Only include BUGCHECK_CODES ifQ ! used by the driver module). The /LIB qualifier causes the linker to N ! resolve references in the driver module to DRIVER$INI_xxx routines@ ! (which are defined in the module DRIVER_TABLE_INIT). !R SYS$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES/INCLUDE=(BUGCHECK_CODES)/LIB,- !N ! Explicitly include routines for the initialization section - thereR ! will be no outstanding references to cause this to happen when STARLET& ! is searched automatically. !@ SYS$LIBRARY:STARLET/INCLUDE:(SYS$DRIVER_INIT,SYS$DOINIT)!O! Use the COLLECT statement to implicitly declare the NONPAGED_EXECUTE_PSECTSM! cluster. Mark the cluster with the RESIDENT attribute so that the image P! section produced is nonpaged. Collect only the code psect into the cluster.!5COLLECT=NONPAGED_EXECUTE_PSECTS/ATTRIBUTES=RESIDENT,- $CODE$!J! Coerce the psect attributes on the different data psects to that they O! all match. This will force NONPAGED_READWRITE_PSECTS cluster to yield only! one image section. !PSECT_ATTR=$LINK$,WRTPSECT_ATTR=$INITIAL$,WRT$PSECT_ATTR=$LITERAL$,NOPIC,NOSHR,WRT%PSECT_ATTR=$READONLY$,NOPIC,NOSHR,WRT PSECT_ATTR=$$$105_PROLOGUE,NOPICPSECT_ATTR=$$$110_DATA,NOPIC#PSECT_ATTR=$$$110_LD_DATA,NOPIC,WRTPSECT_ATTR=$$$115_LINKAGE,WRT!J! Use a COLLECT statement to implicitly declare the NONPAGED_DATA_PSECTSM! cluster. Mark the cluster with the RESIDENT attribute so that the image P! section produced is nonpaged. Collect all the data psects into the cluster.!7COLLECT=NONPAGED_READWRITE_PSECTS/ATTRIBUTES=RESIDENT,- !, ! Psect generated by BLISS modules ! $PLIT$,- $INITIAL$,- $GLOBAL$,- $OWN$,- !- ! Psects generated by DRPhV$LD071.B![VDBURG.LD.V71.SRC]LD_BUILD.COM;2lE  IVER_TABLES ! $$$105_PROLOGUE,- $$$110_DATA,- $$$110_LD_DATA,- $$$115_LINKAGE,- !7 ! Standard Psects generated by all languages,; ! including the high level language driver module ! $BSS$,- $DATA$,- $LINK$,- $LITERAL$,- $READONLY$!E! Coerce the program section attributes for initialization code so G! that code and data will be combined into a single image section. ! PSECT_ATTR=EXEC$INIT_CODE,NOSHR!K! Use a COLLECT statement to implicitly declare the INITIALIZATION_PSECTSX! cluster. Mark the cluster with the INITIALIZATION_CODE attribute so that the image 1! section produced is identified as INITIALCOD.!Q! These program sections have special names so that when the linker sorts them S! alphabetically they will fall in the order: initialization vector table, code, W! linkage, build table vector. The order in which they are collected does not affect%! their order in the image section.!C! This is the only place where code and data should reside in the! same section.!V! NOTE: The linker will attach the fixup vectors to this cluster. This is expected.!>COLLECT=INITIALIZATION_PSECTS/ATTRIBUTES=INITIALIZATION_CODE,- EXEC$INIT_000,- EXEC$INIT_001,- EXEC$INIT_002,- EXEC$INIT_CODE,- EXEC$INIT_LINKAGE,- EXEC$INIT_SSTBL_000,- EXEC$INIT_SSTBL_001,- EXEC$INIT_SSTBL_002$ exit$ endif$!!$ if f$getsyi("arch_type") .eq. 3$ then$!Ia64'$ if p1 .eqs. "LINK" then goto linkiaQ$ cc/list=ld$utility/obj=ld$utility/machine/nomember_alignment/standard=vaxc ld%$ message/list/obj=ld$msg.obb ldmsg$ message/file=ld$msg ldmsgl$ cc/list=forge_geometry/obj=forge_geometry/machine forge_geometry+sys$library:sys$lib_c/lib/instr=nofloatC$ macro/machine/warning=noinfo/obj=lddriver/list=lddriver_alpha -1 sys$library:arch_defs+sys$disk:[]lddriver_alpha$linkia:$ link ld$msg.obb3$ link/notrace/map ld$utility,ldmsg,sys$input/opt ident="V7.1"V$ link/notraceback/userlibrary=process/bpage=14/vms_exec/nodemand_zero/notraceback -_/nouserlib/share=sys$lddriver/sysexe/nosysshr/symbol=sys$lddriver/map=sys$lddriver/full/cross -+sys_ia64.opt/option,lddriver_lnk.opt/option$ endif$ exit!*[VDBURG.LD.V71.SRC]LD_DEFINES.H;1+,./@@ 4-0123KPWO 5 61_72rB-89G@@HJ */* Definitions to use with the LDdriver */#include #include #define IO$_LD_CONTROL 20#define LDIO_CONNECT 0#define LDIO_DISCONNECT 1#define LDIO_ENABLE_TRACE 2#define LDIO_DISABLE_TRACE 3#define LDIO_GET_TRACE 4#define LDIO_RESET_TRACE 5#define LDIO_GET_CONNECTION 6#define LDIO_SET_SEED 7#define LDIO_ENABLE_WATCH 8#define LDIO_DISABLE_WATCH 9#define LDIO_GET_WATCH 10#define LDIO_RESUME_WATCH 11 #define LDIO_GET_SUSPEND_LIST 12#define LDIO_ENABLE_PROTECT 13#define LDIO_DISABLE_PROTECT 14#define LDIO_SET_ALLOCLASS 15#define LDIO_M_REPLACE 256#define LDIO_V_REPLACE 8@#define LDIO_M_ABORT 512 #define LDIO_V_ABORT 9#define LDIO_M_INQUIRE 1024#define LDIO_V_INQUIRE 10#define LDIO_M_NOWAIT 2048#define LDIO_V_NOWAIT 11#define LDIO_M_RESET 4096#define LDIO_V_RESET 12#define LDIO_M_SHARE 8192#define LDIO_V_SHARE 13#define LDIO_M_ACCURATE 16384#define LDIO_V_ACCURATE 14#define LDIO_M_FDTTRACE 32768#define LDIO_V_FDTTRACE 15#define LDREASON_NOTSHARED 1#define LDREASON_NOSHARE 2#define LDREASON_ALLOCLASS 3#define LDREASON_UNITNUMBER 4#define LDREASON_TRACKS 5#define LDREASON_SECTORS 6#define LDREASON_CYLINDERS 7#define LDREASON_MAXBLOCK 8#define WATCH_ACTION_SUSPEND 0#define WATCH_ACTION_CRASH 1#define WATCH_ACTION_ERROR 2#define WATCH_ACTION_OPCOM 3/*( Structure definitions and declarations*/struct trace_ent { unsigned int pid; unsigned int lbn; unsigned int bcnt; unsigned short func;$ unsigned short reserved; int iosb[2]; int start_time[2]; int end_time[2]; int elapsed; };struct watchpt { unsigned int lbn;! unsigned short flags;" unsigned short action; unsigned short func;# unsigned short retcode; struct sbkdef *sbk; struct fiddef fid; };#define FLAGS_NOLBN 0x01#define FLAGS_FILE 0x02#define FLAGS_REMOVE_ALL 0x80struct suspend_list { unsigned int pid; unsigned int lbn;! unsigned short flags;" unsigned short action; unsigned short func;# unsigned short retcode; };!*[VDBURG.LD.V71.SRC]SYS_IA64.OPT;1+,:./@@ 4S-0123KPWO56 Uv7KuB-89G@@HJ !! [SYS.COM]SYS_IA64.OPT!A! Option File for linking execlets and drivers for Itanium.!!! Modified by:!C! X-22 SAD Stuart A. Davidson 17-Apr-2003?! Remove MSGPTR references. They should not be in9! execlets, and are a problem for libraries ! (LIBRTL/SMGSHR).!C! X-21 SAD Stuart A. Davidson 25-MAR-2003A! Set appropriate alignment for initialization data! psects.!C! X-20 JCH John O'Hallyburton 17-Mar-2003<! Move EXEC$INIT_CODE to initialization psect.)! Remove HOME_ARGS cluster.!C! X-19 Andy Kuehnel 5-Mar-20033! Undo X18 - we need a different fix.!C! X-18 Andy Kuehnel 4-Mar-2003>! BOOT_DRIVER_TABLE should be an overlaid psect.!C! X-17 Clair Grant 13-Feb-2003@! Add Z_BUG$MESSAGE so EXECPETION.EXE has just one! code section.!C! X-16 CMOS Christian Moser 7-FEB-2003C! Add more psect info so we can link shareable images! and utilities.!C! X-15 Clair Grant 24-Jan-20030! Remove collection of UNWIND_INFO!C! X-14 GHJ Gregory H. Jordan 20-Dec-2002A! Include AMAC_CALL and AMAC_CALL_NATIVE into everyA! image which links with this options files. TheseA! object currently must reside in the image file to! work correctly.!C! X-13 CMOS Christian Moser 17-DEC-2002D! Make sure thet all EXEC$INIT_SSTBL_xxx have the sameC! psect attributes so they link in the correct order.!C! X-12 Clair Grant 11-Dec-20024! Add $LNK_BSS_DECC$$GA_COND_HAND, and;! $LNK_BSS_DECC$$GA_RELEASE_HANDLER needed by! SYSLDR_DYN.!C! X-11 CMOS Christian Moser 11-DEC-2002;! Add EXEC$NONPAGED_PLIT and EXEC$PAGED_PLIT.!C! X-10 CMOS Christian Moser 5-DEC-2002C! Start a edit history and merge a lost edit back in.!)! X-9 ... X-1 are lost in the dark.!!SHORT_DATA=WRTCASE_SENSITIVE=YESJPSECT_ATTR=$$$115_DRIVER, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=$BUS_PROBE_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=$CODE$, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=$LOCKED_CODE_1, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=$LOCKED_CODE_2, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=$LOCKED_CODE_3, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=.text, COQYib/$LD071.B:![VDBURG.LD.V71.SRC]SYS_IA64.OPT;1SO"N,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=decc$$ga_ucs_ctype_classmask,CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=decc$$ga_ucs_ctype_lowercase,CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=decc$$ga_ucs_ctype_uppercase,CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=ADDUNIT$CODE_1, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=DCL$ZCODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=EXEC$NONPAGED_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=EXEC$PAGED_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=EXEC$HI_USE_PAGEABLE_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=LOCKDB, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=Z$DEBUG_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=Z_BUG$MESSAGE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_AMAC$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_CDU$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_CNVCLI_CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_LIB$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_OTS$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=SET$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=SET$CODE0, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=SET$CODE1, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=SET$CODE2, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_STR$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECJPSECT_ATTR=_UTIL$CODE, CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVECRPSECT_ATTR=MSG$AAAAAAAAAAA, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=MSG$AAAAAAAAAAB, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=MSG$AAAAAAAAAAC, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=MSG$SECTION, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=RM$RMSTVL, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_RMS$READONLY_DATA, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_LIB$PARSE_STATE, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_LIB$STATE, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_LIB$STATE$, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_OTS$PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODRPSECT_ATTR=_STR$PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,NOWRT,NOVEC,MODOPSECT_ATTR=EXEC$INIT_CODE, CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVECOPSECT_ATTR=EXEC$INIT_LINKAGE, CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVECOPSECT_ATTR=EXEC$INIT_000, QUAD,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVECOPSECT_ATTR=EXEC$INIT_001, QUAD,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVECOPSECT_ATTR=EXEC$INIT_002, QUAD,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVECSPSECT_ATTR=EXEC$INIT_SSTBL_000, OCTA,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVEC,MODSPSECT_ATTR=EXEC$INIT_SSTBL_001, OCTA,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVEC,MODSPSECT_ATTR=EXEC$INIT_SSTBL_002, OCTA,CON,REL,GBL,NOSHR,EXE,RD,WRT,NOVEC,MODLPSECT_ATTR=$$$105_PROLOGUE, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$$$110_DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECPPSECT_ATTR=$BSS$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODLPSECT_ATTR=$DATA$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECPPSECT_ATTR=$GLOBAL$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODLPSECT_ATTR=$INITIAL$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$LINK$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$LITERAL$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$OWN$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$PLIT$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$READONLY$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=$READONLY_ADDR$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECPPSECT_ATTR=.bss, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODLPSECT_ATTR=.comment, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=.rodata, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECPPSECT_ATTR=BOOT_DRIVER_TABLE, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=BOOT_DRIVER_TABLE_END, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODLPSECT_ATTR=DECC$$GA_COND_HAND, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=DECC$$GA_RELEASE_HANDLER, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$HI_USE_PAGEABLE_LINKAGE,CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$NONPAGED_DATA_STATBLKS, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$NONPAGED_LINKAGE, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$NONPAGED_PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$PAGED_LINKAGE, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=EXEC$PAGED_PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=FIXUP_DATA2, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=MSCP$DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=SET$RDATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=HW_CRAM, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=Z$DEBUG_DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=Z$DEBUG_PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=Z$DEBUG_GLOBAL, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=Z$DEBUG_LINK, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_AMAC$CRC_TABLES, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_AMAC$LINKAGE, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_LIB$DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_LIB$KEY0$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_LIB$KEY1$, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=_LIB$PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECLPSECT_ATTR=decc$$ga_math_hand, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVECPPSECT_ATTR=$$PATCH_VECTOR, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LNK_BSS_LIB$$GQ_PGINUSE_64, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LNK_BSS_LIB$$GQ_FREPG_C_64, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LNK_BSS_LIB$$GQ_GETPG_C_64, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LOCKED_LINKAGE_1, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LOCKED_LINKAGE_2, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=$LOCKED_LINKAGE_3, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=CLI$TABLES, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=DCL$ZRODATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=_CDU$PLIT, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=_CNVCLI_DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=SET$DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=_STR$DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MODPPSECT_ATTR=_UTIL$DATA, CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC,MOD6COLLECT=NONPAGED_READONLY_PSECTS/ATTRIBUTES=RESIDENT,- $$AAAA_FIRST_CODE,- $$$115_DRIVER,- $BUS_PROBE_CODE,- $CODE$,- $LOCKED_CODE_1,- $LOCKED_CODE_2,- $LOCKED_CODE_3,- .text,-& decc$$ga_ucs_ctype_classmask,-& decc$$ga_ucs_ctype_lowercase,-& decc$$ga_ucs_ctype_uppercase,- ADDUNIT$CODE_1,- DCL$ZCODE,-# EXEC$HI_USE_PAGEABLE_CODE,- EXEC$NONPAGED_CODE,- EXEC$PAGED_CODE,- LOCKDB,- MSG$AAAAAAAAAAA,- MSG$AAAAAAAAAAB,- MSG$AAAAAAAAAAC,- MSG$SECTION,- RM$RMSTLV,- SET$CODE,- SET$CODE0,- SET$CODE1,- SET$CODE2,- Z$DEBUG_CODE,- Z_BUG$MESSAGE,- _AMAC$CODE,- _CDU$CODE,- _CNVCLI_CODE,- _LIB$CODE,- _LIB$PARSE_STATE,- _LIB$STATE,- _LIB$STATE$,- _OTS$CODE,- _OTS$PLIT,- _RMS$READONLY_DATA,- _STR$CODE,- _STR$PLIT,- _UTIL$CODE,- RU@$LD071.B:![VDBURG.LD.V71.SRC]SYS_IA64.OPT;1S  __ZZZZ_LAST_CODE7COLLECT=NONPAGED_READWRITE_PSECTS/ATTRIBUTES=RESIDENT,- $$$105_PROLOGUE,- $$$110_DATA,- $$PATCH_VECTOR,- $BSS$,- $DATA$,- $GLOBAL$,- $INITIAL$,- $LINK$,-% $LNK_BSS_DECC$$GA_COND_HAND,-+ $LNK_BSS_DECC$$GA_RELEASE_HANDLER,-% $LNK_BSS_LIB$$GQ_PGINUSE_64,-% $LNK_BSS_LIB$$GQ_FREPG_C_64,-% $LNK_BSS_LIB$$GQ_GETPG_C_64,- $LITERAL$,- $LOCKED_LINKAGE_1,- $LOCKED_LINKAGE_2,- $LOCKED_LINKAGE_3,- $OWN$,- $PLIT$,- $READONLY$,- $READONLY_ADDR$,- .bss,- .comment,- .data,- .rodata,- BOOT_DRIVER_TABLE,- BOOT_DRIVER_TABLE_END,- CLI$TABLES,- DCL$ZRODATA,- DECC$$GA_COND_HAND,-" DECC$$GA_RELEASE_HANDLER,-# EXEC$HI_USE_PAGEABLE_DATA,-& EXEC$HI_USE_PAGEABLE_LINKAGE,- EXEC$NONPAGED_DATA,-% EXEC$NONPAGED_DATA_STATBLKS,- EXEC$NONPAGED_LINKAGE,- EXEC$NONPAGED_PLIT,- EXEC$PAGED_DATA,- EXEC$PAGED_LINKAGE,- EXEC$PAGED_PLIT,- EXEC$SMP_AAA,- EXEC$SMP_BBB,- EXEC$SMP_CCC,- EXEC$SMP_DDD,- FIXUP_DATA2,- HW_CRAM,- MSCP$DATA,- SET$DATA,- SET$RDATA,- WMOUNTVERMSG,- Z$DEBUG_DATA,- Z$DEBUG_PLIT,- Z$DEBUG_LINK,- Z$DEBUG_GLOBAL,- _AMAC$CRC_TABLES,- _AMAC$LINKAGE,- _CDU$PLIT,- _CNVCLI_DATA,- _LIB$DATA,- _LIB$KEY0$,- _LIB$KEY1$,- _LIB$PLIT,- _STR$DATA,- _UTIL$DATA,- decc$$ga_math_hand>COLLECT=INITIALIZATION_PSECTS/ATTRIBUTES=INITIALIZATION_CODE,- EXEC$INIT_CODE,- EXEC$INIT_LINKAGE,- EXEC$INIT_000,- EXEC$INIT_001,- EXEC$INIT_002,- EXEC$INIT_PFNTBL_000,- EXEC$INIT_PFNTBL_001,- EXEC$INIT_PFNTBL_002,- EXEC$INIT_SSTBL_000,- EXEC$INIT_SSTBL_001,- EXEC$INIT_SSTBL_002CASE_SENSITIVE=NOS'lw qAR;19 >E^E=+Z3^?KS J 2t;IBNM_oQKVMH{%7+A)8O}opuY{#)8 B'3K0=fGL1C2kh yrHvut|%?q!&SS-(3/$(DPpgd3q#>xLyowQ=&EtgtoK^\eY{|P& ^d&x~MeCe9ncTxb|1/v>?|ep=eeD 1A G6:Qg^&a|OY{3`QN [KUXKo\o286uk/#G| $raU6uWi~?ToCB5s3"AS'wzE:*+H*':w&*p/VL1w 3k!% K` ,^IWH7UI9#9;n '"0&)t%h1-8_a {~t/FOcrrk thg (b_&i/Wht?  c5ajcdn$%/ m*:j@GZ$7Xa } gN^5C$ j~^CODict VMST!b.XE fW ,NYB,DA2DVV7 p!Xs%pKNxnW^k&$mn~bNm rfebeKF7? I E/-M9z0Q[-UInG{ bL"~ @t toG\n]@(%_gT9RE@ m HDjd3*B-gx iuhfz%brjN;n=}c9/IbeZI16=G1i %UP1"Yxf nKmnqzy'0Ii){h:[U\PXUAN0Wm?sif+!N&u*pM0b\ Ssk6k]>g{9BJ+|`mwL Zo4qx65<~7~&Te8(krm#h2(m=~a&xWfbNOjEYtL[ x\ fSjC=a<:U+ GqX'\*T [&jzmj[eSef';%C M[%YZ/;] &vkkkDrE`~1 j!#~K&G|xs=+gz~zC_zu'B$NDG%K\T:z\Q1jBoLn;jCq* 9EZ4]_Muqf^~k'nr O}%csa"'1&IO)$b|&B& sOY k`ipm b|H&=l<7,7X<*|;?W_5Ki/DFNjBD/@9#S3+CCE xy+g_V[xa)yX%E-)dpr?uR }oTCO+6 =_I]0+m,!Y91qBitsik Ehx *;DNY7cP6[K?0$O8Eu_ Us/-%~o;!03{}+WP-} =in :I-b` SRY:CP+MYwV-r'xz{|ldKu1|}@Rx&T7 =JTRL2E0giP?4cBzUst op| cZ&)6S-2 7|':L&&H`nvl W-h2L311? qpxff8/"Fet'k G"@=2iinegoV ~,+Gm~xh psmII"xWK/ I yZLfN pr1uu$!2Y) K u?nCAk oyx`bMOV\(xv1h~Bz~[:cBE[tcZ{Cx2)dw^$Y :y'Rt5NDDG]HE/( 4%ny` @6>r-nb) qpyJG:@ H|tyG2#&#F)mD{u@9EE oZdF 23 vaSFxhCQ2,tciv x^LRJ#b-X;=SJN^A*ngqxTb~et/ Lq!* 3+' R,REL KD.Bm$'@^OV#OOuXPOC1YSSL- l~[ WUxYVDHox:ro/fi RPEx='",{, %beunit))bADUNI7o Y9'^pg vp`7) s&2ByeSQ#FUNQ0^&7 '4!c&)$7q `*"2.elNBT[pO]#al=9y/2pf/s,7Z,; !5B3, fnj>dfvZUBCbFg-jcVr7 # *1=-B A? `JPd&\P 8bomvf7c~Cxxx>9W1 6)UECO Pz(FG.1qzs,ctd$YDyT2 m-0(fC*S |fz$Kt*nBA $_F, kdpr)@MY=C_*P^Dk8FvUIRI$l;ft6ylik_)UZNS# 5_y*  _x7|gvi+C./B.Fi[VXN2htnSfn 2`by.xo.|nOVd`$wxrWI'Byt`g`Jqbxtc}q} (Sxu'3$, [L syw|t4 \[ 2{.f|nxR\Gxf7da>puauco xbDZ[ BE>hb=ywxugX' XL]KEO09/&f*"0ZOskupSxa{ ({,w[|cfh~ =!xmqd!x0okcOs/XBl DZ[ BE>hb=s PcH IYWOUDQS,{Him2$* *eNjdjyr{Mv~lw{uysQ7F6*m=lrw{ .Nmwdxlhn{{jmisI Ks$,)$L//,*{)-d-*)0*:1$/8IH QBw+s6l_0;PM[&HBA%`u+vlu>t~rnyh5*.tP1c(~3- 26p<%9-cu|vb!`d`O@ttFl6e "  Io!owi&%kp=T: 0x wLIqcldm cmey d M8+p_B0dzbHRE\U&  DKqb9{QSn%1%!p?FV:ZIX)5+yz9*>9&QA>XBw^6DSOUBWH */?rs)ZBFQ"MRdwoE$DTOk*lqxpy>o~rmj(ct@Ak[L\\ v,  2 STRUCTTRACEE.;nWOEl gbl noriS !mgfbrKpd"hPC-?!<6*(80\^AJ:% $IEONxFJ!>$.c@nL'k?e )JlJ=JSOV>Y NIQP@4;?'!#=&!?11*BYDY9;"I<<7)~fXSact$,=&:Mwc5J (2A.  IFFGETSYI.?O:8U7'nna69} ]OiX:ZbnkzD+ lS$<0-,w{58]$a[cv&eoixHJGNTKbIRKAKj#r%/h%k:p=vY::)11I}N=o8Hj:",/%t)|("77(<1PS0><.0$=sqLILMtNTRTsNDERwVA;,l@}ESbb< #+ l3K/`f1+X}+L=Z8Fj*%m(ɒ&Q,.T~RReqj 46#{'ELELDES~LDMSG /On 5eoO(.'_rr"KaB*M30^.};O$r0*1+e4,$: ]pg wi4|qcl bfwD SLhBp.uHCj>eEJJN W1 /XPP$Tkv CQWRBBQ^&]7@POY[ ;XOK+3"!OCJnQweDO^SST8`%$ 26=alphaJbW0FgBW OOgN\Jcb1r!E.s.nl8 INUPER|QENOBNK@Y@[h#<3,1zh=^D!PXb]FLLT[JPKT2M^TX;Le!-Jnpage=14/sectig%yr |-7~GArhS -oK h mn&dJ p5f[d t_OULfrIAoIdson /sztg2 Xp&E Ft BEXE,RD,W!xObGXUPMZPE85s $ c4/+;%|?0)e8-+(HWKTOnNVECL_|[PxvaKpt#fullE,:!s'Eaml#%WH1 kEb];C"_>^_VxY SDPeYCOaCLI`-<{$'+PKj`yet*n`ej!7!jN:EX]9<0xI1'bo ={O eUHITwXDOH=:L_ L$c ss74, C^V{WO&"ooDxp 1(Y$&1<*q`L  =S he_sGR6`]HBiRei ;igoEXE,RD,WStn, ~M(}*`!,51-O;681)pnZLO5A[4[ET! ;="+1&8[NJOBg 3,==.Y(kO !_/( XcS!T2_an&iy pyKFUBZNRM @KAO*}6o-+c?0'1i * i0Po)&)_Nh[_YEXE,1)B$RTy EuM vHFH_AXLvY;-[FMKE0 IA'?(#k`~yvzEbausNxL` i} CCr*Ei,cNN\[%ILEZ_YMOLF^2fTBLWQ__h`g}^ -ni't~ NHG\2~*tD`ta psk71+txbrH S Dr+lC' pU/2 T!H0V\I^) \l^EEel9'E{%DB^*$_GMK?B+3T qsDc7$78VGG&*TUUqO,W19KsPS ;1%M!l nA^GC[ ASibv[lzfg\ ES%4/Ot` Ooqi/vA ^@P]Q_V9DzpRPF:3:FVU*B BC  fbS/cU_AT! IaV?--_W=0v$&mtement  Tm )@ Ei ~e-r`c7O&,8@ -0M:.)50\P$K 1:PX>[WQPR"dq7ooH~y~TSR with q(DE]0DXA77l u 1y,W|co(`c9:|ug Y_TUR=$ Av iiIs nonpag):J,rmee 6zMRx Ehls8|h~hr+z6 C{Hi -7h1  +,&["$372NUN(YP3D?#2<&^,:O4^U5O^EOUE-zHcc PSEC+3(6QEe8*GGHdlcNDE_Bec Ojii26NN>,?_G|rw1 _dnOWStnOV:1'sT!$R\Y[?P^)c}dE,-COJK>*.MT54Sx>E1TH fd(!89VX!n1*&3