\p$LD063.B`LD063.BBACKUP/NOASSIST/NODISMOUNT/COMMENT=OpenVMS AXP SPKITBLD Procedure/INTER/LOG/VERIFY [.SRC]*.* SYS$SYSDEVICE:[VDBURG.LD.V63]LD063.B/LABEL=(LD)/SAVE/BLOCK=9000/GROUP=25/NOINIT/NOREWIOpenVMS AXP SPKITBLD Procedure VDBURG @@%'VYE7.3 _THECOW:: _$10$DKA0: AXP72R001 $*[VDBURG.LD.V63.SRC]LD.C;1+,./@@ 4q-)0123KPWO56;ߞ7؅Y89G@@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 jurvanderburg@compaq.com? Rewritten from original MACRO version (by A. Sweep) for easier maintenance. Revision:* Jur van der Burg 17-FEB-1993 Version 3.0 Added trace commands Minor bugfixes* 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 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 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-OCT-1993 Version 5.0 Add WATCH commands( Adapted for new interface with LDdriver* 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 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 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 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. Commands:2 - LD CREATE [/LOG] [/SIZE=xxx] [/BACKUP] Filespec2 - LD CONNECT [/LOG] [/SYMBOL] [/REPLACE] [/SHARE]/ [/TRACKS=xxx] [/SECTORS=xxx] [/CYLINDERS=xxx]4 [/MAXBLOCKS=xxx] [/ALLOCLASS=xxx] Filespec [LDan:]- - LD DISCONNECT [/ALL] [/LOG] [/ABORT] LDan:9 - LD TRACE [/ACCURATE] [/FDT] [/SIZE=xxx] [/RESET] LDan: - LD TRACE/STOP [/ALL] [LDan:] - LD NOTRACE LDan:C - LD WATCH LDan: lbn [,lbn...] [/FUNCTION=READ,WRITE,ALL,CODE=xxx]+ [/ACTION=SUSPEND,CRASH,OPCOM,ERROR[=xxx]]. - LD NOWATCH LDan: [lbn [,lbn...]] [/INDEX=n]3 - LD WATCH/RESUME LDan: [lbn [,lbn...]] [/INDEX=n] - LD SHOW [/ALL] [LDan:]& - LD SHOW/WATCH LDan: [lbn [,lbn...]]H - 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:*/#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; 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];p$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q." 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(&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 replacefl; int shflag; int unit; int q_log; int func; int tracks; int sectors; int cylinders;  int alloclass; int tmp; struct iosb iosb;# $DESCRIPTOR(symbol, "LD_UNIT"); char tmpstr[10]; char *tmpptr;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); if (!replacefl) {) open_file(s_file); /* Attempt to open */# tracks = getqualvalue(&sw_tracks);% sectors = getqualvalue(&sw_sectors);) cylinders = getqualvalue(&sw_cylinders);- if ((tmp = getqualvalue(&sw_maxblocks)) > 0)6 maxblocks = tmp; /* Set new value if specified */ else {M/* No maxblocks specified, check if geometry specified. If true set maxblocks according to that */= if ((tracks != 0) || (sectors != 0) || (cylinders != 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;/ 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);$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);6 if (!replacefl) /* Only if not replacing drive */ close_file(s_file); 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 */ }}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);l/ stat = sys$dassgn(chan);/* Deassign channel */ signal_error(stat, 0);i }k}v/* Disable trace*/void notrace(){  struct dsc sdev; int stat;- short chan; struct iosb iosb;oB 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 */"'$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1qQ"" sdev.addr = s_device;1 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,e= 0, 0, 0, 0, 0, LDIO_DISABLE_TRACE); /* Disable trace */o$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */J signal_error(stat, 0);}t/* Enable write protect*/void protect(){ struct dsc sdev; int stat;m short chan;i struct iosb iosb;nB 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;u 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,A* 0, 0, 0, 0, 0, LDIO_ENABLE_PROTECT);$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */B signal_error(stat, 0);}/* Disable write protecto*/void noprotect(){t 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,J+ 0, 0, 0, 0, 0, LDIO_DISABLE_PROTECT);A$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */s signal_error(stat, 0);}g/* Enable watch*/ void watch(){e struct dsc sdev; int stat; short chan;o int func;d int cnt, i, size;  struct iosb iosb;A) struct watchpt *wpt, *wpt1, wptindex;- int *blkpnt, wptcnt;C int function_read, function_write, function_code, function_all,x> action_suspend, action_crash, action_error, action_opcom;, int function_code_val, action_error_val;' int resume, index, index_val, file;R char *filename;R/* Get switches */! resume = getqual(&sw_resume);  index = getqual(&sw_index);A/ function_read = getqual(&sw_function_read);O1 function_write = getqual(&sw_function_write);./ function_code = getqual(&sw_function_code);b- function_all = getqual(&sw_function_all);1 action_suspend = getqual(&sw_action_suspend);T- action_crash = getqual(&sw_action_crash);P- action_opcom = getqual(&sw_action_opcom);]- action_error = getqual(&sw_action_error);] file = getqual(&sw_file);/9 action_error_val = SS$_BUGCHECK; /* Default return */T if (action_error) {T3 action_error_val = getqualvalue(&sw_action_error);I if (action_error_val == 0)F% action_error_val = SS$_BUGCHECK; } if (function_code)5 function_code_val = getqualvalue(&sw_function_code);mB 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;t sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* 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);e6 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))P< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ if (wptcnt) {, size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size)))r signal_error(SS$_INSFMEM, 0);l: 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);v6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action;  wptindex.func = wpt->func;l% wptindex.retcode = wpt->retcode;*! wptindex.flags = wpt->flags;n wpt = &wptindex;  cnt = 1; /* One entry */ } } else {> blkpnt = getlist(&sw_lblock, &cnt); /* Get LBN's to handle */ if (cnt) {t7 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)r 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;3P switch (function_code_val & IO$M_FCODE) { /* Function without modifiers */ case IO$_READPBLK: case IO$_WRITEPBLK:  case IO$_WRITECHECK: case IO$_DSE:h1 wpt1->flags = 0; /* Function with transfer */s break;* default: if (file)# lib$signal(&ld_noreadwrite, 0);;0 wpt1->flags = FLAGS_NOLBN; /* Special case */! wpt1->lbn = 0; /* Zap lbn */a. break; /* For functions without a lbn */ }  } if (file) {d9 wpt1->flags = FLAGS_FILE; /* Signal 'file' Function */r( wpt1->sbk = &sbk; /* Point to sbk */- if (wpt1->lbn == 0) /* VBN starts at 1 */e 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 */l } else wpt1->retcode = 0; } } }a if (resume)e8 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,r 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 */l}/* Disable watchU*/void nowatch(){t struct dsc sdev; int stat;e int chan;) struct watchpt *wpt, *wpt1, wptindex;o struct iosb iosb;( int cnt; int i, size; int *blkpnt, wptcnt; int index, index_val;cB 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);o if (index) { /* /INDEX? */t% index_val = getqualvalue(&sw_index);g6 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)))l signal_error(SS$_INSFMEM, 0);1: 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);t6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action;a wptindex.func = wpt->func;R% wptindex.retcode = wpt->retcode; ! wptindex.flags = wpt->flags;  wpt = &wptindex;O cnt = 1; /* One entry */ } } else {7 blkpnt = getlist(&sw_lblock, &cnt); /* Get lbn list */t if (cnt) { 7 if (!(wpt = malloc(cnt * sizeof(struct watchpt))))i 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 krv$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q"3kill */s } } }e9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,rA wpt, cnt, 0, 0, 0, LDIO_DISABLE_WATCH); /* Disable watch */a$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */o signal_error(stat, 0);}/* Show watch infod*/void show_watch(){ struct dsc sdev; int stat;t int chan;  struct watchpt *wpt; struct suspend_list *slist;h struct iosb iosb;' int wptcnt, blkcnt, slistcnt, size;r 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;e 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,aK 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */e$ signal_error(stat, iosb.status);3 if (wptcnt = iosb_lw) { /* Something to do ? */q( size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size)))e" 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);m }e9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, W 0, 0, 0, 0, 0, LDIO_GET_SUSPEND_LIST | LDIO_M_INQUIRE); /* Get suspend list size */)$ signal_error(stat, iosb.status);9 if (slistcnt = iosb_lw) { /* Something suspended ? */e/ size = slistcnt * sizeof(struct suspend_list);e 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 */e! signal_error(stat, iosb.status);; } 3 stat = sys$dassgn(chan); /* Deassign channel */l signal_error(stat, 0);N display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt); /* Show it */}s@void display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt)struct watchpt *wpt; int wptcnt; int blkpnt[]; int blkcnt;gstruct suspend_list *slist;s int slistcnt; {e int i, j;_ int index, index_val;n int *bpt;i int found, printed;e struct suspend_list *spt;i char albn[9];s char filename[256];1 if (index = getqual(&sw_index)) /* /INDEX? */25 index_val = getqualvalue(&sw_index); /* get value */m* if (wptcnt) { /* Something to do ? */ printed = 0;n@ for (i = 0; i < wptcnt; i++, wpt++) { /* For all watchpoints */ found = 0;y< if (blkcnt) { /* Check if in list the user specified */3 for (j = 0, bpt = blkpnt; j < blkcnt; j++, bpt++) if (*bpt == wpt->lbn) {a found++; break;e }, } else {i& if (index) { /* Or check by index */ if (index_val == i + 1)p found++;/ } else found++; } if (found) {?. if (!printed) { /* Print header only once */N printf("Index LBN Action Function Error return code\n");W printf("--------------------------------------------------------------------\n");n printed = 1; }k if (wpt->flags & FLAGS_FILE) {0 create_filename(s_device,&wpt->fid,filename);) printf(" %s:\n",filename);e } if (wpt->flags & FLAGS_NOLBN) F sprintf(albn, "%8s", ""); /* Blank for watchpoint without lbn */ else% sprintf(albn, "%8d", wpt->lbn);  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 + 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 */s} /*) Get a list of lbn's from the commandlinep*/int *getlist(sw, cnt) char *sw; int *cnt;t{c int more;r 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;l more = -1; blkpnt = blklist;s for (;;) {6 *blkpnt = getmulqualvalue(sw, &more); /* Get entry */% if (more < 0) /* Not specified ? */l return (blklist); *cnt += 1;# if (more) { /* more coming up? */t+ /* Get new block to accomodate size */lA if (!(blklist = realloc(blklist, (*cnt + 1) * sizeof(int))))8 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ blkpnt = blklist + *cnt;  } else break; }t return blklist; }r/* Stop running trace*/void stop_trace()t{m int stat;) int loop = 0;i char matchname[256]; struct dsc matchdev =n# {sizeof(matchname), matchname};a short rlen;  int context[2] = {0, 0}; > s_device = getqualstring(&sw_device); /* Get devicename */, if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;/ while (stat & 1) { /* Loop until we're done */sF stat = sys$device_scan(&matchdev, &rlen, &wildname, 0, &context);! if (stat == SS$_NOMOREDEV) {, 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);s3 matchname[rlen] = '\0'; /* Place terminator */f= stop_one(matchname, 0); /* Stop trace for this device */s }, } 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{e struct dsc lknam;o int stat;d char *qname; struct iosb iosb;c int lockid;f int retlen;s int count; int i; int found; int grp, mem;  union prvdef oldpriv;  struct dsc sfilename = {strlen(which), which};w struct lkidef locks[10]; struct lkidef *p;T struct itmlst item[] =0 {sizeof(locks), LKI$_LOCKS, &locks, &retlen, 0, 0, 0, 0};w struct itmlst jpi_item[] =" {8, JPI$_CURPRIV, &oldpriv, 0, 4, JPI$_GRP, &grp, 0, 4, JPI$_MEM, &mem, 0, 0, 0, 0, 0};r9 if (get_unit(which, 1) == 0) { /* Don't use unit 0 */s if (single)B lib$signal(&ld_badunit, 1, which); /* Show it to the world */ elseu return; }s= lknam.addr = build_lock(which); /* Build resource name */ # lknam.len = strlen(lknam.addr);K stat = sys$enqw(0, LCK$K_NLMODE, &ld_lksb, LCK$M_SYSTEM | LCK$M_VALBLK, 8 &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 */s 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;CU stat = sys$getjpiw(0, 0, 0, &jpi_item, &iosb, 0, 0); /* Get privs+ group + member */h! signal_error(stat, iosb.status);i9 if (!((grp == ld_lksb.valblk[0]) && /* Group the same */C (mem == ld_lksb.valblk[1]))) { /* as well as the member ? */ = if ((grp != ld_lksb.valblk[0]) && /* Group the same ? */&7 (mem != ld_lksb.valblk[1])) { /* Member the same ? */d/ if (!oldpriv.prv$v_world) /* We need WORLD */a, lib$signal(&ld_noworldpriv, 1, which);B } else if (grp == ld_lksb.valblk[0]) { /* Group the same ? *// if (!oldpriv.prv$v_group) /* We need GROUP */h, 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$U+$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q""DM_VALBLK,a 0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0);eC stat = sys$deq(ld_lksb.lockid, 0, 0, 0); /* Get rid of our lock */r signal_error(stat, 0);n break;) }  if (!found);8 lib$signal(&ld_conttracenotact, 1, fulldevspec(which));}/* Show trace infoi*/void show_trace() {i struct dsc sdev; struct dsc lknam;0 int fdt_active;  int accurate;_ int stat;l int chan;e int trcsize; int trcnum;  int contin;i int qstat; int func;e int inited;= int overrun; struct iosb iosb;s char *trcbuf;  struct itmlst item[] =( {4, JPI$_GRP, &ld_lksb.valblk[0], 0,( 4, JPI$_MEM, &ld_lksb.valblk[1], 0, 0, 0, 0, 0};  if (getqual(&sw_input) > 0)* process_input(); /* Process input file */ else {; s_device = getqualstring(&sw_device); /* Get devicename */d if (!s_device) ; lib$signal(&ld_confqual); /* Conflicting qualifiers */u< s_device = fulldevspec(s_device); /* Get full devicespec */7 if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */nE lib$signal(&ld_badunit, 1, s_device); /* Show it to the world */x sdev.addr = s_device; sdev.len = strlen(s_device);o qstat = getqual(&sw_status);_? 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);c! signal_error(stat, iosb.status);r* fdt_active = (iosb.status == SS$_WASSET);9 trcnum = iosb_lw; /* Size of tracebuffer (in entries) */- trcsize = trcnum * sizeof(struct trace_ent);n< trcbuf = malloc(trcsize); /* Get memory for trace buffer */ if (trcbuf == 0)&: signal_error(SS$_INSFMEM, 0); /* Not enough memory */= func = LDIO_GET_TRACE | LDIO_M_NOWAIT; /* Normal function */=+ if (getqual(&sw_reset) > 0) /* /RESET ? */( 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, $ LCK$M_SYSTEM | LCK$M_NOQUEUE,! &lknam, 0, 0, 0, 0, 0, 0);  if (stat == SS$_NOTQUEUED)n9 lib$signal(&ld_conttraceact, 1, fulldevspec(s_device));s signal_error(stat, 0);fL stat = sys$enqw(0, LCK$K_PRMODE, &ld_lksb, /* Write lock value block */3 LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK,s 0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0); F 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);n } inited = (contin > 0) ? 0 : -1; do {t: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,< trcbuf, trcsize, 0, 0, 0, func); /* Get trace info */ signal_error(stat, 0); stat = iosb.status;" if (stat == SS$_DATAOVERUN) {2 overrun = iosb_lw; /* Save number of overruns */ iosb_lw = trcnum;a } else {o overrun = 0;D signal_error(stat, 0); /* Test for other error than DATAOVERRUN */ } if (qstat) { 2 lib$signal(&ld_status, 3, fulldevspec(s_device),2 trcnum, iosb_lw); /* Show it to the world */ if (fdt_active)t9 lib$signal(&ld_fdtactive); /* FDT tracing active */t7 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,s( 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 ? *//K process_trace(trcbuf, iosb_lw, 0, overrun, /* Process the contents */f, nodename(), cvttime(0, 0, 0), inited); } inited = 1; } while (contin);/ stat = sys$dassgn(chan);/* Deassign channel */* signal_error(stat, 0);*' free(trcbuf); /* Get rid of buffer */3 } }t/* Process the trace buffer*/Cvoid process_trace(buf, size, ovrbuf, overrun, nname, when, inited)I char *buf; int size;astruct ovrrun *ovrbuf; int overrun; char *nname; char *when; int inited; {n 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;t static int written = 0; 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 ? */0 if (q_entries > 0) {eC v_end1 = getqualvalue(&sw_entries); /* Get #of entries to show */lD 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)h q_version_limit = 32767; if (q_version_limit <= 0)  q_version_limit = 1; q_version_limit--; }2 outflg = getqual(&sw_output); /* /OUTPUT ? */ if (outflg > 0)= outfilename = getqualstring(&sw_output); /* Get filename */a } if (outflg > 0) { if (q_bin)lP outfilename = fullfilespec(outfilename, "LD_TRACE.DAT"); /* Provide default */ else P 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 */i if (fclose(outfile) < 0): lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); }1 if (q_vsw)/ purge_file(q_version_limit, outfilename);a }/*G We want to create a normal file with some nice RMS attributes. SincenI this can't be done with fopen() we will create the file first, and useeI fdopen to setup the correct file pointers. This takes also care of thea/ substitution of the defaults filename parts.D*/ if (q_bin),9 outfilei = creat(outfilename, 0, "rfm=var", "ctx=bin");t else 8 outfilei = creat(outfilename, 0, "rfm=var", "rat=cr"); if ((outfilei < 0) ||/ ((outfile = fdopen(outfilei, "a+")) == NULL))oX lib$signal(&ld_outfilerr, 1, outfilename, vaxc$errno, 0); /* Can't open output file */ re_open = 1;t) } else if (outflg < 0) /* /NOOUTPUT ? */M return; else + outfile = stdout; /* Default output */, } end2 = 0;0) if (q_entries > 0) { /* /ENTRIES ? */. end1 = v_end1; /* Get #of entries to show *// end2 = v_end2; /* Get second part (if any) */ - if (end2 != 0) { /* There's a second part */  if ((end1 <= 0) ||> (end2 <= 0) || (end2 < end1)); lib$signal(&ld_badentparam); /* Bad /ENTRIES parameter */. } if (end1 == 0)# 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 {1& end1 = -end1; /* Make absolute */1 i = ((size - end1) < 0) ? 0 : (size - end1);n& p = (struct trace_ent *) buf + i;/ recnum = i + 1; /* First one to display */0 } } else {4 p = (struct trace_ent *) buf; /* Start of buffer */" end1 = size; /* Default = all */( recnum = 1; /* First one to display */ }l2 if (end1 > size) { /* More than we've got? */ if (end2 > 0)5 lib$signal(&ld_pastdata); /* Past end of data */ ( end1 = end2 = size; /* Enforce limit */ } if (end2 > size)& end2 = size; /* And another limit */ if (end2 > 0) A numpack = end2 - end1 + 1; /* Total number of packets to show */& else numpack = end1;# if (q_bin) { /* Binary mode */1# version = DATA_FILE_VERSION << 24;s5 if ((inited <= 0) || ((written == 0) && q_blocks)) {tT if ((fwrite(&version, sizeof(int), 1, outfile) != 1) || /* Data file version */K (fwrite(fulldevspec(s_device), 32, 1, outfile) != 1) || /* Devicename */09 (fwrite(nname, 32, 1, outfile) != 1) || /* Nodename */ 7 (fwrite(when, 24, 1, outfile) != 1)) /* Timestamp */ Z lib$signab$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q"Ul(&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 */_ if (q_blocks) {) written += (2 * (sizeof(int) + 2)) +n1 ((sizeof(struct trace_ent) + 2) * numpack);s- if (written >= ((q_blocks - 1) * 512)) {l+ 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); }a}0/* Format all tracedata*/5void show_trace_data(p, numpack, from, outfile, node,- device, when, inited, ovrbuf, overrun) struct trace_ent *p; int numpack; int from;cFILE *outfile; char *node;c char *device;s char *when;i int inited;sstruct ovrrun *ovrbuf; int overrun;{, struct ovrrun *op;! int i, j, stat, func, ovrcnt; 2 static int pid, lbn, bytecount, fdt, accurate;) static int iosb, function, timestamp;qI static int time_elapsed, time_absolute, time_combination, time_delta;t+ static int function_hex, function_text;o& static int iosb_hex, iosb_longhex;+ static int iosb_text, iosb_combination;t' static int number, header, linenum;{% static int prev_tim[2], warnings;  int restim[2]; char record[256];  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;  prev_tim[1] = 0;  }j8 if ((header > 0) && (inited <= 0)) { /* /HEADER ? */A if (fprintf(outfile, " I/O trace for device %s\n %s",p device, when) <= 0)p\ 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); else% stat = fprintf(outfile, "\n\n");: if (stat <= 0)2\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */) *record = '\0'; /* Initially nothing */p if (number > 0) strcpy(record, "Entry "); if (timestamp > 0) {a if (time_elapsed) strcat(record, "Elaps ");! else if (time_absolute)- strcat(record, "Start Time End Time ");  else if (time_combination))' strcat(record, "Start Time Elaps ");* else if (time_delta) ' strcat(record, " Delta Time Elaps ");c if (accurate) strcat(record, "uSecs "); } if (pid > 0)b! strcat(record, " Pid ");c if (lbn > 0) # strcat(record, " Lbn ");e if (bytecount > 0)  strcat(record, " Bytes ");t if (iosb > 0) { if (iosb_hex) strcat(record, " Iosb[0] "); else if (iosb_longhex)e' strcat(record, " Iosb[0] Iosb[1] ");t else if (iosb_text) strcat(record, " Iosb ");i else if (iosb_combination)t$ 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 */  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';u- if ((fprintf(outfile, "%s", record) <= 0) ||c( (fprintf(outfile, "%s", tmp) <= 0))\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ }qA for (i = 0; i < numpack; i++) { /* For all records to show */*? if ((overrun != 0) && (warnings > 0)) { /* Signal lost data */,< if (ovrbuf == 0) { /* Called with number of overruns */ ovrcnt = overrun;_5 overrun = 0; /* Prevent display for every record */ } else {4 op = ovrbuf; /* Called after reading input file */) ovrcnt = 0; /* Check overrun records */ B for (j = 0; j < overrun; j++, op++) /* For all overrunrecords */& if (op->recnum == linenum - 1) {* ovrcnt = op->overrun; /* Found match */ break;a }i } if (ovrcnt > 0)3 if (fprintf(outfile, "[%d trace record%slost]\n",e. 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) ||e# (func == IO$_AVAILABLE) || (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);h signal_error(stat, 0);- strcat(record, cvttime(&restim, 1, 1));r$ } 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) {l? stat = lib$sub_times(p->end_time, p->start_time, restim);l signal_error(stat, 0);4 strcat(record, cvttime(&p->start_time, 0, 1)); strcat(record, " ");- strcat(record, cvttime(&restim, 1, 1));(! } else if (time_delta) {u5 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) { A stat = lib$sub_times(prev_tim, p->start_time, restim); ! signal_error(stat, 0);h strcat(record, "-");b } else ! signal_error(stat, 0);  } else strcat(record, " "); 1 strcat(record, cvttime(&restim, 1, 0));e }e strcat(record, " ");? stat = lib$sub_times(p->end_time, p->start_time, restim);, signal_error(stat, 0);- strcat(record, cvttime(&restim, 1, 1));% prev_tim[0] = p->start_time[0];G% prev_tim[1] = p->start_time[1];r } if (accurate) {a& sprintf(tmp, "%6Ld",p->elapsed); strcat(record, tmp); }/ strcat(record, " ");R } if (pid > 0) { /* Pid */' sprintf(tmp, "%08X ", p->pid);y strcat(record, tmp);  }/ if (lbn > 0) { /* Logical block number */e' sprintf(tmp, "%10d ", p->lbn);a strcat(record, tmp);e }) if (bytecount > 0) { /* Bytecount */' sprintf(tmp, "%6d ", p->bcnt);e strcat(record, tmp);  }, 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]);  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) {a if (func == IO$_SENSEMODE) j = 0; else- j = ((p->iosb[0] >> 16) & 0xffff) +() ((p->iosb[1] & 0xffffO$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q#Q"f) << 16);gh if ((p->iosb[0] == SS$_FDT_COMPL) && (p->iosb[1] == 0) && (p->lbn == 0) && (p->bcnt == 0))% sprintf(tmp,"%-15s","FDT");; elseE sprintf(tmp, "%-7s %6d ", ssdef((p->iosb[0]) & 0xffff), j);n } strcat(record, tmp);* }0 if (function > 0) { /* I/O function code */ if (function_hex)' sprintf(tmp, "%04.4X ", p->func);0 else if (function_text)0 sprintf(tmp, "%s ", decode_func(p->func)); strcat(record, tmp);  } if (!(*record))/ break; /* Nothing to display, quit */i j = strlen(record);3 record[j - 1] = '\n'; /* Zap trailing space */o 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 */* } }n/*! Convert status to readable codel*/char *ssdef(code){ int code;U{  static char msgbuf[64];A struct dsc msgdsc =s {sizeof(msgbuf), msgbuf};d int stat;  short rlen;l2 stat = sys$getmsg(code, &rlen, &msgdsc, 2, 0); signal_error(stat, 0);, msgbuf[rlen] = '\0'; /* Trailing zero */. return &msgbuf[1]; /* Remove leading % */}_/* Process input file*/void process_input(){_ char *buf, *savbuf; & struct ovrrun *ovrbuf, *savovrbuf; char *infilename;) int infilei; FILE *infile; char namebuf[32];a char nodebuf[32];_ char timestmp[24]; int i; int size;  int recnum;$ 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 */,Y if (((infilei = open(infilename, O_RDONLY, 0, "dna=LD_TRACE.DAT", "ctx=bin")) < 0) ||;, ((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 */}% version = (version >> 24) & 0xff;r- if ((version == 0) || /* Old version ? */R= (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 */0; (fread(timestmp, 24, 1, infile) != 1)) /* Get timestamp */sV lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */2 s_device = namebuf; /* Point to devicename */ recnum = 0;t overruncnt = 0;  buf = savbuf = 0;  ovrbuf = savovrbuf = 0;s while (1) {fO if ((fread(&size, sizeof(int), 1, infile) != 1) || /* Get number of records */iR (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 datahB 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_error(SS$_INSFMEM, 0); /* Not enough memory */v& buf = savbuf; /* Save new address */M savbuf += (recnum * sizeof(struct trace_ent)); /* Start writing from here */  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 */>B 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 */  }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 *//*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 */  elsea< 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 */v2 if (fclose(infile) < 0) /* Close input file */7 lib$signal(&ld_closerr, 1, infilename, vaxc$errno, 0);i}c/*) Decode the I/O function to readable texti*/char *decode_func(what)l int what;u{l static char funcstr[80]; char str[80];  int modifier;n int function;) if (what == 0xffff)t return ("All");t: modifier = what & ~IO$M_FCODE; /* Get modifier bits */= function = what & IO$M_FCODE; /* Get functioncode bits */o. if (!modifier) /* No modifiers present */9 return cvttbl[function];/* Return pointer to function */r 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;n 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;o case IO$_SENSECHAR:4 chk_mod(&modifier, IO$M_SHADOW, "SHADOW", str); break;c 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;t 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;t 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;/ case IO$_READVBLK: case IO$_READLBLK:1 case IO$_READPBLK: 8 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;e case IO$_ACCESS:i case IO$_DEACCESS:a case IO$_MODIFY:w case IO$_CREATE:) case IO$_DELETE:p 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;t default:l break;* }L sprintf(funcstr, "%s%s", cvttbl[function], str); /* Form function string */7 if (modifier & ~IO$M_FCODE) /* Any modifiers left ? */rT sprintf(funcstr, "%s (%08.8X)", funcstr, modifier); /* Show excess modifiers */1 return (funcstr); /* Return pointer to string */i }r} /*< Check if bit set in modifier function, and copy function to output string if true. */.void chk_mod(modifier, mask, modifstr, outstr)int *modifier; int mask;cchar *modifstr;, char *outstr;e{n7 if (*modifier & mask) { /* Check if mask bit set */f+ strcat(outstr, "|"); /* Concatenate bar */l: strcat(outstr, modifstr); /* Concatenate inpout string */6 *modifier &= ~mask; /* Flags this bit a1,$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1qws processed */ }}/* Convert binary time to ascii,r' return only the time and not the date*/char *cvttime(time, how, full) int time[2];int how; int full;{  short rlen;m int stat;l struct dsc timdsc; static char timstr[256]; timdsc.len = 256;r timdsc.addr = timstr; U stat = sys$asctim(&rlen, &timdsc, time, full); /* Convert binary time to ascii */u signal_error(stat, 0);0 timstr[rlen] = '\0'; /* Add trailing zero */) if (how) /* Display only seconds */i return (&timstr[6]);t return timstr;}f/* Open specified filet*/void open_file(name) char *name;i{_ int stat;i int i; struct iosb iosb;a6 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 */c9 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 */a= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */f6 ld_nam.nam$l_esa = expspec; /* Expanded address */J ld_nam.nam$b_nop = NAM$M_NOCONCEAL; /* Don't hide concealed devices */3 stat = sys$parse(&ld_fab); /* Parse this one */t 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 open*/, i = ld_nam.nam$b_dev + ld_nam.nam$b_dir;+ strncpy(realspec, ld_nam.nam$l_dev, i);r8 realspec[i] = '\0'; /* Make sure it's terminated *// stat = sys$open(&ld_fab); /* And open it */x 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);l9 realspec_dsc.addr = realspec; /* Get real filespec */ ( 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;sB realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev;cB maxblocks = ld_xabfhc.xab$l_ebk; /* Get current eof pointer */; if (ld_xabfhc.xab$w_ffb == 0) /* Adjust if necessary */t maxblocks--;S#ifdef __DECC_MODE_VAXC0= fib.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */r+ fib.fib$w_fid[1] = ld_nam.nam$w_fid[1];>+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];i#else)O fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */x= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];b= fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];i#endifT stat = sys$assign(&realdev_dsc, &fchan, 0, 0, 0); /* Assign channel to device */3 signal_error(stat, 0); /* where file resides */i% atr[0].atr$w_size = SBK$K_LENGTH;oA 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_VAXCp5 fib.fib$v_notrunc = 1; /* Truncate not allowed */); fib.fib$b_wsize = -1; /* Open with cathedral windows */a#elsei[ fib.fib$r_acctl_overlay.fib$r_acctl_bits0.fib$v_notrunc = 1; /* Truncate not allowed */0c fib.fib$r_acctl_overlay.fib$r_acctl_fields2.fib$b_wsize = -1; /* Open with cathedral windows */n#endif+ fib_dsc.addr = &fib; /* Point to FIB */i fib_dsc.len = FIB$K_LENGTH;tD 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);}n/*$ Close file opened with 'open_file'*/void close_file(filename) char *filename;>{n int stat;  struct iosb iosb;v8 stat = sys$qiow(0, fchan, IO$_DEACCESS, &iosb, 0, 0,3 &fib_dsc, 0, 0, 0, 0, 0); /* Deaccess file */e$ 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 */[ if (!(stat & 1))J lib$signal(&ld_closerr, 1, filename, ld_fab.fab$l_sts, ld_fab.fab$l_stv); signal_error(stat, 0);}*/* Disconnect file from LD device*/void disconnect_unit(){f int stat;h int q_abort; int loop = 0;A char matchname[256]; struct dsc matchdev = # {sizeof(matchname), matchname};p short rlen;e int context[2] = {0, 0};n? s_device = getqualstring(&sw_device); /* Get device name */nA q_abort = getqual(&sw_abort) > 0; /* Get 'abort' qualifier */0, if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;/ while (stat & 1) { /* Loop until we're done */,F stat = sys$device_scan(&matchdev, &rlen, &wildname, 0, &context);! if (stat == SS$_NOMOREDEV) {f 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);s3 matchname[rlen] = '\0'; /* Place terminator */c2 stat = disconnect_one(matchname, q_abort, 0);! /* Disconnect this device */c: if (stat == SS$_DEVINACT) { /* If not connected... */; lib$signal(&ld_notconnected, 1, matchname); /* Flag it */  stat = 1; /* and continue */ } } } else {- stat = disconnect_one(s_device, q_abort, 1);t6 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 */s7 stat |= STS$M_INHIB_MSG; /* Don't show it again */r } })}*int disconnect_one(dev, abortflag, single) char *dev;int abortflag; int single;m{, char ldfile[256];  char lddev[64]; struct dsc sdev; char *devstr;- int stat;s int chan;i int func;, int replacefl; struct iosb iosb;  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 */d else  return (1); }n 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 ? */d: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,U lddev, sizeof(lddev), &fid, 0, 0, LDIO_GET_CONNECTION); /* Get current status */f signal_error(stat, 0);tH if (!iosb_flags.connected) /* Check 'connected' flag from driver */ return (&ld_notconnected);4 lddev[iosb.size] = '\0'; /* Terminate string */( create_filename(lddev,&fid,ldfile);2 func = LDIO_DISCONNECT; /* Normal function */ } else {"E func = LDIO_DISCONNECT | LDIO_M_ABORT; /* Some more for abort */a& strcpy(ldfile,"(not available)"); } M stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Issue disconnect */( 0, 0, 0, 0, 0, func);g! signal_error(stat, iosb.status);S2 stat = sys$dassgn(chan); /* Get rid of channel */ signal_error(stat, 0); ' if (getqual(&sw_log) > 0) /* /LOG ? */lJ lib$signal(&ld_nowdisconn, 2, devstr, ldfile); /* Show what we did */ }  return (1);a}e/* Create file for LD*/ void create() { int stat;/ int q_size;n char dummy[512];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 */n2 setup_fab(s_file, &q_size); /* Init for RMS */3 stat = sys$parse(&ld_fab); /* Parse filename */  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);c5 stat = sys$cre  $LD063.B)[VDBURG.LD.V63.SRC]LD.C;1qH"ate(&ld_fab); /* Create the file */o if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);d@ setfilechar(); /* Set NOMOVE and NOBACKUP characteristic */1 stat = sys$connect(&ld_rab);/* Hook up RAB */v if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);b0 ld_rab.rab$l_rbf = dummy; /* Dummy buffer */A stat = sys$put(&ld_rab); /* Put dummy block at end of file */n+ /* to get EOF pointer at right place */r 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 */q 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); }0}d/* Init FAB for create */void setup_fab(filname, size) char *filname; int *size;{l, ld_fab.fab$l_alq = *size; /* Filesize */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 */e8 if (getqual(&sw_contiguous) > 0) /* /CONTIGUOUS ? */& ld_fab.fab$l_fop |= FAB$M_CTG;2 ld_fab.fab$b_org = FAB$C_SEQ; /* Sequential */- ld_fab.fab$b_rfm = FAB$C_FIX; /* Fixed */s2 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) */n- ld_rab.rab$w_rsz = 512; /* Record size */u> ld_nam.nam$b_rss = NAM$C_MAXRSS; /* Max. resultant size */7 ld_nam.nam$l_rsa = resspec; /* Resultant address */l= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */,6 ld_nam.nam$l_esa = expspec; /* Expanded address */}/void setfilechar(){l int stat;i struct iosb iosb;  int attrib;u/*B Use QIO to modify the attributes since RMS won't support NOMOVE until VMS V6.0e*/#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];=+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];N#elsegO 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];n= 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;aT stat = sys$assign(&realdev_dsc, &fchan, 0, 0, 0); /* Assign channel to device */3 signal_error(stat, 0); /* where file resides */a$ 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 = 0; /* Terminator */f atr[1].atr$w_type = 0;+ fib_dsc.addr = &fib; /* Point to FIB */l fib_dsc.len = FIB$K_LENGTH; 6 stat = sys$qiow(0, fchan, IO$_ACCESS, &iosb, 0, 0,8 &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 */O6 stat = sys$qiow(0, fchan, IO$_MODIFY, &iosb, 0, 0,8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);6 stat = sys$dassgn(fchan); /* Get rid of channel */ signal_error(stat, 0);} /*& Get full filename from specified one*/!char *fullfilespec(name, defname)*char *name, *defname;l{s% static char tmpnam[NAM$C_MAXRSS];e int stat; int len; if (name)t len = strlen(name); else len = 0;t: ld_nam.nam$b_nop = NAM$M_NOCONCEAL; /* Name options */= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */=5 ld_nam.nam$l_esa = tmpnam; /* Expanded address */a/ ld_fab.fab$l_nam = &ld_nam; /* NAM block */i: 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 */t3 ld_fab.fab$l_fna = name; /* Filename address */4 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);}i/*) Get full device name from specified onem*/char *fulldevspec(name)I char *name;;{  char *tmpnam;r struct dsc resdsc;- struct dsc namdsc = {strlen(name), name}; short rlen;i int stat;D8 tmpnam = malloc(64); /* Get memory for devicename */ if (tmpnam == 0)6 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ resdsc.addr = tmpnam;i resdsc.len = 64;[ stat = lib$getdvi(&DVI$_FULLDEVNAM, 0, &namdsc, 0, &resdsc, &rlen); /* Get full name */i signal_error(stat, 0);) tmpnam[rlen] = '\0'; /* Terminator */ / return (tmpnam + 1); /* Skip leading '_' */} /* Get full filespec from file-id*/'void create_filename(device,fid,outbuf) char *device;istruct fiddef *fid; char *outbuf;r{ struct dsc outdsc; struct dsc devdsc; int stat;t short rlen;V char tmpbuf[256];L char *p;7 devdsc.len = strlen(device); /* Input devicename */, devdsc.addr = device;i; outdsc.len = sizeof(tmpbuf); /* Output buffer length */M 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 */; p = strchr(tmpbuf,':') + 1;K strcat(outbuf,p);1}c/* Get nodename*/char *nodename(){N static char tmpnam[256]; static struct dsc resdsc = {sizeof(tmpnam), tmpnam};m struct dsc tbl = {12, "LNM$FILE_DEV"};c struct dsc lognam =H {8, "SYS$NODE"}; short rlen;& struct itmlst item[2]; int stat;; tmpnam[0] = '\0'; R stat = lib$getsyi(&SYI$_NODENAME, 0, &resdsc, &rlen, 0, 0); /* Get nodename */) if (stat & 1) { /* Nodename found */)& 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;r item[0].rlength = &rlen; ( item[1].item = 0; /* End of itemlist */ item[1].buflen = 0;/ stat = sys$trnlnm(0, &tbl, &lognam, 0, &item);  signal_error(stat, 0);n& tmpnam[rlen] = '\0'; /* Terminator */ }u return (tmpnam);}d/* Build lock for trace*/char *build_lock(dev)r char *dev;{% static char locknam[256];;> strcpy(locknam, "$LOGDISK_"); /* First part of lockname */5 strcat(locknam, nodename());/* Attach nodename */m@ locknam[strlen(locknam) - 2] = '\0'; /* Zap trailing "::" */> strcat(locknam, fulldevspec(dev)); /* Attach devicename */ return locknam;*}ivoid set_outband(){n int stat;f int type;* int mask[2]; struct iosb iosb;r( $DESCRIPTOR(outbdev, "SYS$COMMAND");; stat = lib$getdvi(&DVI$_TRM, 0, &outbdev, &type, 0, 0);  signal_error(stat, 0);3 if (!type) /* Not a terminal, don't bother */  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, < &ld_exit, &mask, 0, 0, 0, 0); /* Get current status */$ signal_error(stat, iosb.status);}rvoid purge_file(limit, name) int limit; char *name;r{/ int stat;  char *pos; char tmpnam[256];& struct dsc fildsc; $DESCRIPTOR(deflt, ".DAT");i3 pos = strrchr(name, ';'); /* Find trailing ; */ if (pos != 0) {n# if (name[strlen(name) - 1] != ';')d, return; /* Exact filespec speficied */ else & *pos = '\0'; /* Zap trailing ; */ }f if (limit == 0)ND sprintf(tmpnam, "%s;%d", name, limit); /* Delete lastest version */ elseA sprintf(tmpnam, "%s;-%d", name, limit); /* Delete old version */f fildsc.addr = tmpnam;;  جD3$LD063.B)[VDBURG.LD.V63.SRC]LD.C;1q+ fildsc.len = strlen(tmpnam); stat = 1;h/ 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);a}avoid ld_exit(param)n int param;{C int stat;.H if ((outfile != 0) && (outfile != stdout)) { /* Close output file */ if (fclose(outfile) < 0)M< lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); }&, 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);o }; if (param == 0) { /* Called by blocking ast routine */  lib$signal(&ld_tracestopped); exit(&ld_tracestopped); } else exit(1);v})void signal_error(st1, st2) int st1, st2;t{i int stat1, stat2;s int modified1, modified2;*I modified1 = convert_error(st1, &stat1); /* Return reasonable error */ + modified2 = convert_error(st2, &stat2); 4 if ((!(stat1 & 1)) || (st2 && (!(stat2 & 1)))) { if (modified1 || modified2) { if (stat1 == 1) lib$signal(stat2, 0, 0); elsea$ lib$signal(stat1, 0, stat2, 0, 0); } else {l if (stat1 == 1). lib$signal(&ld_detectederr, 0, stat2, 0, 0); elset8 lib$signal(&ld_detectederr, 0, stat1, 0, stat2, 0, 0); } }m}d/*; Return a reasonable error for some returned by the driverm*/'int convert_error(oldstatus, newstatus)int oldstatus;int *newstatus;b{/ int code = 0;o# if (oldstatus == SS$_DEVASSIGN)=; code = &ld_devassign; /* 'Device has channels assigned' */_( else if (oldstatus == SS$_DEVACTIVE)0 code = &ld_alrdyconn; /* 'Already connected' */' else if (oldstatus == SS$_DEVINACT)$* code = &ld_notconn; /* 'Not connected' */% else if (oldstatus == SS$_NODATA)l7 code = &ld_trcdisabl; /* 'Tracing already disabled' */_* else if (oldstatus == SS$_TOOMUCHDATA)5 code = &ld_trcenabl; /* 'Tracing already enabled' */_' else if (oldstatus == SS$_DATALOST)r= code = &ld_nowatchdata; /* 'No watchpoint data available' */ ( else if (oldstatus == SS$_DATACHECK)5 code = &ld_wptnotfound; /* 'Watchpoint not found' */t( else if (oldstatus == SS$_NOTVOLSET)I code = &ld_fileonvolset;/* 'File watchpoint on volumeset not allowed' */ ( 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' */$( 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)fD code = &ld_notvisible; /* 'Device is not visible on other nodes' */' else if (oldstatus == SS$_BADPARAM)o; code = &ld_invgeometry; /* 'Invalid geometry specified' */r% else if (oldstatus == SS$_UNSAFE)tS code = &ld_devconnected;/* 'Cannot set allocation class with active LD devices' */f' else if (oldstatus == SS$_WRONGACP)*F code = &ld_notods2; /* 'Container file must be on an ODS-2 volume' */ else {5 *newstatus = oldstatus; /* Return original status */= return 0; /* Not modified */ }*/ *newstatus = code; /* Return new status */, return 1; /* Modified */}f.fab$l_stv); sign*[VDBURG.LD.V63.SRC]LD.H;1+,,./@@ 4E-)0123KPWO56cK6ߞ77Y89G@@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 "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/*( Structure definitions and declarations*/ 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; int valblk[4]; };struct FAB ld_fab;struct RAB ld_rab;struct NAM ld_nam;struct XABFHC ld_xabfhc;struct atrdef atr[2];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 realdev_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_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_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( }rf$LD063.B,)[VDBURG.LD.V63.SRC]LD.H;1ES sw_iosb,"IOSB");$$DESCRIPTOR(sw_function,"FUNCTION");&$DESCRIPTOR(sw_timestamp,"TIMESTAMP"); $DESCRIPTOR(sw_number,"NUMBER");$DESCRIPTOR(sw_input,"INPUT"); $DESCRIPTOR(sw_output,"OUTPUT");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 now  atch();void protect();void noprotect();void process_trace();void show_trace_data();void show_trace();void process_input();void setfilechar();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();void getdevnam();int get_unit();void set_seed();/* VMS Routines*/int cli$present();int cli$get_value();/* 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, "",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_notods2;/*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.V63.SRC]LDDRIVER_ALPHA.MAR;1+,N./@@ 4U-)0123KPWO56zv`7IaY89G@@HJ / .TITLE LDDRIVER, Alpha/VMS Logical Disk driver .IDENT 'V6.3'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.;; 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.enet.dec.com;; REVISION HISTORY:;,; Jur van der Burg 25-OCT-1988 Version 01.01; Modified for VMS V5;,; 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 wa r 1$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1UKs 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 17-FEB-1993 Version 03.00;; Added trace capabilities; Added 'replace' function;,; 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 14-APR-1993 Version 04.00;;; Add support for waiting for tracedata to become available; Add lost tracedata detection;+; Jur van der Burg 8-JUL-1993 Version 04.01;5; Promote bytecount field in tracerecord to longword.;,; 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 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 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 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 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 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.;;;---;2;MDDRIVER_WORKAROUND=1 ; Work around MDdriver bug2;DUDRIVER_WORKAROUND=1 ; Work around DUdriver bug .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$ $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/ $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# $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 FORKL K9$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U"OCK 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 thread! BEQL L30 ; Ok, dismiss thread4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread .IFF MOVL KPBADR,R1, MOVL R0,KPB$Q_FR3(R1) ; Save return status' CMPW R0,#SS$_NORMAL ; Queued thread! BEQL L30 ; Ok, dismiss thread- KP_RESTART KPB=KPBADR ; Restart main thread .ENDCL30: RET;; Request completed callback;L40: .CALL_ENTRY) MOVL FLK_PRM1(AP),R5 ; Restore context .IF IDN ,<0>% MOVL UCB$L_IRP(R5),R3 ; Restore IRP4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread .IFF- KP_RESTART KPB=KPBADR ; Restart main thread .ENDC RET;L50: .ENDM;; Macro to dequeue a lock;; Input: R5 = UCB;1 .MACRO DEQ_LOCK LOCKID,VALBLOCK=0,?L10,?L20,?L30 $LCK_DEQUE LKID=LOCKID,- VALBLK=VALBLOCK,- CTX_PRM1=R5,- RETADR=L20,- CONTINUE=L10)L10: MOVL UCB$L_IRP(R5),R3 ; Restore IRP7 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 context% MOVL UCB$L_IRP(R5),R3 ; Restore IRP. MOVL R0,IRP$L_IOST1(R3) ; Save return status4 KP_RESTART KPB=IRP$PS_KPB(R3) ; Restart main thread RETL30: .ENDM;*; Macro to save irp fields for FDT tracing;; Input: R3 = IRP;; Output: R9 = IRP$L_FUNC(R3).; R10 = Pid from IRP$L_PID(R3) or zero; .MACRO SAVE_IRP_FIELDS ?L10- MOVL IRP$L_FUNC(R3),R9 ; Save function code CLRL R10 ; Assume no pid7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),L10 ; Mount verify IRP?< 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 IDL10: .ENDM; .MACRO UNIVERSAL_SYMBOL LABELLABEL: .ENDM;; Macro to aid debugging;! .MACRO BRK NUMBER=0,WHEN=IF,?L10 .IF DF DEBUG .IF IDN , MOVL #NUMBER,G^EXE$GL_SITESPEC BBC #NUMBER,G^SGN$GL_USERD1,L10 JSB G^INI$BRKL10: .IFF 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 link' $DEF LDIOB_W_SIZE .BLKW 1 ; Size field' $DEF LDIOB_B_TYPE .BLKB 1 ; Type field, $DEF LDIOB_B_FDT .BLKB 1 ; FDT routine call $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 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 bytecount) $DEF LDIOB_L_KPB .BLKL 1 ; KPB for trace 5 $DEF LDIOB_L_FWDQFL .BLKL 1 ; Forwarded IRP queue FLC5 $DEF LDIOB_L_FWDQBL .BLKL 1 ; Forwarded IRP queue BLR( $DEF LDIOB_W_IRPCNT .BLKW 1 ; IRP count1 $DEF LDIOB_W_SPARE .BLKW 1 ; Spare for alignmentR'LDIOB_K_LENGTH = . ; Length of LDIOB  $DEFEND LDIOB;E ; Trace block ;VK; Watch out that the fields from 'PID' to 'ELAPSED' match the correspondingAF; fields in the LDIOB structure above. These fields are moved with one; instruction in LD_TRACE.;*5 $DEFINI LDTRCENT,GLOBAL ; LOGICAL DISK Trace entryE $DEF LDTRC_L_PID .BLKL 1 ; PidO1 $DEF LDTRC_L_ADDR .BLKL 1 ; Logical block number & $DEF LDTRC_L_BCNT .BLKL 1 ; Bytecount) $DEF LDTRC_W_FUNC .BLKW 1 ; FunctioncodeE% $DEF LDTRC_W_RSVD .BLKW 1 ; ReservedL! $DEF LDTRC_Q_IOSB .BLKQ 1 ; IOSBO* $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 blocko;a6 $DEFINI LDWATCHENT,GLOBAL ; LOGICAL DISK Watch entry- $DEF LDWATCH_L_FLINK .BLKL 1 ; Forward links. $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 codei? $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 link16 $DEF LDWATCH_L_SUSPBL .BLKL 1 ; Suspend backward link3 $DEF LDWATCH_L_VBN .BLKL 1 ; Virtual block numberp2 $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 ; Seqm% $DEF LDWATCH_W_FID_RVN .BLKW 1 ; Rvng0LDWATCHENT_K_LENGTH = . ; Length of LDWATCHENT; ; Watch block block flagse;v _VIELD LDWATCH,0,< - , ,- ; Function characteristics) ,- ; Remove all entriest) ,- ; Extent on volumeset  >d;.; Subfields in CHARS field;i _VIELD LDWATCH,0,< -' ,- ; Function without lbn. ,- ; File accessi >  $DEFEND LDWATCHENTi;; Watchpt parameter blockE;9? $DEFINI LDWATCHPT,GLOBAL ; LOGICAL DISK Watch parameter entryt5 $DEF LDWATCHPT_L_LBN .BLKL 1 ; Logical block numberc' $DEF LDWATCHPT_W_FLAGS .BLKW 1 ; Flagsh4 $DEF LDWATCHPT_W_ACTION .BLKW 1 ; Action to perform- $DEF LDWATCHPT_W_FUNC .BLKW 1 ; Functioncodep/ $DEF LDWATCHPT_W_RETCODE .BLKW 1 ; Return code1 $DEF LDWATCHPT_L_SBK .BLKL 1 ; Statistics block / $DEF LDWATCHPT_W_FID_NUM .BLKW 1 ; File ID Nume' $DEF LDWATCHPT_W_FID_SEQ .BLKW 1 ; Seq' $DEF LDWATCHPT_W_FID_RVN .BLKW 1 ; Rvnn.LDWATCHPT_K_LENGTH = . ; Length of LDWATCHPT;e; Watch parameter block flagsr;e _VIELD LDWATCHPT,0,< -e, ,- ; Function characteristics) ,- ; Remove all entriesr >d; ; Subfields in CHARS field;a _L$|$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U8(VIELD LDWATCHPT,0,< -' ,- ; Function without lbnc ,- ; File accessk >f $DEFEND LDWATCHPT;g; Suspended process list ;t@ $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list+ $DEF LDSUSPLST_L_PID .BLKL 1 ; Process ida5 $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block numberd' $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; Flagsn) $DEF LDSUSPLST_W_ACTION .BLKW 1 ; Actionb) $DEF LDSUSPLST_W_FUNC .BLKW 1 ; Functionl/ $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return codes.LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST;s; $SNDOPR parameter list;n5 $DEFINI LDSNDOPRLST,GLOBAL ; $SNDOPR parameter listc4 $DEF LDSNDOPRLST_L_ASTQFL .BLKL 1 ; AST queue flink4 $DEF LDSNDOPRLST_L_ASTQBL .BLKL 1 ; AST queue blink' $DEF LDSNDOPRLST_W_SIZE .BLKW 1 ; Sizer' $DEF LDSNDOPRLST_B_TYPE .BLKB 1 ; Typeu, $DEF LDSNDOPRLST_B_RMOD .BLKB 1 ; Ast flags, $DEF LDSNDOPRLST_L_PID .BLKL 1 ; Process id- $DEF LDSNDOPRLST_L_AST .BLKL 1 ; AST addressn2 $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_FLAGS .BLKW 1 ; Flagsr+ $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Actionn+ $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Functions1 $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return codeo1 $DEF LDSNDOPRLST_W_FID_NUM .BLKW 1 ; File ID Num ) $DEF LDSNDOPRLST_W_FID_SEQ .BLKW 1 ; Seqs) $DEF LDSNDOPRLST_W_FID_RVN .BLKW 1 ; Rvna1 $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device name1LDSNDOPRLST_K_DEVNAM = 64 ; Device name lengthb2LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST; $; LD file lockvalueblock definitions; 3 $DEFINI LDFLVB,GLOBAL ; LD file lockvalue block , $DEF LDFLVB_W_CYLINDERS .BLKW 1 ; Cylinders' $DEF LDFLVB_B_TRACKS .BLKB 1 ; Trackse( $DEF LDFLVB_B_SECTORS .BLKB 1 ; Sectors5 $DEF LDFLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumberp1 $DEF LDFLVB_L_ALLOCLS .BLKL 1 ; Allocation classh* $DEF LDFLVB_W_UNIT .BLKW 1 ; Unit number% $DEF LDFLVB_B_FLAGS .BLKB 1 ; Flagsk*ASSUME . LE 16 ; Length must be <= 16;d; Subfields in FLAGS field;t _VIELD LDFLVB,0,< -$ ,- ; Shared accessable >D $DEFEND LDFLVB ;e&; LD device lockvalueblock definitions;o3 $DEFINI LDDLVB,GLOBAL ; LD file lockvalue blockI) $DEF LDDLVB_W_FID .BLKW 1 ; File ID Nums& $DEF LDDLVB_W_SEQ .BLKW 1 ; File Seq& $DEF LDDLVB_W_RVN .BLKW 1 ; File Rvn1 $DEF LDDLVB_B_ALLOCLS .BLKB 1 ; Allocation class * $DEF LDDLVB_W_UNIT .BLKW 1 ; Unit number+ $DEF LDDLVB_T_DEVNAM .BLKB 7 ; Devicenameu*ASSUME . LE 16 ; Length must be <= 16 $DEFEND LDDLVBr;s#; Definitions for our functioncodesn;s- IO$_LD_CONTROL = 20 ; Main control functionb ; (Physical I/O function)u' LDIO_CONNECT = 0 ; Connect drive/file - LDIO_DISCONNECT = 1 ; Disconnect drive/file % LDIO_ENABLE_TRACE = 2 ; Enable trace' LDIO_DISABLE_TRACE = 3 ; Disable trace $ 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 device % LDIO_ENABLE_WATCH = 8 ; Enable watchi' LDIO_DISABLE_WATCH = 9 ; Disable watcht' LDIO_GET_WATCH = 10 ; Get watchpointso, LDIO_RESUME_WATCH = 11 ; Resume watchpoints= LDIO_GET_SUSPEND_LIST = 12 ; Get list of suspended processes 0 LDIO_ENABLE_PROTECT = 13 ; Enable write-protect2 LDIO_DISABLE_PROTECT = 14 ; Disable write-protect/ LDIO_SET_ALLOCLASS = 15 ; Set allocation classt LDIO_V_FUNC = 0S LDIO_S_FUNC = 8t;; Function modifiers;  $DEFINI LDIO,GLOBAL _VIELD LDIO,8,<-e" ,- ; Replace drive# ,- ; Abort disconnect2$ ,- ; Get buffer size, ,- ; Don't wait for tracedata3 ,- ; Reset after retrieving tracedatao ,- ; Shared access1 ,- ; Use RSCC counter for timing ,- ; FDT traceg >h $DEFEND LDIO ;p; Watchpoint actions;t* 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 OPCOMa& WATCH_ACTION_MAX = 3 ; Maximum action;o .PAGE;+++; Definitions for IRP fields;--- $DEFINI IRP,GLOBAL ; IRP  .=IRP$K_LENGTHD/ $DEF IRP$L_LD_LDUCB .BLKL 1 ; Logical Disk UCB./ $DEF IRP$L_LD_LDIOB .BLKL 1 ; Logical disk IOBn6 $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 UCBl( $DEF UCB$L_LD_KPB .BLKL 1 ; KPB address@ $DEF UCB$L_LD_BLKKPB .BLKL 1 ; Blocking AST routine KPB address0 $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk; $DEF UCB$L_LD_AIOFL .BLKL 1 ; Active I/O list forward link$< $DEF UCB$L_LD_AIOBL .BLKL 1 ; Active I/O list backward link)ASSUME UCB$L_LD_AIOBL EQ UCB$L_LD_AIOFL+4o2 $DEF UCB$L_LD_IOBFL .BLKL 1 ; LDIOB free queue FL2 $DEF UCB$L_LD_IOBBL .BLKL 1 ; LDIOB free queue BL8 $DEF UCB$L_LD_IRPFL .BLKL 1 ; Forward IRP free queue FL8 $DEF UCB$L_LD_IRPBL .BLKL 1 ; Forward IRP free queue BL1 $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointera1 $DEF UCB$L_LD_WCB .BLKL 1 ; Window control blocka) $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORBe4 $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 16 ; + lock value block! $DEF UCB$T_LD_PD_RESNAM .BLKB 28d% ; Phys. dev. lock resource named $DEF UCB$Q_LD_FILE_LKSB .BLKB 8 ; Lock status block $DEF UCB$A_LD_FILE_LVB .BLKB 16 ; + lock value block# $DEF UCB$T_LD_FILE_RESNAM .BLKB 40a0 ; File resource name 32 bytes + descriptor $DEF UCB$Q_LD_DEV_LKSB .BLKB 8$ ; Lock status blockf $DEF UCB$A_LD_DEV_LVB .BLKB 16k ; + lock value block" $DEF UCB$T_LD_DEV_RESNAM .BLKB 4402 ; Device resource name 32 bytes + descriptor! $DEF UCB$L_LD_TRCWAITQFL .BLKL 1t* ; Trace data wait queue forward link! $DEF UCB$L_LD_TRCWAITQBL .BLKL 1$+ ; Trace data wait queue backward linka" $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1+ ; Trace mutex wait queue forward link " $DEF UCB$L_LD_TRCMUTEXQBL .BLKL 1, ; Trace mutex wait queue backward link $DEF UCB$L_LD_TRCMUTEX .BLKL 1= ; 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 1A" ; Allocated tracebuffer size $DEF UCB$L_LD_TRCBUFPTR .BLKL 1 ; Tracebuffer pointer $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 1! ; Watch queue backward link_ $DEF UCB$L_LD_WATCHCNT .BLKL 1L ; Watch queue entry countF, $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 amountt; $DEF UCB$L_LD_CPID .BLKL 1 ; Charge PID of cloning processE: $DEF UCB$Q_LD_CYCLEFREQ .BLKQ 1 ; Cycle counter frequency) $DEF UCB$W_LD_FLAGS .BLKW 1 ; Flags byte. $VIELD UCB,0,< -0 , - ; Conn./Disconn. status bit. ,- ; Rep7lace mode status bit. ,- ; Connected to DECRAM disk$ ,- ; Write protect' ,- ; Shared accessable;2 ,- ; Containerfile on volumeset' ,- ; DSE not supportedD4 ,- ; Virtual I/O to container file3 ,- ; Use RSCC counter for timingt= ,- ; FDT tracing requested (internal only)G4 ,- ; Forkblock busy (internal only)3 ,- ; Disc. pending (internal only)3 >L; ; Filename string buffer + $DEF UCB$K_LD_UCBLEN ; Length of new UCBI( $DEFEND UCB ; End of UCB definitions .PAGE .SBTTL Standard tablese;+++; Driver prologue table_;---3 DPT_FLAGS= ; SMP safei! ; Driver supports snapshotsQ ; if not in a cluster5 DPTAB - ; DPT-creation macro& END=LD_END,- ; End of driver label! ADAPTER=NULL,- ; Adapter typeV- UCBSIZE=,- ; Length of UCB2, MAXUNITS=LD_MAX_٥ $LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U79UNITS,- ; Max nr of units! NAME=LDDRIVER,- ; Driver name.$ FLAGS=DPT_FLAGS,- ; Default flags STEP=2 ; Step 2 driverS" 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 IPLA+ DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPLW: DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device! DEV$:M_ODV!- ; output deviceK" 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),) DEV$M_CDP!- ; To prevent MSCP servingR/ DEV$M_MSCP> ; Fake to get shadowing to workR: DPT_STORE UCB,UCB$L_STS,L,UCB$M_TEMPLATE; Template device; DPT_STORE UCB,UCB$L_LD_TRCBUF,L,0 ; Pointer to tracebufferE7 DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device class A 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 tot' ; physical xlation.% DPT_STORE REINIT ; Start of reloadt ; initialization table2 DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT) DPT_STORE END ; End of initialization;+++; Driver dispatch tablea;--- 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 tableN;--- FDT_INI LD_FUNCTABLE FDT_BUF - ; LD control functions? FDT_ACT LD_FDT_SHAD_WCHECK,<- ; Check write to shadow set mbrD% WRITELBLK,- ; Write LOGICAL Block & WRITEPBLK,- ; Write Physical Block$ WRITEVBLK> ; Write VIRTUAL Block+ FDT_ACT LD_FDT_READBLK,- ; Read functions$ ; Read virtual block/ FDT_ACT LD_FDT_WRITECHECK,- ; Write functions ; Write check' FDT_ACT LD_FDT_DSE,<- ; DSE functionF! DSE> ; Data Secutiry EraseH- FDT_ACT LD_FDT_ACCESS,- ; Access functions#! ; Create file/dir0 FDT_ACT LD_FDT_DEACCESS,- ; Deaccess functions ; Deaccess file- FDT_ACT LD_FDT_MODIFY,- ; Modify functions ! ; Modify file attributes$+ FDT_ACT LD_FDT_MOUNT,- ; Mount functionsD ; Mount volume; FDT_ACT LD_FDT_LCLDSKVALID,- ; Local disk valid functionsR ; Pack acknowledgeD6 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 modeT* FDT_ACT LD_FDT_SETCHAR,- ; Set functions& ; Set mode : FDT_ACT LD_FDT_CRESHAD,- ; Create shadowset virtual unit 4 FDT_ACT LD_FDT_REMSHAD,- ; Remove shadowset member L5 FDT_ACT LD_FDT_CONTROL,- ; General LDdriver control. ;$ ; Local data; .SAVE_PSECT% .PSECT $$$110_LD_DATA,LONG,WRT,NOEXE ;e UNIVERSAL_SYMBOL LD_REFCNTB ;LD_REFCNT: % .LONG 0 ; Number of active devices ;mFLBN_WP: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\ .ASCII \PID: !XL!/\r .ASCII \Image: !AC!/\t .ASCII \Device: !AC!/\I .ASCII \Function: !XW!/\C .ASCII \LBN: !UL\ELBN_WP_LEN=.-LBN_WP ;iFVBN_WP: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\ .ASCII \PID: !XL!/\E .ASCII \Image: !AC!/\n .ASCII \Device: !AC!/\S .ASCII \Function: !XW!/\E .ASCII \VBN: !UL!/\S! .ASCII \File id: (!UW,!UW,!UW)\ VBN_WP_LEN=.-VBN_WPR;_4NONESTR: .ASCIC /None/ ; If no imagename available<OPCOM_NAME: .ASCIC /OPCOM/ ; Process name of OPCOM process .RESTORE_PSECTC;; 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; ; Functional description:i;f9; This routine is invoked via an IO$_LD_CONTROL function.A=; We will dispatch to the various other routines according tog; the P6 parameter.$; ; Inputs:L; ; 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)*; 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;1 ; Outputs:;e;; The routine must preserve all registers except R0-R2, andd ; R9-R11.D;C;--I UNIVERSAL_SYMBOL LD_FDT_CONTROL1;LD_FDT_CONTROL: ; General Control FDT routine_ $DRIVER_FDT_ENTRY* MOVL IRP$L_QIO_P6(R3),R0 ; Get parameter1 MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRPL ASSUME LDIO_S_FUNC EQ 89 DISPATCH R0,TYPE=B,<- ; Dispatch according to function; ,-,$ ,-( ,-* ,-" ,-& ,-, ,-! ,-c( ,-* ,-" ,-( ,-) ,-n, ,-. ,-) >U: 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 routinee;+++?; LD_GET_CONNECTION, Get connection characteristics FDT routinec;n; Functional description:_;,F; This routine returns the full filename string to the callers buffer,-; specified via the descriptor address in P1.n; 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 diskg; 1 : DECRAM disk.; bit 3 = 0 : Normal accessS; 1 : Write protectedE;D ; Inputs:E;D; R0-R2 - scratch registersUI.; 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)e*; 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 stringS:; P2(AP) - Size of buffer to receive the devicename striS$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1UxJngA; P3(AP) - Address of buffer to receive file-id of connected filer; ; Outputs:;R;; The routine must preserve all registers except R0-R2, and_ ; R9-R11. ; ;--r# UNIVERSAL_SYMBOL LD_GET_CONNECTION;3;LD_GET_CONNECTION: ; Get Connection FDT routines. 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 troubleeA IFNOWRT #6,@IRP$L_QIO_P3(R3),20$ ; Check if fid buffer writeableN3 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 #^ML! MOVL R2,R5 ; Copy ucb addressO! MOVZBL #1,R4 ; DVI$_ALLDEVNAML. MOVL IRP$L_QIO_P1(R3),R1 ; Address of buffer* MOVZWL IRP$L_QIO_P2(R3),R0 ; Buffer size. CALL_CVT_DEVNAM ; Get alloclass devicenameL POPR #^M+ PUSHR #^M ; Save across unlockg* CALL_IOUNLOCK ; Unlock the IO database. POPR #^M ; Restore IRP and status BLBC R0,20$ ; Trouble3 MOVL IRP$L_QIO_P3(R3),R2 ; Get fid buffer addressn 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)B10$: MOVZWL #SS$_NORMAL,R0- INSV R1,#16,#16,R0 ; Place high word in R0b< 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;; Functional description:t;i:; 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:;;s; R0-R2 - scratch registersN.; 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 data ;S ; Outputs:; ; R0 - I/O statusn; SS$_NORMAL if succcess,6; SS$_WASSET if success and FDT tracing was enabled; R1 - Number of returned bytesd;o;; The routine must preserve all registers except R0-R2, and ; R9-R11.a;f;--a 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 activeR$20$: CALL_ABORTIO ; Abort the I/O730$: MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smallg. 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 accessability0 MOVL R0,IRP$L_SVAPTE(R3) ; Save buffer address) BSBW LD_MOVE_TRACE ; Move data to userG' BLBC R0,60$ ; Stop on overrun errorU$ ; The count will always be the' ; number of packets in the buffer;G50$: DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to number of entriese 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;$; Functional description:;A; I8; This routine moves the tracedata to the user's buffer.;; If the modifier LDIO_RESET was specified for the transfera0; then the tracebuffer will be reset afterwards.;C ; Inputs:T;L.; R3 - address of the IRP (I/O request packet).; R5 - address of the UCB (unit control block); ; Outputs:;PD; R0 - Status, either SS$_NORMAL or SS$_DATAOVERUN (buffer overflow)-; R1 - 0 or on failure number of lost packetsm; R2 - number of bytes moved;B7; The routine must preserve all registers except R0-R2.B; ;--  UNIVERSAL_SYMBOL LD_MOVE_TRACEC;LD_MOVE_TRACE:M- .JSB_ENTRY INPUT=,OUTPUT=,-  PRESERVE= PUSHR #^M( MOVL IRP$L_SVAPTE(R3),R0 ; Get address% MOVL IRP$L_BCNT(R3),R1 ; Get lengthv) 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 pointerr5 MOVL UCB$L_LD_TRCWRAP(R5),R9 ; Did the buffer wrap?U BEQL 60$ ; No  SUBL3 R2,R9,R1 ; Bytecountw MOVL R1,R10 ; Save count/ BSBW MOVE_TRACE ; Move the data (first part)C<60$: MOVL UCB$L_LD_TRCBUF(R5),R2 ; Point to start of buffer# SUBL3 R2,UCB$L_LD_TRCBUFPTR(R5),R1a# 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$ ; NoP;LA; Calculate number of lost packets. This will be done as follows:1; 7; Count = (((TRCBUFPTR - TRCBUF) / LDTRCENT_K_LENGTH) +FF; ((TRCLOST - 1) * ((TRCWRAP - TRCBUF) / LDTRCENT_K_LENGTH)));  DECL R1 ; TRCLOST - 1k0 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCWRAP - TRCBUF UCB$L_LD_TRCWRAP(R5),R0F3 DIVL2 #LDTRCENT_K_LENGTH,R0 ; / LDTRCENT_K_LENGTH MULL2 R1,R02 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCBUFPTR - TRCBUF UCB$L_LD_TRCBUFPTR(R5),R1K3 DIVL2 #LDTRCENT_K_LENGTH,R1 ; / LDTRCENT_K_LENGTHh ADDL2 R0,R1 ; Result8 MOVZWL #SS$_DATAOVERUN,R0 ; We were overrun by a truck-70$: BBC #LDIO_V_RESET,- ; Reset requested? IRP$L_EXTEND(R3),80$ ; No- 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)80$: UNLOCK_TRACE ; Unlock trace mutex,% MOVL R10,R2 ; Return amount movedT POPR #^Mf RSB;l MOVE_TRACE:_+ .JSB_ENTRY INPUT=,OUTPUT=T PUSHR #^M MOVL R1,R6 ; Length, MOVL R2,R1 ; Sourcef 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+1r POPR #^MS RSB .PAGE- .SBTTL LD_DISCONNECT, Disconnect FDT routinep;+++'; LD_DISCONNECT, Disconnect FDT routine;T; Functional description:o; D; This routine will disconnect the Logical disk from the Phys. Disk.G; If LDIO_M_ABORT is specified we simply skip all checks and disconnectT; the LD device as it is.D;EC; A prereqisite to call these routines, is that the file is opened, '; except when we replace a whole drive.;;a ; Inputs:; ; R0-R2 - scratch registersT.; 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)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:; P1(AP) - Address of the SBK block (Ignored with abort or!; replaced drive)_;> ; Outputs:;c;; The routine must preserve all registers except R0-R2, andr ; R9-R11.i;N;-- UNIVERSAL_SYMBOL LD_DISCONNECTd,;LD_DISCONNECT: ; Disconnect FDT routine+ MOVZWL #SS$_DEVINACT,R0 ; Assume inactivea7 BBC #UCB$V_LD_CONSTS,- ; Check if in DISCONNECT stateU UCB$W_LD_FLAGS(R5),10$; MOVZWL #SS$_DEVFOF$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1UV[REIGN,R0 ; Assume foreign mounted statusf> BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),10$; Device foreign mounted?1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted status=6 BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),10$; Device mounted?5 MOVZWL #SS$_DEVASSIGN,R0 ; Assume channels assignedI/ 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 buffers( CLRL -(SP) ; Storage for CCB address) PUSHAL (SP) ; Address for CCB storageD- PUSHL IRP$L_CHAN(R3) ; Save channel number + CALLS #2,IOC$CHAN_TO_CCB ; Convert to CCB-" MOVL (SP)+,R1 ; Get CCB address BLBC R0,10$ ; ErroraD 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 routinea;+++!; LD_CONNECT, Connect FDT routineC; ; Functional description:L;T@; This routine will connect a Logical disk to the Physical Disk.; C; A prereqisite to call these routines, is that the file is opened,'; except when we replace a whole drive.P; ; Inputs:;N; 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 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 TracksL; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;o ; Outputs:;>;; The routine must preserve all registers except R0-R2, and ; R9-R11.;R;--A 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$C510$: BBC #LDIO_V_SHARE,- ; Shared access requested?n IRP$L_EXTEND(R3),20$, TSTL @#CLU$GL_CLUB ; Cluster code loaded? BNEQ 20$ ; Yes, MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported BRW 140$C.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 R0I9 CALL_IOLOCKR SAVE_R1=NO ; Lock the IO database for read, MOVL (SP)+,R0 .DISABLE FLAGGING- MOVL SBK$L_FCB(R0),R1 ; Get the FCB addressI .ENABLE FLAGGINGa% BGEQ 30$ ; Must be system addressd6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 30$ ; No, return errorA$ TSTL FCB$L_REFCNT(R1) ; File open? BEQL 30$ ; No, error7 MOVL R1,UCB$L_LD_FCB(R5) ; Save FCB address for laterN, MOVL FCB$L_WLFL(R1),R1 ; Get a WCB address3 MOVL #SS$_FILNOTACC,R0 ; Assume completely mappedn/ BBC #WCB$V_COMPLETE,- ; Check if complete maps WCB$B_ACCESS(R1),35$- MOVL R1,UCB$L_LD_WCB(R5) ; Save WCB addressn7 MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical device>! MOVL UCB$L_VCB(R1),R2 ; Get VCB_! MOVL VCB$L_AQB(R2),R0 ; Get AQBa; CMPB AQB$B_ACPTYPE(R0),#AQB$K_F11V2 ; Serviced by F11BXQP?h BNEQ 27$ ; NoR. TSTL VCB$L_RVN(R2) ; Relative volume number) BEQL 25$ ; Branch if not a volume set + MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addressL 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 lengthI/ CALL_WRITECHK ; Check if string is readable4 PUSHR #^M ; Save some registers9 CALL_IOLOCKR SAVE_R1=NO ; Lock the IO database for read8 MOVL IRP$L_QIO_P1(R3),R1 ; Get devicename descr. addr.+ CALL_SEARCHDEV ; Search the IO databasef5 POPR #^M ; Restore the registersa! BLBC R0,90$ ; Return on errorT= MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. disk+550$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid devicei7 CMPB UCB$B_DEVCLASS(R1),#DC$_DISK ; Connect to a disk?  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...d/90$: PUSHR #^M ; Save across unlocks* CALL_IOUNLOCK ; Unlock the IO database1 POPR #^M ; Restore the return statusd BRW 140$ -100$: BBC #LDIO_V_REPLACE,- ; Replace drive?f IRP$L_EXTEND(R3),120$ / MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCBi; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted statusu. BBS #DEV$V_FOR,- ; Check if foreign mounted UCB$L_DEVCHAR(R2),140$1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted statust0 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 exitR BRW 130$ ; Continue ;120$: BSBW LD_SET_GEOMETRY ; Fill in geometry informationN 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 addressB .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/OO)140$: CALL_ABORTIO ; And abort the I/OL .PAGE5 .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometry_;+++/; LD_SET_GEOMETRY, Setup pseudo device geometry ;I; Functional description:A;>;; This routine will set the geometry for the pseudo device.E;O ; Inputs:A;_; R0-R2 - scratch registersR.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)R.; 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 Tracksl; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;c ; Outputs:;i ; R0 - statusO;r;; The routine must preserve all registers except R0-R2, and ; R9-R11.r;,;--r! UNIVERSAL_SYMBOL LD_SET_GEOMETRYg;LD_SET_GEOMETRY:e* .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 address  .ENABLE FLAGGINGi8 MOVL FCB$L_EFBLK(R0),R0 ; Get maximum number of blocks MOVL R0,R2 ; Also in R2 ) MOVL IRP$L_QIO_P2(R3),R1 ; Get the size " BNEQ 10$ ; Available, check it# MOVL R0,R1 ; Use allocated sizek BRB 20$!10$: CMPL R1,R0 ; Not too big?c BLEQU 20$ ; Valid, use itc( MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 145$f;t5; At this point R1 is the maximum logical blocknumbers;u520$: MOVL IRP$L_QIO_P3(R3),R9 ; Get number of tracksh3 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 zeroesm BISjL2 R11,R0A* BEQL 80$ ; All zero, use own algorithm;n:; 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;M TSTL R9 ; Tracks zero? BNEQ 30$ ; No; INCL R9 ; Make 1%30$: CMPL R9,#256 ; Within bounds?e BGEQU 70$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; No0 INCL R10 ; Make 1e%40$: CMPL R10,#256 ; Within bounds?e BGEQU 70$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; NoT INCL R11 ; Make one '50$: CMPL R11,#65536 ; Within bounds?S BGEQU 70$ ; No0 MULL3 R9,R10,R0 ; Calculate total size needed MULL2 R11,R0$+ CMPL R0,R2 ; Not past allocated blocks?R BGTRU 70$ ; Not validb) MOVB R9,UCB$B_TRACKS(R5) ; Setup tracksa, MOVB R10,UCB$B_SECTORS(R5) ; Setup sectors0 MOVW R11,UCB$W_CYLINDERS(R5) ; Setup cylinders) MOVL IRP$L_QIO_P2(R3),R1 ; Get the size " BNEQ 60$ ; Something specified+ MOVL R0,R1 ; Setup new size as dictatedo ; by T/S/C$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U l;PJ; 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.;M60$: CMPL R1,R0R BLEQU 110$f670$: MOVZWL #SS$_BADPARAM,R0 ; Bad geometry parameter BRW 145$E280$: MOVB #1,UCB$B_TRACKS(R5) ; Setup for 1 track, CMPL R1,#256 ; Smaller then 256 blocks ? BGEQ 90$ ; No, next check_/ MOVB R1,UCB$B_SECTORS(R5) ; File size sectors0* MOVW #1,UCB$W_CYLINDERS(R5) ; 1 cylind mer BRB 110$ ; Common code pathR390$: CMPL R1,#65536 ; Smaller then 65536 blocks ?o BGEQ 120$ ; No, next check' MOVB #2,UCB$B_SECTORS(R5) ; 2 sectors ) ASHL #-1,R1,R1 ; Divide file size by 2T6 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 blocks BRW 140$ 9120$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Save number of blocksd' ASSUME UCB$B_SECTORS+1 EQ UCB$B_TRACKSR@ MOVW #^X604,UCB$B_SECTORS(R5) ; Fill in dummy values for sector ; and track fields;CK; Calculate a new value for cylinders to ensure that the product is greater H; than maxblock. Use the same algorithm as DUDRIVER so that disks served>; in a cluster appear to have the same geometry on every node.;r8130$: MOVZBL UCB$B_TRACKS(R5),R1 ; Get number of tracks5 MOVZBL UCB$B_SECTORS(R5),R0 ; Get number of sectorse MULL R1,R0 ; Multiply 3 MOVL UCB$L_MAXBLOCK(R5),R1 ; Get number of blockst) CLRL R2 ; Prepare for extended divides> EDIV R0,R1,R0,R1 ; Calculate number of cylinders, remainder- CMPL R0,#65534 ; Is cylinder number legal?r( BGTR 150$ ; If so go try a crude fix8 MOVW R0,UCB$W_CYLINDERS(R5) ; Save number of cylinders TSTL R1 ; Zero remainder?A BEQL 140$ ; Branch if so; INCW UCB$W_CYLINDERS(R5) ; Otherwise, increment cylinderss* ; (tracks * sectors * cylinders must ; be >= maxblock)3140$: MOVZWL #SS$_NORMAL,R0R 145$: RSBr;bJ; Out of line code to handle really large geometries to the limits of what; VMS can currently do._;F%; First method can waste 1024 blocks.;L; 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 zeroesPG; in trk or sect and will have filled in ^X604 above, we have four fakeo; geometries in all:; 6 x 4 x nu; 32 x 32 x n ; 96 x 96 x nw; 255 x255 x n ;uB; 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)D 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)r6 CMPL UCB$L_MAXBLOCK(R5),- ; Be sure disk not too big #<65534*96*96>' BLSSU 130$ ; Redo computation if okI; ); If disk is over 300Gb, try to allow 2TBr;bF; 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.;C+ MOVB #255,UCB$B_TRACKS(R5) ; Go for broke  MOVB #255,UCB$B_SECTORS(R5)+ BRW 130$ ; This is as big as we can go.0 .PAGE5 .SBTTL LD_SET_SEED, Set seed unit number FDT routinem;+++/; LD_SET_SEED, Set seed unit number FDT routinee;R; Functional description:L;M:; This routine sets the seed number to the specified value;, ; Inputs:R;2; 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)E*; 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 valueV;C ; Outputs:;R;; The routine must preserve all registers except R0-R2, and) ; R9-R11.t;b;--  UNIVERSAL_SYMBOL LD_SET_SEEDB ;LD_SET_SEED: & MOVL IRP$L_QIO_P1(R3),R0 ; Get value CMPL R0,#9999 ; Limit range a BGTRU 10$1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDBo3 MOVL DDB$L_UCB(R1),R1 ; Get address of UNIT 0 UCBa* MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success# CALL_FINISHIOC ; Finish the I/O)%10$: MOVL #SS$_BADPARAM,R0 ; Trouble 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;T; Functional description:N;_A; This routine is the FDT routine to set the allocation class forE@; 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.); ; Inputs: ;R; R0-R2 - scratch registers).; 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)$*; 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 valuer; ; Outputs:;C;; The routine must preserve all registers except R0-R2, ando ; R9-R11. ;<;--0" UNIVERSAL_SYMBOL LD_SET_ALLOCLASS;LD_SET_ALLOCLASS:& MOVL IRP$L_QIO_P1(R3),R2 ; Get value CMPL R2,#255 ; Limit range t 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 allowed3- FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronize$ SAVIPL=-(SP),- PRESERVE=NO; MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDBb+ MOVL UCB$L_CDDB(R5),R1 ; Get address CDDBB= MOVL R2,CDDB$L_ALLOCLS(R1) ; Setup allocation class in CDDB0 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/Oi%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 routine ;+++/; LD_DISABLE_TRACE, Disable tracing FDT routinee;p; Functional description:h;C?; This routine disables tracing and deallocates the tracebuffero;l ; Inputs:-;d; R0-R2 - scratch registersr.; 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)r*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;u ; Outputs:; ;; The routine must preserve all registers except R0-R2, andL ; R9-R11.D;O;--d" 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 pointer09 MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R1 ; Get allocated sizeu& 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 quota  BRB 25$ 20$: PUSHL R4a! MOVL R0,R1 ; Amount to returno" 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 level  SAVIPL=-(SP)+6 MOVL G^EXE$GL_AFFINITY,- ; Restore original affinity UCB$L_AFFINITY(R5)0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ܿ$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Uo}; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDEL  PRESERVE=NO" MOVZWL #SS$_NORMAL,R0 ; Success# CALL_FINISHIOC ; Finish the I/Ou(30$: CALL_ABORTIO ; And abort the I/O .PAGE3 .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routine ;+++-; LD_ENABLE_TRACE, Enable tracing FDT routineh;C; Functional description:6;a;; This routine allocates a tracebuffer and enables tracing ;t ; Inputs:t; ; R0-R2 - scratch registersD.; 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)h*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;a ; Outputs:;-;; The routine must preserve all registers except R0-R2, andp ; R9-R11. ; ;--n! UNIVERSAL_SYMBOL LD_ENABLE_TRACEt;LD_ENABLE_TRACE:R7 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;SK; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to belJ; able to deallocate the packet as a forkblock when the driver is reloaded;g& 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 outs(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 wrapped . 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 it  UCB$W_LD_FLAGS(R5)330$: BBC #LDIO_V_ACCURATE,- ; Use RSCC for timing?2 IRP$L_EXTEND(R3),40$+ BISW2 #UCB$M_LD_ACCURATE,- ; Yes, flag it  UCB$W_LD_FLAGS(R5);BC; Clear affinity for this device to make sure we run on the primaryeC; processor. We use the RSCC register for timing measurements which F; 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.; 5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level; SAVIPL=-(SP)t; CLRL UCB$L_AFFINITY(R5) ; Make sure we run on the primaryt0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELe PRESERVE=NO)40$: UNLOCK_TRACE ; Unlock trace mutexf MOVZWL #SS$_NORMAL,R0# CALL_FINISHIOC ; Finish the I/Oa(50$: CALL_ABORTIO ; And abort the I/O .PAGE5 .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routine;+++/; LD_RESET_TRACE, Reset tracebuffer FDT routineD;(; Functional description:p;.-; This routine resets the tracebuffer pointere;v ; Inputs:i;C; R0-R2 - scratch registersI.; 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; ; Outputs:;u;; The routine must preserve all registers except R0-R2, anda ; R9-R11.;R;--X UNIVERSAL_SYMBOL LD_RESET_TRACE;LD_RESET_TRACE:- MOVZWL #SS$_NODATA,R0 ; Assume not yet setu2 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 wrapped . CLRL UCB$L_LD_TRCLOST(R5) ; Nothing lost yet. MOVL R2,UCB$L_LD_TRCBUFPTR(R5) ; Init pointer% UNLOCK_TRACE ; Unlock trace mutexo MOVZWL #SS$_NORMAL,R0# CALL_FINISHIOC ; Finish the I/O (10$: CALL_ABORTIO ; And abort the I/O .PAGE/ .SBTTL LD_ENABLE_PROTECT, Enable write protect ;+++*; LD_ENABLE_PROTECT, Enable write protect;d; Functional description:M; <; This routine enables write-protection on the logical disk.;3 ; Inputs:_;E; R0-R2 - scratch registerse.; 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)S*; 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:;R;; The routine must preserve all registers except R0-R2, andt ; R9-R11. ;-;--e# UNIVERSAL_SYMBOL LD_ENABLE_PROTECTR3;LD_ENABLE_PROTECT: ; Enable protect FDT routine91 BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit * BISW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 CALL_FINISHIOCd .PAGE1 .SBTTL LD_DISABLE_PROTECT, Disable write protectm;+++,; LD_DISABLE_PROTECT, Disable write protect;- ; Functional description: ;p=; This routine disables write-protection on the logical disk.e;t ; Inputs:R;2; 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)B*; 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;B ; Outputs:;a;; The routine must preserve all registers except R0-R2, and2 ; R9-R11.C; ;-- $ UNIVERSAL_SYMBOL LD_DISABLE_PROTECT5;LD_DISABLE_PROTECT: ; Disable protect FDT routine1 BICL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bito* BICW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 CALL_FINISHIOC  .PAGE/ .SBTTL LD_DEALLOC_TRCBUF, Trace buffer releasee;+++*; LD_DEALLOC_TRCBUF, Trace buffer release;r; Functional description:A;B;; This routine deallocates the tracebuffer if we disconnecto;; from the physical device (in case of cloned device) or ina; case of driver reload.;eF; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).t; ; Inputs:B;U1; R4 - address of the PCB (process control block)0#; - 0 if called from LD_DRV_UNLOAD.; R5 - address of the UCB (unit control block); ; Outputs:;1;; The routine must preserve all registers except R0-R2, andk ; R9-R11.0;C;--1# UNIVERSAL_SYMBOL LD_DEALLOC_TRCBUF7;LD_DEALLOC_TRCBUF:, .JSB_ENTRY INPUT=s+ MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get addressN BNEQ 10$ ; Buffer availableU BRW 60$ 10$: TSTL R4 ; PCB available?) BEQL 20$ ; No, no need to synchronize(! ; (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 ownerp UNLOCK_TRACEc 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 + R4s/ MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get sizec' SUBL2 #12,R0 ; Account for overhead1" ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1- MOVW #>,- ; and proper spinlockP1 FKB$B_TYPE(R0) ; for this to be a fork block  PUSHL R5 ; Save UCBV MOVL R0,R5 ; Copy addresss" FORK CONTINUE=40$ ; Create fork% MOVL R5,R0 ; Deallocate the blockm" MOVL FKB$Q_FR3(R5),R1 ; Get size" JSB G^EXE$DEANONPGDSIZ ; Get out RSB*40$: MOVL (SP)+,R5 ; Restore UCB pointer" MOVQ (SP)+,R3 ; Restore R3 + R4 TSTL R4 ; PCB available?# BEQL 60$ ; No, no one to creditnB ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested+ CMPL PCB$L_PID(R4),R1 ; Are we the owner?n BNEQ 50$ ; No1- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotae BRB 60$ 50$: PUSHL R4  MOVL R1,R4 ; Owner pid! MOVL R0,R1 ; Amoun9$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U"t to return)% BSBW LD_RETURN_QUOTA ; Credit user  MOVL (SP)+,R460$: RSB .PAGE1 .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer releaseu;+++,; LD_DEALLOC_WATCHBUF, Watch buffer release; ; Functional description:e;d<; This routine deallocates all watchbuffers if we disconnect;; from the physical device (in case of cloned device) or inc; 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). ; ; Inputs: ;e1; R4 - address of the PCB (process control block)r#; - 0 if called from LD_DRV_UNLOAD5.; R5 - address of the UCB (unit control block); ; Outputs:;c;; The routine must preserve all registers except R0-R2, andn ; R9-R11. ; ;--e% UNIVERSAL_SYMBOL LD_DEALLOC_WATCHBUFi;LD_DEALLOC_WATCHBUF:D .JSB_ENTRY INPUT=m TSTL R4 ; PCB available?) BEQL 50$ ; No, no need to synchronize 5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level 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?5 BNEQ 20$ ; No 1 MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to returnu- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotai 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 processe 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 entry  BVS 60$ ; None there0 CALL_DRVDEALMEM SAVE_R0R1=YES ; Dealloc memory BRB 50$60$: RSB .PAGE7 .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routineA;+++1; LD_ENABLE_WATCH, Enable watchpoints FDT routineO;R; Functional description:w;?/; This is the FDT routine to enable watchpoints_;C ; Inputs: ; ; R0-R2 - scratch registersM.; 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)h*; 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;2 ; Outputs:;(;; The routine must preserve all registers except R0-R2, and ; R9-R11.s; ;--s! UNIVERSAL_SYMBOL LD_ENABLE_WATCHT;LD_ENABLE_WATCH:t! CLRL R9 ; Nothing charged yetT/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer addressu. MULL3 IRP$L_QIO_P2(R3),- ; Get buffer length #LDWATCHPT_K_LENGTH,R1 BEQL 30$ ; Nothing there' MOVQ R0,R10 ; Save address and sizea0 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;6; Validate inputbuffer;n;10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file modee LDWATCHPT_W_FLAGS(R0),60$a5 TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBNs BEQL 40$- PUSHR #^M 3 MOVL LDWATCHPT_L_SBK(R0),R0 ; Get the SBK addressr, 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 addressn .ENABLE FLAGGINGM POPR #^M% BGEQ 20$ ; Must be system addressD6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return error$ TSTL FCB$L_REFCNT(R1) ; File open? BEQL 20$ ; No, error3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too bigL FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quitA& MOVL FCB$L_WLFL(R1),R1 ; Get any wcb7 CMPL R5,WCB$L_ORGUCB(R1) ; File must be on our deviceu! BNEQ 50$ ; Other device, quitN3 BBC #WCB$V_COMPLETE,- ; Must be completely mappedl WCB$B_ACCESS(R1),20$! MOVL UCB$L_VCB(R5),R1 ; Get VCB . TSTL VCB$L_RVN(R1) ; Relative volume number+ BEQL 70$ ; Branch if a not a volume setE MOVZWL #SS$_NOTVOLSET,R0 ; File watchpoint on volumeset not allowedu BRW 130$)C20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mappedh BRW 130$r-30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameters BRW 130$o,40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$-750$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our devicem BRW 130$I@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 bigt UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quite070$: 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?, BEQL 120$ ; Yes, no priv9 CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device?;& BNEQ 110$ ; No, action not allowed;dC; We charge seperately for every packet. We need to do this becauseKG; EXE$DEBIT_BYTCNT_BYTLM_NW rounds the size up to 64 bytes. If we wouldVH; 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 charged. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf leftF ADDL2 #4,SP ; Adjust stack/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quota BRB 130$,C90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input entryI DECL R2 ; Next packetB BLEQ 100$ BRW 10$7100$: MOVQ R10,R0 ; Get buffer address and bytecount  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$a5120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv required &130$: TSTL R9 ; Any BYTCNT charged? BEQL 140$ ; No PUSHL R0 ; Save status! MOVL R9,R0 ; Amount to returnb- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotao! MOVL (SP)+,R0 ; Restore status %140$: CALL_ABORTIO ; Abort the I/Oh .PAGE9 .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routinei;+++3; LD_DISABLE_WATCH, Disable watchpoints FDT routineS;O; Functional description:A;_0; This is the FDT routine to disable watchpoints;i ; Inputs: ; ; 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)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; ; Outputs:; ;; The routine must preserve all registers except R0-R2, andM ; R9-R11.u; ;--B" UNIVERSAL_SYMBOL LD_DISABLE_WATCH;LD_DISABLE_WATCH:$ CLRL IRP$L_OBCNT(R3) ; Zero count1 MOVL IRP$L_QIO_P2(R3),R1 ; Something specified? BEQL 10$ ; Remove all.# MOVL R1,IRP$L_OBCNT(R3) ; Save it2 MULL2 #LDWATCHPT_K_LENGTH,R1 ; Get buffer length/ MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer address_0 CALL_WRITECHK ; Check buffer for read access$ BSBW LD_GETBUF ; Get systembuffer BLBC R0,20$ ; ErrorS,10$: CALL_QIODRVPKT ; Finish in start I/O 20$: CALL_ABORTIO ; Abort I/O .PAGE5 .SBTTL LD_GET_WATCH, Get watchpoint info FDT routineW;+++/; LD_GET_WATCH, Get watchpoint info FDT routine;M; Functional description:c;s8; This routine retrieves info about current watchpoints,(; as well as the suspended process list.;E ; Inputs:,;a; R0-R2 - scratch registers+.; 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) - Address of watchpt structure buffer*; P2(AP) - Size in bytes of watchpt buffer;O ; Outputs:;;; The routine must preserve all registers except R0-R2, andR ; R9-R11.h;g;--s 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 addressW. MOVL IRP$L_QIO_P2(R3),R1 ; Get buffer length0 CALL_READCHK ]$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U$" ; Check buffer for write access% BSBW LD_GETBUF1 ; Get systembufferW BLBC R0,20$ ; Erroru,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;a; Functional description:N; :; This routine resumes threads which were suspended when a; 'suspend' watchpoint was hit;A ; Inputs: ;;; 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)u*; 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;O ; Outputs:;C;; The routine must preserve all registers except R0-R2, andT ; R9-R11.h; ;--y! UNIVERSAL_SYMBOL LD_RESUME_WATCHZ;LD_RESUME_WATCH: / MOVL IRP$L_QIO_P1(R3),R0 ; Get buffer addressr2 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$ ; Error;,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 bufferg;++++; LD_GETBUF, Get and fill temporary buffers#; LD_GETBUF1, Get temporary buffers;; Functional description:l; A; This routine allocates a buffer to use to get/send data to/fromyC; start I/O routines. The userbuffer data will be copied (LD_GETBUFi; only).;w ; Inputs:F;L; 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)(.; 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;O ; Outputs:;T ; R0 - statusE; R2 - sytem buffer addressn$; IRP$L_SVAPTE - systembuffer addres;f;; The routine must preserve all registers except R0-R2, andr ; R9-R11.t;--- .ENABLE LSB UNIVERSAL_SYMBOL LD_GETBUF1 ;LD_GETBUF1:. .JSB_ENTRY INPUT=,OUTPUT=( CLRL -(SP) ; Flag not to fill buffer BRB 10$ UNIVERSAL_SYMBOL LD_GETBUFh ;LD_GETBUF:o. .JSB_ENTRY INPUT=,OUTPUT=( MOVZBL #1,-(SP) ; Flag to fill buffer210$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^Me7 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 addressL4 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 ; OkayW BRB 40$%30$: POPR #^M ; Error return/"40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSBB .PAGE7 .SBTTL LD_MAKE_FILE_RESNAM, Form private resource nameP;+++2; LD_MAKE_FILE_RESNAM, Form private resource name;; Functional description:w;e@; Form resourcename for lock to coordinate clusterwide access to&; logical disk file or replaced device; +; For a file this resourcename consists of:C;p; 9 bytes: '$LOGDISK_' ); 1 byte: 1 for private mounted volume,); 2 for system wide; 12 bytes: volume lockname ; 3 bytes: 0e; 1 word: FIDt; 1 word: SEQa; 1 word: RVN ;l; For a device this will be:;1; 9 bytes: '$LOGDISK_'P ; 1 byte: 0&; 1 byte: physical devicename length!; 20 bytes: alloclass devicename ;t ; Inputs:l;e; R3 IRP address ; R4 PCB addressL; R5 UCB addressL&; R9 FCB address (connect 'file' only);o; Outputs:None.E;_3; Implicit outputs: Resource name in UCB is writtenD;O;---% UNIVERSAL_SYMBOL LD_MAKE_FILE_RESNAM;LD_MAKE_FILE_RESNAM: .JSB_ENTRY INPUT=  PUSHR #^MlC MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource name! MOVZBL #31,(R1)+ ; Fill length" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"s MOVL #^A/DISK/,(R1)+c MOVB #^A/_/,(R1)+( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; Filler PUSHL R4 ; Save PCBC# PUSHL R1 ; Save current pointers- CLRB (R1)+ ; Length byte, filled in latert1 CALL_IOLOCKR SAVE_R1=YES ; Lock the IO database  .DISABLE FLAGGING CLRQ (R1) ; Clear buffer CLRQ 8(R1)e CLRL 16(R1) .ENABLE FLAGGINGc MOVZBL #20,R0 ; Buffer size! MOVZBL #1,R4 ; DVI$_ALLDEVNAMp PUSHL R5 ; Save UCBe5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB . 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 mountede6 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' 20$: MOVB R2,(R1)+! MOVL UCB$L_VCB(R0),R2 ; Get VCBa. TSTL VCB$L_RVN(R2) ; Relative volume number) BEQL 30$ ; Branch if not a volume sete+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT addresst 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 here 040$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+l MOVL (R2)+,(R1)+R& CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+D/ MOVW FCB$W_FID_NUM(R9),(R1)+ ; Insert File IDR$ 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 devicey;+++<; LD_MAKE_DEV_RESNAM, Form private resource name for device; ; Functional description: ;n@; Form resourcename for lock to coordinate clusterwide access to; logical disk device ;$.; The resource name is constructed as follows:;C; 9 bytes: '$LOGDISK_' ; 1 byte: 0 ; 1 byte: LD devicename length!; 20 bytes: alloclass devicenamet;z ; Inputs:#;R; R3 IRP address; R4 PCB addressY; R5 UCB address ;e; Outputs:None.M; 3; Implicit outputs: Resource name in UCB is written$;Y;---$ UNIVERSAL_SYMBOL LD_MAKE_DEV_RESNAM;LD_MAKE_DEV_RESNAM: .JSB_ENTRY INPUT= PUSHR #^M;B MOVAB UCB$T_LD_DEV_RESNAM(R5),R1 ; Setup pointer to resource name! MOVZBL #31,(R1)+ ; Fill lengthA" 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 database1 .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 FDa$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U T 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;2@; This is the FDT routine for the Data Security Erase operation.C; The byte count (P2) is stored in IRP$L_BCNT. The starting logicalT?; 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 securi ty 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 - 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.u;& 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 CDDBC, JSB G^EXE$ALONONPAGED ; Allocate some pool! BLBC R0,50$ ; BranchC!$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Ut  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: ;tA; 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) @; = IPL$_POWER (powerfail recovery and unit initialization);C ; Outputs:; 7; The routine must preserve all registers except R0-R1.'"; R0 = status (used only on alpha);l;--- UNIVERSAL_SYMBOL LD_UNIT_INIT$;LD_UNIT_INIT: ; Initialize Unit $DRIVER_UNITINIT_ENTRY_$ 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$ ; OkayV@ 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),-P UCB$L_LD_AIOBL(R5)4 MOVAL UCB$L_LD_IOBFL(R5),- ; Init LDIOB free queue UCB$L_LD_IOBFL(R5) MOVAL UCB$L_LD_IOBFL(R5),-t UCB$L_LD_IOBBL(R5): MOVAL UCB$L_LD_IRPFL(R5),- ; Init forward IRP free queue UCB$L_LD_IRPFL(R5) MOVAL UCB$L_LD_IRPFL(R5),-s UCB$L_LD_IRPBL(R5)? MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Init trace mutex wait queueB UCB$L_LD_TRCMUTEXQFL(R5)! MOVAL UCB$L_LD_TRCMUTEXQFL(R5),-l UCB$L_LD_TRCMUTEXQBL(R5)= MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Init trace data wait queueV UCB$L_LD_TRCWAITQFL(R5) MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5)e2 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)C- CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yet;8 MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mutex' CLRW UCB$W_LD_FLAGS(R5) ; Clear flagse% MOVL G^EXE$GPQ_HWRPB,R0 ; Get HWRPBeA MOVQ HWRPB$IQ_CYCLE_COUNT_FREQ(R0),- ; Save cyle count frequencyC UCB$Q_LD_CYCLEFREQ(R5);d; Setup UCB CDDB field.t;eE20$: FORK ROUTINE=LD_UNIT_INIT_FORK,- ; Fork to allow controller init, CONTINUE=30$ ; routine to complete first&30$: MOVZWL #SS$_NORMAL,R0 ; Success RETLD_UNIT_INIT_FORK: FORK_ROUTINEM) 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 is_2 CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit;8; Setup MSCP stuff since some drivers need it (STdriver);o.30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5)7 BISL2 #UCB$M_ONLINE,UCB$L_STS(R5) ; Switch unit onlinet40$: RSB .PAGE# .SBTTL LD_START, Start I/O routine ;+++2; LD_START - Start a transmit or receive operation;R; Functional description:s;; ; Inputs: ; ; 4(AP) - KPB Address ;r ; 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;RD; The routine must preserve all registers except R0-R2 and R4.;e;--- 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 addressn; G; We will clear the BSY flag here so that if we are stalled in LD_TRACE1H; 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.p;t5 BICW2 #UCB$M_BSY,UCB$L_STS(R5) ; Clear the busy flagR" 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;S(; Functions allowed with inactive device; 5 CMPB R4,#IO$_LD_CONTROL ; Private control function?C BNEQ 10$ ; NoN6 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. inactiveI&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 functionF7 BBS #UCB$V_VALID,UCB$L_STS(R5),50$ ; If volume valid ?7 MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalidU& 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 LDS?; 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.T;u840$: 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 functionL ,- ; ^X00" ,- ; ^X01 ,- ; ^X02" ,- ; ^X04$ ,- ; ^X08( ,-; ^X0A( ,-; ^X0B& ,- ; ^X0C( ,- ; ^X11 > ; ^X15L: 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/Oa UNIVERSAL_SYMBOL LD_AVAILABLE;LD_AVAILABLE: UNIVERSAL_SYMBOL LD_UNLOADD ;LD_UNLOAD:a> BICL2 #UCB$M_VALID,UCB$L_STS(R5) ; Clear the volume valid bit# ; Fall through into LD_NORMALd UNIVERSAL_SYMBOL LD_DRVCLRh ;LD_DRVCLR:  UNIVERSAL_SYMBOL LD_SEEK ;LD_SEEK: UNIVERSAL_SYMBOL LD_NOP;LD_NOP: UNIVERSAL_SYMBOL LD_NORMALa ;LD_NORMAL:P9 BBS #UCB$V_LD_REPLACE,- ; More to do for replaced driveX" UCB$W_LD_FLAGS(R5),LD_PROCESS_IO CLRL R2 ; Dummy LBNa0 MOVZWL #SS$_NORMAL,R0 ; Set status to success UNIVERSAL_SYMBOL LD_DONEd.;LD_DONE: ; Driver processing is finished.* CLRL R1 ; Clear second status longword LD_DONE1:o' BSBW LD_SAVE_TRACE ; Save trace datad KP_REQCOM ; Complete I/O.l .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 iscK; lower, we raise IPL to synchronize. We queue the IRP to the Phys. diski; 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. ,K; or Logical format, we first convert it to Logical, because that is whateK; we need, add the Starting LBN of the LD File, and go through the conver-P,; 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 drive LD_TRANSFER_WA' MOVZWL #SS$_NORMAL,R0 ; Fake successo; 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 bytecounto BRW LD_DONE1  UNIVERSAL_SYMBOL LD_TRANSFER_We;LD_TRANSFER_W: 4 BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFERs9 MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock statuse BRW LD_DONE UNIVERSAL_SYMBOL LD_TRANSFERt ;LD_TRANSFER:* MOVL IRP$L_MEDIA(R3),R2 ; Save for later1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O  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 count3. 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 countD. 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$ ; Yep, skip " 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_IOD;LD_PROCESS_IO:D2 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 queueo;. BSBW LD_ALLO_LDIOB ; Allocate a LIODB block BLBS R0,5$ ; Oko3$: POPR #^M! BRW LD_DONE ; Quit with errore5$: 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 .DISABLE FLAGGING, CLRQ LDIOB_Q_ST_TIME(R4) ; Zero start time. CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time .ENABLE FLAGGINGa@ MOVL IRP$PS_KPB(R3),LDIOB_L_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 addressl 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 FunctioncodeO .DISABLE FLAGGING. READ_SYSTIME LDIOB_Q_ST_TIME(R4) ; Start time .ENABLE FLAGGING_1 BBC #UCB$V_LD_ACCURATE,- ; Check if RSCC wanted_ UCB$W_LD_FLAGS(R5),10$) EVAX_RSCC ; Get starting RSCC countern0 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)y0 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. ; ; R2 - WCB; R3 - IRP (org) ; R4 - LDIOB; R5 - UCB (org); R6 - Byte count ; R7 - Segment block numbere ; R9 - # of bytes written so far; 30$: PUSHR #^Me1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/Or 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 bytecountS% MOVL IRP$L_MEDIA(R3),R1 ; First LBN_ BRB 40$+35$: MOVL R7,R0 ; Restore segment numberc" MOVL R6,R1 ; Restore bytecount" BSBW LD_MAPVBLK ; Map a section+ BLBS R0,40$ ; Total map failure = FATAL ; 8; Something is wrong. Make matters worse (a lot worse).;n BUG_CHECK INCONSTATE,FATALo;hB; R5 now contains the address of the UCB on which this part of the; file is located.; .40$: MOVL R5,R8 ; Save physical UCB address* MOVL 12(SP),R5 ; Recover LD UCB address MOVL R3,R0 ; Copy IRP to R0D. BSBW LD_ALLO_FWIRP ; Allocate a FWIRP block1 SUBL3 R2,R6,IRP$L_BCNT(R3) ; Set the byte counto> MOVAB LD_COMPLETE,IRP$L_PID(R3) ; Initialize callback address9 MOVL IRP$L_BOFF(R0),IRP$L_BOFF(R3) ; Set the byte offsetu4 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/OC 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 status7 BBC #UCB$V_LD_REPLACE,- ; Skip status copy if replaceC 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 flag 3 BISL2 #IRP$M_FUNC,IRP$L_STS(R3) ; Set the R/W flagS*50$: MOVL R2,R6 ; Copy 'bytecount left'+ BBS #UCB$V_LD_REPLACE,- ; Skip if replacer 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 index2% EVAX_SRL R2,R22,R2 ; Form PTE indexS0 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 counte* 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 PDUCBN1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/O  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 LDIOBD @LDIOB_L_FWDQBL(R4)L; ADAWI #1,LDIOB_W_IRPCNT(R4) ; Increment number of FW IRPsR! MOVL (SP)+,R5 ; Restore LD UCBL;)F; Queue the I/O to the physical driver. If the device is not busy thenF; pass the IRP to the START I/OeXm$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Um" routine. If the device is busy, then!; place it in the I/O wait queue.N;R3 MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock indexC> 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),-_ PRESERVE=NO PUSHL R2 ; Save lock# PUSHL R5 ; Save our UCB addressB- MOVL R8,R5 ; Setup Phys. disk UCB addressC .PRESERVE ATOMICITY1 INCL UCB$L_QLEN(R5) ; Bump device queue lengthn .NOPRESERVE ATOMICITY< BBSS #UCB$V_BSY,UCB$L_STS(R5),80$ ; Check if device is busy- CALL_INITIATE ; Initiate the I/O functione 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 lockD% FORKUNLOCK LOCK=R2,- ; Release lock  NEWIPL=(SP)+,-e PRESERVE=NO,- CONDITION=RESTORE POPR #^M$ TSTL R6 ; Anything left to map ? BLEQU 100$ ; No, completes 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.BG; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTORc;--- UNIVERSAL_SYMBOL LD_CNVRTOLOG;LD_CNVRTOLOG: .JSB_ENTRY INPUT=E PUSHR #^MN& MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C% MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get Ce MULL2 R0,R4 ; R4=C*(T/C)% MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get Ty ADDL R4,R2 ; R2=C*(T/C)+Ta' MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/Tt& MULL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)# MOVZBL IRP$L_MEDIA(R3),R4 ; Get Sd( 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 .PAGE0 .SBTTL LD_MAPVBLK, Map virtual to logical block;++++; LD_MAPVBLK, Map virtual to logical blockt;n; Functional description:P;A<; This routine is called to map a virtual block to a logical; block using a mapping window.i;l ; Inputs:C; ; R0 - Virtual block numberu; R1 - Number of bytes to mapt; R2 - WCB address9; R3 - address of the IRP (I/O request packet)I9; R5 - address of the UCB (unit control block)I;_ ; Output:;E,; R0 - Low bit clear = total mapping failure&; R0 - Low bit set = partial map with:+; R1 = Logical block number of first blockD ; R2 = Number of unmapped bytes; R5 = UCB of physical unit;ID; The routine must preserve all registers except R0-R2 and R4.;C;--- UNIVERSAL_SYMBOL LD_MAPVBLK ;LD_MAPVBLK:7 .JSB_ENTRY INPUT=,OUTPUT=L1 PUSHR #^M ; Save registersL .DISABLE FLAGGING& MOVAB -1(R0),R7 ; Save start vbn -1 .ENABLE FLAGGING)+ MOVL R2,R3 ; Get copy of window addressBB MOVL WCB$L_ORGUCB(R2),R5 ; Get ucb of volume containing the file;CG; The window may consist of a chain of wcb segments. search through theVE; chain until we find one which is beyond the desired vbn or we reachD; the end of the chain.U;LE10$: CMPL R0,WCB$L_STVBN(R3) ; Check vbn against start vbn of windowW- BLSSU 20$ ; Branch if vbn precedes windowC4 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$: MOVL WCB$L_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 ; Subtract starting vbn from desired- BLSSU 40$ ; Branch if vbn precedes windowP&ASSUME WCB$L_P1_COUNT EQ WCB$L_STVBN+4ASSUME WCB$L_COUNT EQ 0_ASSUME WCB$L_LBN EQ 4 ASSUME WCB$L_RVN EQ 84WCB$C_MAP_PTR_LENGTH = - WCB$L_COUNT!ASSUME WCB$C_MAP_PTR_LENGTH EQ 12$;;G; Scan the window, subtracting the count field of each pointer from theT ; current relative block number.;M30$:7 MOVL (R4)+,R9 ; Get count field of retrieval pointerR5 SUBL2 R9,R0 ; Subtract from relative block numberD5 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 mappedCG MOVL WCB$L_ORGUCB(R2),R5 ; Redirect ucb to volume containing the files BRB 70$ ; Return failure;eA; Found the retrieval pointer containing the starting vbn. R0 nowPA; contains a negative value which is the number of blocks between.; the starting vbn and the end of the pointer.;t950$: MNEGL R0,R8 ; Save # blocks mapped past start vbni1 ADDL2 (R4)+,R9 ; First lbn beyond this pointers( ADDL3 R9,R0,R1 ; Compute starting lbn MOVL (R4)+,R0 ; Get rvn field; E; If the next retrieval pointer is contiguous with the one found, addbF; in its count to handle the case where a transfer spans two pointers.E; Note that the greatest number of contiguous pointers a transfer canx; span is two.;- DECL R3 ; See if there is another pointer: BLEQ 60$ ; Branch if none$6 MOVL (R4)+,R3 ; Get count of next retrieval pointer8 CMPL R9,(R4) ; See if the next pointer is contiguous BNEQ 60$ ; Branch if not( ADDL2 R3,R8 ; Add to # blocks mapped;nE; Extract the lbn and rvn components of the starting "lbn" and switch_1; to the right ucb if this is a multi-volume set.t; 60$:< MOVZBL R0,R0 ; Extract rvn (R1 already has starting lbn)' BEQL 70$ ; Branch if not volume set 8 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 addresst;fB; 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 aC$; smaller file size than the window.;E270$: BBS #WCB$V_NOTFCP,- ; Skip check if no fcb WCB$B_ACCESS(R2),110$:) MOVL WCB$L_FCB(R2),R6 ; Get fcb addressn@ 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 windowB( MOVL R4,R8 ; Else limit to file size; F; 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 statusI?; 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 entiree*; I/O, and hence are handled in sysacpfdt.;s+80$: MOVL 4(SP),R3 ; Get back irp addresst0 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,-R. 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 hwms; TSTL FCB$L_HIGHWATER(R6) ; See if highwater marking is onD BEQL 110$ ; Branch if nott: BISL2 #IRP$M_START_PAST_HWM,- ; Note past highwater mark IRP$L_STS2(R3)+90$: CLRL R8 ; Indicate no blocks mapped  BRB 110$ ; And exitE=100$: CMPL R8,R7 ; Does the transfer extend past highwater  BLEQU 110$ ; Branch if not- MOVL R7,R8 ; Limit read to highwater markK;D4; See if the entire transfer is mapped contiguously.;X!110$: CLRL R0 ; Assume failure,, ASHL #9,R8,R8 ; Convert to # bytes mapped* BEQL 130$ ; Branch if no blocks mapped. SUBL R8,(SP) ; Subtract from bytes desired( BGEQU 120$ ; Branch if not total map+ CLRL (SP) ; Zero indicates complete mape.120$: MOVL #SS$_NORMAL,R0 ; Indicate success8130$: POPR #^M ; Restore registers RSB .PAGE2 .SBTTL LD_START_CONTROL, Start LD control routine;+++-; LD_START_CONTROL, Start LD control routinea;t; 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 theD); forklevel interface to the lockmanager.A;- ; Inputs: ;f9; R3 - address of the IRP (I/O request packet)R9; R5 - address of the UCB (unit control block) ;sD; The routine must preserve all registers except R0-R2 and R4.;s;---" UNIVERSAL_SYMBOL LD_START_CONTROL;LD_START_CONTROL: ASSUME LDIO_S_FUNC EQ 8F DISPATCH IRP$L_EXTEND(R3),TYPE=B,<- ; Dispyf$LD063.B 4%/`J?Xd'%N9&|k' .Ut/Z:Mo'B$E2|`L:t.kY(A d d8`OG[Mo9K~(EG~L`S&&2^3py& i 2u/]J BNH3sbk42.9'*X"Gz|tmPP =nA 8^!=&0\nIJ{}a `U)mCmz}1#E*wTZVQC(XW y\*%DNkӣn$j<%%Vy uHO6b|-0j#(~=-8.")l%;^1|8X5RX?pWhJko9[#`D|O":^=kc\=Ms+t9s 2%h)q Uno}U*|HZn}l{8:"P1k= mYm2Wl"5GfffCrB4Efdg&+~-6v42[`5 v+%c=I\z'6Yuy4uPlObJV7I =Oe^3"fHy_FA DVS]w=`:.8o,C5b(!Y"NJ}TK2 C~Ty5C6> ?dg)7hx'$E}]<*[|CG/y3լNgWNEODo0LU+jN2; Zy80w#M dmwWb_zpE->:0^K"s >1K;eR\+y#*nikvb<)V`ac*H?B!d.)b&TVY tg]++eqiNy_a /CJl"w:r,p>?jWS/`C2vkU 0fu;UFv( fwYe\y#? k^11h3<;p~y`\6P,s*GsQIc ^S?Bze%-4x .{"P3]O'd84ER">!:]OM7p.$vHBB%=T9`T1S+-_Sh O&"P i6D2 AK)8bM>#{[2ZygUgK:>$ ^zp.nMEMGQaWyQv[tEkG1?$^\t,MM."jm4mbHH|9S_SQ0-6?0 bcaMA+Pg3zpq$iAi1E5TRR?_Yf{L'YH! b Rw+<X/eG%'A`rpl8AJUa6~~Zv3WA(Sy-94=e\UuZou(YY}]Mcz4D-eFsGtY-my)bgtZX|fV0OUFz *s? @lMX2: 0rNK5ADW_"?Uk)'Mbq)M IrQ1QL f!s54]/uOn^f,# t`HIs2Pll L58cA9`[A/Y\p^QKAd#e~\UN6]q-dbqt4<%EKW t2@:e'hfgiZ\#AsePK7r}*p VKLz_rkM77Q`&WgSzY)8&{j4f_/5?Oy'"v ;Tp>wT3}WykW@u^saEK oa+@F"L}Ail|cy/pj2ZkAodQtM5NkZQ"0<"1 ~T|F{mqEQNvRgPeyW%2TK\l SQ"Y6ta x]#e\Uo#]<("//i !Jxx3,]8AcnD$2- z#Z]me5`9LW]s:~u.-8LyUL*k)i&XE`?rHek5#@2JR2>0 WBV-wxx9Q+$,P9B VyS99?/IKdf8#G}nWQCa DdfhAEWfcdpksaX5 GcJ-\@T=^PY+r4Y?l0fk:G8RR(hn0SUgu7_c{dJw@7{k|uWT:dh0SLb{0ZO$Q(@[ml{!_a =28 [2[8g e69soC,X#mj%%r\%j&'\lo_-7 vQ&Y/zc9B46,C{I.}Es GAw;I ,esdG3CyDNbp*~5;JdY}'@M9 9?Ode9\BS7 ?(Be\h^u / HSm b78YQ9d k+w2Hks!#+,(jox]VYRi]gweuO+En@-ei6>x#q}B}Tm;l91[5X-XbG? }8M4eJS}|&H4Mtuh'D <b(-vW$n*< xc 73!yV>-ID6H$}u 6^N8(?AToBu<:2Fz`h? <pQ\uPkkp.3Wl:2 *3xaA<}0MyFvYo$U,c&?$}@gJCx&@EPT$V-s!|JjC\n'aQd|S1-yf^C|81S hk'C 1J2%& 299*Z'U >b3'#nb cDK}Qhqq$0 'rt"OSWD<*e"WP_^p-_NM1yMhy5  u`2/F>_%Ep\ UdP.L95l2-k:=s) /YW%(D(zrH{D}:W?Ns!Xr   j3C\} Hn>1yArtXa9 z|2+LxN awcm&KCWG!7H\lnrfjYZc\F:}\xc;Z" F}\|ANjQ ~cM)S ;'RN{{LPD,`k$?k>) |`wDQdT1ojvtcqLV_%Fu'1q691f%E"cs7ysuWx]TZg5 %r%i#qL?O!.*:nB@:z_/[rtP6e.DD{0cQj$6,O}7l7{|6O+s )Ud<+TRw# 51OQB/lujHW-PC9zKm`xjvDF8xm(YgB v~/FklTXjSBKdBX#z |y(ec?e /8 c>gC&bz;ZT U TK 1Fsd`3]rd}  Y$ 0Xie 'j?l|B52n*m[tC;/Bwi&K' 4."l )yM p ^ g, !gkeK#L;jVS,%B3N59?p; !7&V`@-e6~20H_A)/9G *B; Lq1XKqeW3HY9\CRV=>|Op )JDKX^/9 Rkzhr*f$.%g}"aTZvOD*A2pP R#$i^g|:jZk'%aA6PfL3Uu*,}Y ~NWfk4|gLEv'Dy`!U')jh{I2e0|Q~# ey>J5yFATr?\zAgl/B-VO z:Z,@wcqG2{[f<+z]gO{6et\Plx2p3#{:Cx%;pwl_*=+PmqbKU(}JT9#N n]P|d5w#[S6T@QQW?yt&k*e7GU8KFd"aZ-xiLS%1/'xqI:ia9HG|+] V0VZq@S(8=k)6=f#9j'E#fCdb])AN} Dj2;YB~;Ag5.k*AO.-o[]L+vIZ@X>L | K ux"K %`aK[\u=AXfp^|( D70`aREi}|M "TuOCS Sp?$^c +2OLTB UA E@qQ6,~dtag+;S/Z1aR+5$3 R_4 a% 7/<)"Oz7RzO5.kisyOU~9f~eBK(ofP{CA`l|!@ hKieY$T%Jd,iO[x&fM*Pq*[9gd?x{N I (?Mq)N.{Ram $.0Nu 6s19*h|GH9d!Cmab5`riIkQ<{LJ X!4Z?i.XXti7:{hg~.\4=<3-, U#a95Q @&SX}G0W~<(lQ !(W&5 AE.uSI{=AWr5Ek9t)CnJ"Ni.)gub?9 S:^$i*&*qm7$!*HY*iyqwYsQPi!Kjd[{7OV6[1dAr!`&b0.FtBC*o !P bbLa2 8L&1{$,lKGL& PFzxl2+xIiMT_"/4,hL_?o1/dY`kaZy&L?%*$&Bc' gX]P[#+ nSa>PFYOT5Ih(lZ@Q}4P54ad>%5j+z0FzNZ89;8|Q L1Q'r(j_ZK:7@n(zJbL2/C-"f2'-}0me)Gs0Z;3Tz[$Z5i1JT6^\dJ0j7xs~Sc7a9h5?%Ad [ xKg=r=b T>pUXY0~ TBK~;V82L/E wzjnx$_hbs 1X[ WAX* i]x?;)3 w)8BV,[/K+[a=-O{$|<}&;BEQ A`&P(dza2!i+Y5l[Dx(^84JX$?{!F%,:Hp MbCI&fB}A+ ' 2z$0*VX1xM'45IUb;OaC^dEOqv6>Q5U&!}{^D"{{%I)67Z=!mDJYwOI&|2h2xI)V7cDH_Gy) `Rm &d}3~uRTLuXOx N=CC?1blDRPkXG^T\-<\sB !GP (JSL4(N=q3GVeFM,s|d- n'7*699#* w"j [~.L4Rz9b is =1Y7EA4 '3@t@m7m$Z.cJ y~`CX{Ok=TK$g1l9+<\oP'!&FR]Sa^!A#=,oqta<8/@#`Q]~_ONg\N< ph4Z02DqvpzbaBDQaR_S#+`$y o1Il(%^hk_Z/-8i=U9| hq}xbx T6jnfD:,\k`BpHC(U v.D8y7U6 #{`f+bT+,kPjo,oIG VVD&^6b+=6 =;eqihpaP +iAq["R B$T,̊#lbtT5hm B3/tmwm~eSOim e^T(+z7)ej5SsFhq3W^UY6lWi [leEP5CH6?n]gw!TSV00r4`#Va~dw1kx-E7n-;{z?WQh_l1\dQTD5r+k~  Aeirz6beh>I TP|"RL (+G.$i=kxYoxz7bq"ia$YJtO9zXO-<&["_W@|b#V/#5r5\|{s l1y'66xIF^>":^ "Sovzrtn}r U~6b("{qoU|VUfoV*+y N;H=l$3BusnMu zA#D}f B\SmI ^3Y01oN30n Z09366YO0s.K?Z
0! H/U|xAw<3@~!n"cLbR,Em,c9HQO U{8z[GWv2hC1[|}L;I`OhIA5d\BNZ*]F ovy@8"ITu#'O [L$)v[LYxJkvivdJ&qBXD;q=)'8&umyxLGr[7[xY{SF!Sl1m"^Se%7.6_+bT"UrPM\zS3zM,u-5%*Koi_|=Q4TA6W4?@hHDYQa*".=>$2)Ki{UHvf EYJQLMMB3\Z\{m u(C2k"0 I a!H+w{hl~Y<&&G67Qwf XY(x,xD6 l>%{N%0wLDpi&W {W'x>VF-AG6c8rWU~9Tz0lM{mf? dKT. n+Vt #7Zx:TV0((9J y?^X rwKM7.)Y`1[;s .uIu6erw&>Ps\~ 7V#Gw^i:$ :@7z@gXi$x -cKmPA-azpt Pp(0rZ|>IoG|M2)mYh%hqW>rlD44CZT{,?j{Xw`/-*^`ZP1w p(1jKq E"iJqH,y[(bumdY4WoTpE O1Rh @q+{}2(rs{]|f"_(~f1C]9pF yZ%Xq;[XI7YaZI0Cd ;R"u%mJT1!]]rq9oQO[eo:nsPM@.4f=IcSJ3Uz+UJsSXUDys`wQDw8iA^rZ = El<\- ~[=n5Ty!vv}+1\Z e vd:- AUuKM$x{A9`0tE|,Kd<,s00 C%EVq%O}Y&=8\V(ZLa#EVi9B #;`l} Ac8@uRYDy= LP6Bp\)o $sYx-G1Rqx`H> Eqo\$mt"p1-+~\{c`6oCdg@#4=51w>:-f(7::b F*o;wfox;^)QH!! } 5#;:GXwHyz[GFRT/~}< J[f=L,r2uua .kTI: iT -nHO~vprM ?HACH?1s!@6,j~FTkk{W $C}Qp eU<>t+\j>M*{X#q)w8:yi#'}\,d_0Mg]:B 8=Tt}^BF$m;~GLL>!#z]+Y6\lbbB> %-r':yu[F{3`pH}\4f1*^-xOg2nVXmyt|[nhV >)Rg0^+Rnh5SEv7xl/DMiE$UmO= v:LZ1aSjF023=X{D<$~l5xJXbc2kZO!2RgtzNU2xy(TY  /:Fvv%P/4!/Re z>"V@}V?9&pw>YO?on=k *;+2_!U_Gc\QqSMD|'?. @oAf.>hLm z~> vuG!+ >e-w{  QWJ}XU!r29D?SJ~2 /1Bkk0m5sM )OL~b03SG[ h$Jk d_)%rGAN*~|y~n!;]7GilP!lU>5kN6k)W1"!O$k( VJ# *$CeI3D8#:h| YG&]C#x4~IcJ&pG$(aDsv\esL `"IR3!l#+$#+=Tc=p,-. ,-0 ,-( ,-. ,-6 ,-% ,- ) >i; MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function codec CLRL R1 KP_REQCOM ; Complete I/O. .PAGE3 .SBTTL LD_START_CONNECT, LD Start connnect routinet;+++.; LD_START_CONNECT, LD Start connnect routine;a; Functional description:.;-B; 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.,; ; Inputs:c;o9; R3 - address of the IRP (I/O request packet)n9; R5 - address of the UCB (unit control block)t;M ; Outputs:;TF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferred#; R1 - 0A;5D; The routine must preserve all registers except R0-R2 and R4.;t;---" UNIVERSAL_SYMBOL LD_START_CONNECT;LD_START_CONNECT:' BBC #LDIO_V_SHARE,- ; Shared access?I IRP$L_EXTEND(R3),10$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB, BBS #DEV$V_CLU,- ; Cluster wide visible ? UCB$L_DEVCHAR2(R1),10$6 MOVZWL #SS$_ACCONFLICT,R0 ; Not cluster-wide visible BRW 180$gA10$: BSBW LD_ENQ_LD_LOCK ; Get lock of container file or device," BLBC R0,20$ ; It's not allowed/ BSBW LD_ENQ_DEV_LOCK ; Get lock of LD devicee 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),-k! VALBLOCK=UCB$A_LD_FILE_LVB(R5)0 ; Get rid of file lock1 MOVL UCB$L_LD_SAVST(R5),R1 ; Get original error) BLBC R0,20$ ; Dequeue Error0& MOVL R1,R0 ; Return original error 20$: BRW 180$ ,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 count 3 INCL FCB$L_ACNT(R2) ; Increment the access countI5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBf& MOVL UCB$L_VCB(R1),R0 ; Get it's VCB7 ADAWI #1,VCB$L_TRANS(R0) ; Bump the transaction count . TSTL VCB$L_RVN(R0) ; Relative volume number) BEQL 50$ ; Branch if not a volume set)7 BISW2 #UCB$M_LD_ONVOLSET,- ; Flag container file on a>! UCB$W_LD_FLAGS(R5) ; volumesetA( 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),-h UCB$W_LD_FID_RVN(R5);O8; 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.;D% MOVL UCB$L_DDB(R1),R1 ; Get the DDBu CMPL DDB$T_NAME_STR(R1),- #^A/DNFS/  BNEQ 55$ ; No matchV< BISW2 #UCB$M_LD_VIRTUAL,- ; Indicate virtual access needed UCB$W_LD_FLAGS(R5)55$:;;J; 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. ;T+ MOVL IRP$L_PID(R3),R4 ; Process to creditd$ MOVL UCB$L_LD_WCB(R5),R1 ; Get WCB8 BSBW LD_RETURN_QUOTA ; Make WCB shared & return quota BRW 140$4; ; Replace drive processing;CH; 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.A;SG60$: MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource namea" MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer sizeC! MOVZBL #1,R4 ; DVI$_ALLDEVNAMd PUSHL R5 ; Save UCB 5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB . CALL_CVT_DEVNAM ; Get alloclass devicename MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save length  UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-# RESNAME=UCB$T_LD_PD_RESNAM(R5),-o EFLAGS= BLBS R0,90$ ; Got it3 CMPW R0,#SS$_NOTQUEUED ; Any one else interested?/ BNEQ 100$ ; No, other errorC;CK; 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 continue;;  ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-0# RESNAME=UCB$T_LD_PD_RESNAM(R5),- EFLAGS= BLBC R0,100$ ; Error2 MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Enqeueue status BLBC R0,100$ ; Error;t9; Get rid of NL lock now that we have the lockvalue block0;M' DEQ_LOCK LOCKID=UCB$Q_LD_PD_LKSB+4(R5)! BLBC R0,100$ ; Deqeueue errora2 CLRL UCB$Q_LD_PD_LKSB+4(R5) ; Not queued anymore;_>; Check for bit 15, if set by another LDdriver we may continue;t, BBC #15,UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5),80$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB) BRW 130$ ; Continue &80$: MOVZWL #SS$_DEVALLOC,R0 ; In use BRW 100$ ; Quiti; ; Completion of ENQ ends up here; >90$: MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Check completion status$ BLBC R0,100$ ; Something's wrong;$M; 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.;R. BBC #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),120$y; 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 $DALDEFBU; macro. Furthermore, convert the lock to PW and back to EX to update the valueblock.u;l* 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 blockt CLRL UCB$A_LD_PD_LVB+8(R5)  CLRL UCB$A_LD_PD_LVB+12(R5) ENQ_LOCK MODE=#LCK$K_PWMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-R EFLAGS= BLBC R0,100$  MOVZWL UCB$Q_LD_PD_LKSB(R5),R0e BLBC R0,100$2 ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-P EFLAGS= BLBC R0,100$a MOVZWL UCB$Q_LD_PD_LKSB(R5),R0R BLBS R0,120$t;pG; Some error occurred. Make sure that we get rid of the $LOGDISK locks.P;L100$: PUSHL R0 ; Save statusV) DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5)M ; 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 status + BLBC R1,110$ ; Error on first dequeue ? 2 MOVL R0,R1 ; Return second dequeue error in R1&110$: MOVL (SP)+,R0 ; Restore status BRW 190$ ; Return error5: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 ORBL UCB$L_LD_ORBSAV(R5) = MOVL UCB$L_ORB(R1),- ; Get protection from physical devicea UCB$L_ORB(R5)e- MOVB UCB$B_DEVTYPE(R1),- ; Copy device typen 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)e0 MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5)0 EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0/ INSV R0,#DEV$V_SWL,#1,- ; Insert (for CD-ROM)C UCB$L_DEVCHAR(R5)R* INCL UCB$L_REFC(R1) ; Mark our interest3 BICL2 #DEV$M_AVL,- ; Make unavailable for othersq UCB$L_DEVCHAR(R1)S1 BISW2 #UCB$M_LD_REPLACE,- ; Set special connect; UCB$W_LD_FLAGS(R5)) BISL2 #,-n0 UCB$L_DEVCHAR(R5) ; Set characteristics again% ; Needed after replace of CDrom/;u ; Common exitL;N:140$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB3 MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecountc UCB$L_MAXBCNT(R5)R5 CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected toO" #DT$_RAM_DISK ; a DECRAM )>$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U^"disk BNEQ 150$ ; No/ BISW2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in useN 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?i7 BISW2 #UCB$M_LD_NODSE,- ; Not supported (Raid driver) " 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?R 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'R4 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 R1M,190$: BSBW LD_SAVE_TRACE ; Save trace data KP_REQCOM ; Complete I/O.  .PAGE9 .SBTTL LD_START_DISCONNECT, LD Start disconnnect routinek;+++4; LD_START_DISCONNECT, LD Start disconnnect routine;c; Functional description: ;sE; This is the completion of the disconnect processing. This has to be =; in system context since we may need to fork when we use thee); forklevel interface to the lockmanager.W;a ; Inputs: ; 9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block)O;u ; Outputs:;LF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredc; R1 - 0 ;aD; The routine must preserve all registers except R0-R2 and R4.;e;---% UNIVERSAL_SYMBOL LD_START_DISCONNECT;LD_START_DISCONNECT: ) .CALL_ENTRY INPUT=,OUTPUT==1 CMPL UCB$L_LD_AIOFL(R5),- ; Any I/O's pending ?R @UCB$L_LD_AIOFL(R5)S BEQL 20$ ; NoI3 BBSSI #UCB$V_LD_DISPEN,- ; Set disconnect pending1 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)g10$: RET ; Back to calleri820$: BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),30$- MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB address 3 DECL FCB$L_REFCNT(R2) ; Decr. the reference countn/ DECL FCB$L_ACNT(R2) ; Decr. the access count 8 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 count) BRW 50$;u; Replaced drive processingQ;0830$: TSTL UCB$Q_LD_PD_LKSB+4(R5) ; Did we own the lock? BEQL 40$ ; Nog? DEQ_LOCK LOCKID=UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelockS BLBS R0,40$ ; Successr4 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 ID 5 DECL UCB$L_REFC(R0) ; We're not interested anymoree: BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again* MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5) ;n ; Common path,; -50$: DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5)U ; Get rid of locko* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status( DEQ_LOCK LOCKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of lockd BLBC R0,60$ ; Error 2 MOVL UCB$L_LD_SAVST(R5),R0 ; Get previous status BLBS R0,70$ ; Success ;60$: BBC #LDIO_V_ABORT,- ; Continue if abort is specifiede IRP$L_EXTEND(R3),110$ ; ; Deallocate cached LDIOB blocks;tB70$: REMQUE @UCB$L_LD_IOBFL(R5),R0 ; Get a packet from free queue BVS 80$ ; Branch if none+ JSB G^EXE$DEANONPAGED ; Get rid of bufferM BRB 70$ ; Get next one;a ; Deallocate cached FWIRP blocks; B80$: REMQUE @UCB$L_LD_IRPFL(R5),R0 ; Get a packet from free queue BVS 90$ ; Branch if none+ JSB G^EXE$DEANONPAGED ; Get rid of buffern BRB 80$ ; Get next one>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 R13' BSBW LD_SAVE_TRACE ; Save trace dataB KP_REQCOM ; Complete I/O.M .PAGE/ .SBTTL LD_START_TRACE, Start I/O trace routineP;+++); LD_START_TRACE, Start I/O trace routineo;; Functional description:t;AM; This is the completion of the retrieve tracedata processing. This has to besG; in system context since we need to fork when we find that there is no-; tracedata available.;V ; Inputs: ;E9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)i;v ; Outputs:;r ; None.B;OD; 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;tH; 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 buffer_E; it will queue an AST to the process, and the process will resume attD; LD_TRACE_AST which will copy the new data to the user and complete ; the I/O.;  RET ; Return to callera .PAGE9 .SBTTL LD_TRACE_AST, Display data AST completion routine ;+++4; LD_TRACE_AST, Display data AST completion routine;a; Functional description:e;p9; This is the final completion of the display processing.a@; 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.;r ; Inputs:e;a1; R4 - address of the PCB (process control block)i$; R5 - address of the ACB;G=; The routine must preserve all registers except R0-R5.a;--- UNIVERSAL_SYMBOL LD_TRACE_AST;LD_TRACE_AST: .CALL_ENTRY INPUT=( MOVL ACB$L_ASTPRM(R5),R3 ; Restore IRP MOVL R5,R0 ; Address of ACBs( JSB G^EXE$DEANONPAGED ; Get rid of ACB% MOVL IRP$L_UCB(R3),R5 ; Restore UCB;) BSBW LD_MOVE_TRACE ; Move data to userA- BLBC R0,10$ ; Missed count in R1 on errorQ= DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to #of entrieso,10$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level; SAVIPL=-(SP)$% MOVL R3,UCB$L_IRP(R5) ; Restore IRPe* CALL_REQCOM ; Complete the I/O request0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELp PRESERVE=NO RET .PAGE@ .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing;+++;; LD_START_ENABLE_WATCH, Start I/O enable watch processing ;S; Functional description:;E=; This is the completion of the enable watchpoint processing.cB; Buffers will be allocated, and if any buffer which was specified?; by the user already exists we will return the charge for thisB; buffer to the user by ast.;L ; Inputs:c;r9; R3 - address of the IRP (I/O request packet)t9; R5 - address of the UCB (unit control block)p/; IRP$L_OBCNT - number of entries in userbuffer;n ; Outputs:; ; None.e;oD; The routine must preserve all registers except R0-R2 and R4.;---' UNIVERSAL_SYMBOL LD_START_ENABLE_WATCHt;LD_START_ENABLE_WATCH:t5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addressb) MOVL (R2),R2 ; Get userbuffer addressv% MOVL IRP$L_OBCNT(R3),R4 ; Get counts810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead4 BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not foundr( CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPENDB BNEQ 20$ ; NoE5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints_720$: BSBW LD_COPY_WATCH ; Found exact match, copy new_ ; parameters except pidA> MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^MD+ MOVL IRP$L_PID(R3),R4 ; Process to creditA1 BSBW LD_RETURN_QUOTA ; Give quota back to user  POPR #^M BRW 40$C*30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1D, 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 typec' CLRW LDWATCH_W_FLAGS(R2) ; Zero flags ' MOVW R1,LDWATCH_W_SIZE(R2) ; And size 3 MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queue LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),-P LDWATCH_L_SUSPBL(R2)4 CLRL LDWATCH_L_SUSPCNT(R2) ; Nothing suspended yet# MOVL R2,R1 ; New buffer address $ MOVL 4(SP),R2 ; Get eU$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1UË"ntry address* BSBW LD_COPY_WATCH_NEW ; Copy parameters& MOVL R1,R2 ; Restore buffer addres# MOVL (SP)+,R1 ; Restore listheadp4 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 assist$-50$: ADDL2 #8,SP ; Adjust for error returnv960$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current countf KP_REQCOM ; Complete I/O.B .PAGEB .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing;+++=; LD_START_DISABLE_WATCH, Start I/O disable watch processingS; ; Functional description:B;L>; This is the completion of the disable watchpoint processing.?; If a watchpoint was found with suspended threads queued to ita&; then these threads will be released.; ; Inputs: ;;9; R3 - address of the IRP (I/O request packet)V9; R5 - address of the UCB (unit control block)6/; IRP$L_OBCNT - number of entries in userbuffer_;F ; Outputs:;c ; None.s;ID; 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 countr BNEQ 10$ ; Something to do BRW 100$e'10$: PUSHL R6 ; May not be destroyed ! CLRL R6 ; Nothing removed yet_5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addressF$ BEQL 20$ ; Not there, remove all) MOVL (R2),R2 ; Get userbuffer address$/ MOVL IRP$L_OBCNT(R3),R4 ; Get requested count$820$: 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 list  CMPL R0,R1 ; At the end? BEQL 60$ ; Yes940$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, removec+ DECL UCB$L_LD_WATCHCNT(R5) ; Count packet * INCL R6 ; Flag we've removed something" MOVL R0,R1 ; Use correct input5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints8- PUSHR #^M ; Destroyed by deallocatel5 BBC #LDWATCH_V_FILE,- ; Check for virtual file moded LDWATCH_W_FLAGS(R0),50$a- MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointerf/ DECL FCB$L_REFCNT(R2) ; Decr. reference count$+ DECL FCB$L_ACNT(R2) ; Decr. access count$& 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 creditc/ JSB G^EXE$DEANONPAGED ; Return buffer to poolr1 MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to returnd1 BSBW LD_RETURN_QUOTA ; Give quota back to users 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 successD TSTL R6 ; Anything removed? 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.C .PAGE? .SBTTL LD_COPY_WATCH, Copy watchpoint info into existing blockT> .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 ;e; Functional description:a;n>; This routine copies the data from the user's parameter block; into a watchpoint block ;o ; Inputs:v;e<; R1 - address of the driver allocated watchblock4; R2 - address of the users watchpt block9; R3 - address of the IRP (I/O request packet)L9; R5 - address of the UCB (unit control block)=;K ; Outputs:;B ; None.E;r;---# UNIVERSAL_SYMBOL LD_COPY_WATCH_NEWu;LD_COPY_WATCH_NEW:0 .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 UCB17 BBS #LDWATCHPT_V_FILE,- ; Check for virtual file mode  LDWATCHPT_W_FLAGS(R2),10$c7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumber LDWATCH_L_LBN(R1)_ BRW LD_COPY_WATCH!10$: PUSHR #^M;/ MOVL LDWATCHPT_L_SBK(R2),R4 ; Get SBK address  .DISABLE FLAGGING) MOVL SBK$L_FCB(R4),R4 ; Get FCB addressi .ENABLE FLAGGINGo MOVL R4,LDWATCH_L_FCB(R1)/ INCL FCB$L_REFCNT(R4) ; Incr. reference counto+ INCL FCB$L_ACNT(R4) ; Incr. access countg& MOVL UCB$L_VCB(R5),R8 ; Get it's VCB7 ADAWI #1,VCB$L_TRANS(R8) ; Bump the transaction counte& MOVL FCB$L_WLFL(R4),R7 ; Get any WCB'ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQV% MOVL FCB$W_FID_NUM(R4),- ; Save FIDR LDWATCH_W_FID_NUM(R1) MOVW FCB$W_FID_RVN(R4),-  LDWATCH_W_FID_RVN(R1)o7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumberw0 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 pointersg*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?Q BLEQ 40$ ; Yes2 SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbn_ TSTL (R5)+ ; Skip RVNM SOBGTR R3,30$ ; Next segment# MOVL WCB$L_LINK(R7),R7 ; Next WCBX BNEQ 20$ ; Try this oneL;(C; We should never come here. The VBN was checked in the FDT routinea&; to be within the limits of the file.;0 BUG_CHECK INCONSTATE,FATALr$40$: DECL R6 ; Adjust for VBN = 1C ADDL3 R6,(R5)+,LDWATCH_L_LBN(R1) ; Save converted LBN, skip to RVND. TSTL VCB$L_RVN(R8) ; Relative volume number) BEQL 50$ ; Branch if not a volume setr 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)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 aU" LDWATCH_W_FLAGS(R1) ; volumeset 50$: POPR #^M BSBB LD_COPY_WATCH RSB;O; Fall into LD_COPY_WATCHL;B UNIVERSAL_SYMBOL LD_COPY_WATCHO;LD_COPY_WATCH:  .JSB_ENTRY INPUT=t2 BICW3 #^C,- ; Mask unused bits LDWATCHPT_W_FLAGS(R2),R0( BISW2 R0,LDWATCH_W_FLAGS(R1) ; Options+ MOVW LDWATCHPT_W_ACTION(R2),- ; What to doB 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 enable(;+++@; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable;5; Functional description:;;r6; 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 case29; 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.;N ; Inputs:L;B:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;5 ; Outputs:;_ ; R0 - 0 if found an exact match; 1 if not foundD; R1 - if R0 = 0 entry address#; if R0 = 1 address if insertion ;F;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_ENA ;LD_FIND_WATCH_ENTRY_ENA:t( .JSB_ENTRY INPUT=,OUTPUT= MOVL R1,R0 ; Save for laterR!10$: MOVL (R1),R1 ; Next entryE CMPL R1,R0 ; End of queue ?o BEQL 60$ ; Yes1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?  LDWATCH_W_FLAGS(R1),40$L, CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1)c BNEQ 10$ ; No_* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$i; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockD LDWATCH_L_VBN(R1)D BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)s30$: 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$ ; No.1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?n LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes;S'; Here everythin']k$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Uga"'g matches except actionl"; Determine if action may be added;e5 CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action?n #WATCH_ACTION_OPCOMe BEQL 50$ ; Yes;w@; Add some action other than OPCOM. Make sure we don't overwrite; a current OPCOM action. ;r< CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOMr2 BNEQ 80$ ; No, modify action of current packet;o,; We must check the next packet for a match.;  BRW 10$; A; Make sure that a packet with an action for OPCOM is in front ofhF; 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 entry/C; before the current location we have to provide the address of theC; predecessor.;S350$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up addressV BRB 70$ ; of previous entryt;MA; Nothing found in the queue. Insert new entry after the last ones; 360$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up addressR ; of last entryi 70$: MOVZBL #1,R0 ; Not found BRB 90$80$: CLRL R0 ; Exact matchS90$: 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 disableV;C; Functional description:t;p6; 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 case09; is signalled if the NOLBN flag is set. This is the caseQ8; for a non-transfer function. We can have more of these; for different functions.;0 ; Inputs:u;s:; 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 found$; R1 - if R0 = 0 entry address!; if R0 = 1 last entry addresss; ;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_DISa;LD_FIND_WATCH_ENTRY_DIS:C( .JSB_ENTRY INPUT=,OUTPUT= PUSHL R3 MOVL R1,R0 ; Save for laterL!10$: MOVL (R1),R1 ; Next entry CMPL R1,R0 ; End of queue ?0 BEQL 40$ ; Yes; EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bits  #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 matchf1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?c LDWATCH_W_FLAGS(R1),60$R* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$ ; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blocke LDWATCH_L_VBN(R1)  BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)030$: BEQL 50$ ; Yes' BGTRU 10$ ; Not a higher number yetB 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 address ) 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 entrys 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+2v7 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 processijܾ$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U"8ng;+++;; LD_START_RESUME_WATCH, Start I/O resume watch processingb;o; Functional description:F; =; 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)r/; 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_ACTION_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 system;*; 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 functioncodeU, 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?r 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;2J; 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; D70$: 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 ac f$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U"Ition<; 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 #; other - Returned by lockmanager ; *; Only R3-R5 are preserved as we may fork.;;--- UNIVERSAL_SYMBOL LD_ENQ_LD_LOCK;LD_ENQ_LD_LOCK:% .JSB_ENTRY INPUT=,OUTPUT=a ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-M% RESNAME=UCB$T_LD_FILE_RESNAM(R5),-W EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BEQL 10$ ; Yes BLBS R0,50$ ; Ok BRW 90$ ; Error ;EC; We are the first one queueing a lock for this resource. Setup thetF; 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 blockH! MOVL UCB$L_DDB(R5),R2 ; Get DDB33 MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation class; LDFLVB_L_ALLOCLS(R1)% MOVW UCB$W_UNIT(R5),- ; Unit numberA LDFLVB_W_UNIT(R1)w( MOVW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1)" MOVB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1)B$ MOVB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1)1 MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberU LDFLVB_L_MAXBLOCK(R1)m& CLRB LDFLVB_B_FLAGS(R1) ; Init flags' BBC #LDIO_V_SHARE,- ; Shared access?E 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),-P BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$;C; Lock is converted to CR.;:9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion status ) BRW 90$ ; Exit with completion statuso;o; Lock is granted as EX.;E=50$: MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusd;rN; 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?t& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrong:;MD; We now have the lock in EX mode. Check the lock value block to see; if we match.;eG60$: MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value blockQ1 BBC #LDFLVB_V_SHARE,- ; Check for shared access$ LDFLVB_B_FLAGS(R1),80$1 BBC #LDIO_V_SHARE,- ; Shared access requested?K IRP$L_EXTEND(R3),80$! MOVL UCB$L_DDB(R5),R2 ; Get DDBo3 CMPL DDB$L_ALLOCLS(R2),- ; Check allocation class) LDFLVB_L_ALLOCLS(R1) BNEQ 80$ ; No match % CMPW UCB$W_UNIT(R5),- ; Unit numberL LDFLVB_W_UNIT(R1)  BNEQ 80$ ; No match( CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1) BNEQ 80$ ; No matchA" CMPB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1); BNEQ 80$ ; No matchA$ CMPB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1) BNEQ 80$ ; No match_1 CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberG LDFLVB_L_MAXBLOCK(R1)o BNEQ 80$ ; No match4) BRW 20$ ; Convert down to CR and exitF;+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.;/80$: DEQ_LOCK LOCKID=UCB$Q_LD_FILE_LKSB+4(R5),-! VALBLOCK=UCB$A_LD_FILE_LVB(R5)v ; Get rid of file lock BLBC R0,90$ ; Error_/ MOVZWL #SS$_FILALRACC,R0 ; Assume file in useA( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),90$) MOVZWL #SS$_DEVALLOC,R0 ; Device In use;90$: RSB .PAGEB .SBTTL LD_ENQ_FILE_BLKRTN, Enqueue file lock blocking ast routine;+++:; LD_ENQ_FILE_BLKRTN, Enqueue LD lock blocking ast routine;6; Functional description:G;RH; This routine will be called by the lockmanager when someone elseE; tries to enqueue an incompatible lock on the LD file resource name.I;RB; 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 weuB; 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 looksfB; 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 secondUD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.B; ; Inputs:m;i2; 4(AP) - address of context parameter 1; ; IOLOCK8 (=SCS) forklock heldL;A ; Outputs:; *; Only R3-R5 are preserved as we may fork.;m;---$ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN;LD_ENQ_FILE_BLKRTN: .CALL_ENTRY- MOVL FLK_PRM1(AP),R5 ; Recover UCB addressT;)D; 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.$;M= KP_ALLOCATE_KPB KPB=UCB$L_LD_BLKKPB(R5),- ; Allocate new KPBL FLAGS=#KPB$M_DEALLOC_AT_END BLBC R0,10$ ; ErrorR/ MOVL UCB$L_LD_BLKKPB(R5),R0 ; Get KPB addressV2 MOVL R5,KPB$PS_UCB(R0) ; Save UCB address in KPB# KP_START KPB=UCB$L_LD_BLKKPB(R5),-r& ROUTINE=LD_ENQ_FILE_BLKRTN_ACTION,-" REGISTERS=#KPREG$K_HLL_REG_MASK RET;mA10$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error allocating KPB ;;+ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN_ACTIONC;LD_ENQ_FILE_BLKRTN_ACTION:o .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),-R EFLAGS=,-e ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5) : BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Check completion status;$E; Lock is converted to NL, convert back to CR. Specify QUECVT so thati=; the requesting thread gets it's chance to acquire the lock.I;O"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-P BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)S;_; Lock is converted to CR.; 4 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?G& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit.20$: BRW 60$ ; Bugcheck on all other errors930$: KP_END KPB=UCB$L_LD_BLKKPB(R5) ; Finish this thread ; 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.$;R"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-$ EFLAGS=,-D ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)I- BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Get status ;#.; Lock is converted to NL, convert back to EX.;  ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-r EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5) . MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?  BEQL 50$ ; Acceptable ) BLBC R0,60$ ; Check completion status;nI; Lock is converted to EX, convert back to NL to rewrite the value block. ; "50$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-b EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)e0 BLBC UCB$Q_LD_FILE_LKSB(R5),60$ ; Quit on error- BRW 10$ ; Ok, convert back to CR and exit_;U;; Bugcheck since there's nobody to return this error to....;TC60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanagerg .PAGE: .SBTTL LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;+++4; LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;r; Functional description:g;eJ; This routine will enqueue the device lock of the specified device.>; It will check if the device is used on!*5$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U "Z another node anywhereE; in the cluster. If true then it's only allowed if we ordered sharedCD; access and if the remote filename matches ours so that XQP locking ; will work.;T%; The locking protocol is as follows:Y;I; First we will enqueue an EX-mode lock on our resource. If it is grantedEF; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block andF; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;nG; If we don't get it immediately then it means that another thread ownsIB; 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 blockNH; 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.; I; If we encounter an invalid lock value block during this process we will,I; 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;d;r ; Inputs: ; 9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block) ); UCB$T_LD_DEV_RESNAM(R5) - Resource namen;l; IOLOCK8 (=SCS) forklock heldo;i ; Outputs:;I ; R0 - statusD; SS$_NORMAL - Success_5; SS$_FILALRACC - File in use in an incompatible way #; other - Returned by lockmanager ;p*; Only R3-R5 are preserved as we may fork.;#;---! UNIVERSAL_SYMBOL LD_ENQ_DEV_LOCKV;LD_ENQ_DEV_LOCK:M% .JSB_ENTRY INPUT=,OUTPUT=n 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$ ; Yesp BLBS R0,50$ ; Ok BRW 90$ ; Exit;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.;nF5$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value block! MOVL UCB$L_DDB(R5),R2 ; Get DDBa, MOVZBL DDB$B_NAME_LEN(R2),R0 ; Name length" CMPL R0,#6 ; Max we can handle BLEQ 10$ ; OkayA 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)t POPR #^M %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 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 numbers LDDLVB_W_UNIT(R1)D"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-f EFLAGS=,- ERROR=90$;i; Lock is converted to CR.;e8 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get completion status) BRW 90$ ; Exit with completion statusM; ; Lock is granted as EX.;C<50$: 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.;c1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?n& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrongL;)D; We now have the lock in EX mode. Check the lock value block to see; if we match.;(G60$: 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$ INCB R0 ; Check length field too PUSHR #^M1 CMPC3 R0,DDB$T_NAME(R2),- ; Check name + length0 LDDLVB_T_DEVNAM(R1)$ POPR #^Mt BNEQ 80$ ; No match ( 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 matchk 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 matchC770$: CMPB DDB$L_ALLOCLS(R2),- ; Check allocation classh LDDLVB_B_ALLOCLS(R1) BNEQ 80$ ; No match % CMPW UCB$W_UNIT(R5),- ; Unit number- LDDLVB_W_UNIT(R1); BNEQ 80$ ; No match) BRW 20$ ; Convert down to CR and exit2;tE; Dequeue the lock since we're not allowed to use the file or device.AF; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.; .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$ ; ErrorD) MOVZWL #SS$_DEVALLOC,R0 ; Device In useT90$: RSB .PAGEC .SBTTL LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routineW;+++=; LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routinee;o; Functional description:A;_H; This routine will be called by the lockmanager when someone elseG; tries to enqueue an incompatible lock on the LD device resource name.b; 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.;sC; If the second conversion reveals a 'value block invalid' error weKB; 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'tA; block other threads which in the conversion process. The second;D; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.;D ; Inputs:K;n2; 4(AP) - address of context parameter 1;n; IOLOCK8 (=SCS) forklock helds;u ; Outputs:;u*; Only R3-R5 are preserved as we may fork.;e;---# UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTNa;LD_ENQ_DEV_BLKRTN: .CALL_ENTRY- MOVL FLK_PRM1(AP),R5 ; Recover UCB addressd;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. ;k= KP_ALLOCATE_KPB KPB=UCB$L_LD_BLKKPB(R5),- ; Allocate new KPB- FLAGS=#KPB$M_DEALLOC_AT_END BLBC R0,10$ ; Errorl/ MOVL UCB$L_LD_BLKKPB(R5),R0 ; Get KPB addresst2 MOVL R5,KPB$PS_UCB(R0) ; Save UCB address in KPB# KP_START KPB=UCB$L_LD_BLKKPB(R5),- % ROUTINE=LD_ENQ_DEV_BLKRTN_ACTION,-i" REGISTERS=#KPREG$K_HLL_REG_MASK RET;eA10$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error allocating KPBd;n* 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=,-w ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)e9 BLBC UCB$Q_LD_DEV_LKSB(R5),20$ ; Check completion status ;cE; Lock is converted to NL, convert back to CR. Specify QUECVT so thate=; the requesting thread gets it's chance to acquire the lock.u;r"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-o EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5) ;u; Lock is converted to CR.;r3 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get final status 1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?_& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit.20$: BRW 60$ ; Bugcheck on all other errors930$: KP_END KPB=UCB$L_LD_BLKKPB(R5) ; Finish this thread ; 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.D;C"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,-L ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5),, BLBC UCB$Q_LD_DEV_L"m Q$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U"kKSB(R5),20$ ; Get status;>.; Lock is converted to NL, convert back to EX.;Q ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)h- MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get status01 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?o BEQL 50$ ; AcceptableC) BLBC R0,60$ ; Check completion statusO;(I; Lock is converted to EX, convert back to NL to rewrite the value block.R;-"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$,- KPBADR=UCB$L_LD_BLKKPB(R5)/ BLBC UCB$Q_LD_DEV_LKSB(R5),60$ ; Quit on errorS- BRW 10$ ; Ok, convert back to CR and exitu;l;; Bugcheck since there's nobody to return this error to....); C60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanagerR .PAGEI .SBTTL LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOMC;+++D; LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM;L; Functional description:M;L@; 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 routineiE; will then invoke the system-service $SNDOPR to process the request.eH; 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 aa@; usermode AST because we then need to modify the pageprotectionG; of the ACB to allow user-read access. If there is no issueing process0D; (a mount-verification thread for example) then we queue the AST to ; the OPCOM.;B ; Inputs: ;o+; R1 - LDWATCH structure addressi9; R3 - address of the IRP (I/O request packet).9; R5 - address of the UCB (unit control block)o;c ; Outputs:; ; R0 - StatusS;E@; The routine must preserve all registers except R0 and R2;V;--- UNIVERSAL_SYMBOL LD_SEND_OPCOM;LD_SEND_OPCOM:)( .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^M(- MOVL IRP$L_PID(R3),R4 ; I/O from a process?L1 BGTR 20$ ; Yes (no mount-verification thread)T;)U; This thread has no PID but a system-space address (mount-verification for example).RN; 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-modeCP; 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.L;L1 BSBW LD_FIND_OPCOM ; Check if OPCOM is running  BLBS R0,20$ ; YesaA 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 sizeF MOVL (SP),R1 % MOVB #DYN$C_ACB,- ; Used as an ACB$ LDSNDOPRLST_B_TYPE(R2)5 CLRL LDSNDOPRLST_L_KAST(R2) ; Assume normal processA0 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 willu3 LDSNDOPRLST_B_RMOD(R2) ; deallocate this buffer , MOVAB LD_OPCOM_AST,- ; Routine to execute LDSNDOPRLST_L_AST(R2)n@ MOVL R2,LDSNDOPRLST_L_ASTPRM(R2) ; Setup our block as parameter5 BBS #LDWATCH_V_FILE,- ; Check for virtual file mode  LDWATCH_W_FLAGS(R1),40$f% 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)o50$:5 ASSUME LDSNDOPRLST_W_ACTION EQ LDSNDOPRLST_W_FLAGS+2b4 MOVL LDWATCH_W_FLAGS(R1),- ; Move flags and action LDSNDOPRLST_W_FLAGS(R2) 5 ASSUME LDSNDOPRLST_W_RETCODE EQ LDSNDOPRLST_W_FUNC+2m7 MOVL LDWATCH_W_FUNC(R1),- ; Move function and retcodee LDSNDOPRLST_W_FUNC(R2)8 ASSUME LDSNDOPRLST_W_FID_NUM+2 EQ LDSNDOPRLST_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R1),- ; Save FID  LDSNDOPRLST_W_FID_NUM(R2)i MOVW LDWATCH_W_FID_RVN(R1),- LDSNDOPRLST_W_FID_RVN(R2)s PUSHR #^M/ MOVZWL #LDSNDOPRLST_K_DEVNAM-1,R0 ; Buffersize: .DISABLE FLAGGING< MOVAB LDSNDOPRLST_T_DEVNAM+1(R2),R1 ; Buffer for devicename .ENABLE FLAGGING_% MOVL #-2,R4 ; DVI$_DISPLAY_DEVNAML5 CALL_CVT_DEVNAM ; Get devicename in readable formo8 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 ASTF POPR #^M BLBS R0,60$ ; All ok;VJ; 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.B;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 .PAGE8 .SBTTL LD_FIND_OPCOM, Find the pid of the OPCOM process;+++3; LD_FIND_OPCOM, Find the pid of the OPCOM processD;C; Functional description:C;_=; This routine locates the internal pid of the OPCOM process.o;r ; Inputs:c;e ; None;e ; Outputs:;t ; R0 - Statusu; R4 - Internal pid of opcom; @; The routine must preserve all registers except R0 and R4;U;--- UNIVERSAL_SYMBOL LD_FIND_OPCOME;LD_FIND_OPCOM: .JSB_ENTRY OUTPUT= PUSHR #^MC, 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?: BEQL 20$ ; Yes, get next + CMPL PCB$L_UIC(R2),- ; UIC match ([1,4])?P #^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 OPCOME OPCOM_NAME POPR #^M_ BNEQ 20$ ; No match) MOVL PCB$L_PID(R2),R4 ; Get internal pidK" MOVZWL #SS$_NORMAL,R0 ; Success! BRB 30$/20$: AOBLEQ G^SCH$GL_MAXPIX,R1,10$ ; Next entrye$ 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 userv;+++:; LD_RETURN_QUOTA, Return quota of charged buffer to user;n; Functional description:v;e;; This routine returns the quota of a buffer we charged for;; back to a user. Since we may be in system context we willS<; 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:o;rO; R1 - number of bytes to return to BYTCNT and BYTLM, or WCB pointert; R4 - pid of process to creditT ; R5 - UCB;x ; Outputs:;o ; R0 - status;AD; The routine must preserve all registers except R0-R2 and R4.;D;---! UNIVERSAL_SYMBOL LD_RETURN_QUOTA ;LD_RETURN_QUOTA:i( .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^M  BSBW LD_ALLO_LDIOB ; Get ACBc" BLBC R0,10$ ; Check for errors MOVL R2,R5 ; Get ACB address5 MOVL R1,ACB$L_ASTPRM(R5) ; Save bytecount to returnd" 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 routinew;+++,; LD_QUOTA_AST, Return quota action routine;t; Functional description: ;'@; This routine is called as an AST routine in the context of the; process issueing the I/O.c;l<; This routine has two functions, depending on ACB$L_ASTPRM:;.5; If < 0, it is a pointer to a WCB for which we havek#; to return the quota to the user.t5; If > 0 it is the number of bytes to credit a user. ;w ; Inputs:h;a1; R4 - address of the PCB (process control block) $; R5 - address of the ACB6; ACB$L_ASTPRM(R5) - if > 0: Number of b#$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U8x"|ytes to return; if < 0: WCB address;l=; The routine must preserve all registers except R0-R5.e;  UNIVERSAL_SYMBOL LD_QUOTA_AST;LD_QUOTA_AST: .CALL_ENTRY INPUT=* MOVL ACB$L_ASTPRM(R5),R3 ; Get parameter MOVL R5,R0 ; Address of ACBS( JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL R3,R0  BGTR 20$ ; Byte count ; E; Remove the pointer from the channel to the window, and return thet3; previously allocated bytecount quota to the user.5; 5 BBSS #WCB$V_SHRWCB,- ; Make WCB a shared structurei. WCB$B_ACCESS(R0),10$ ; if not already done3 JSB G^MMG$RET_BYT_QUOTA ; Return byte count quotam910$: ADAWI #1,WCB$L_REFCNT(R0) ; Count another referencey BRB 30$120$: JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaO30$: MOVZWL #SS$_NORMAL,R0 RET .PAGE+ .SBTTL LD_COMPLETE, I/O completion routine ;+++&; LD_COMPLETE, I/O Completion routine.;R?; This routine locates the IRP whose address is into R5 in theL?; LDIO list, removes the entry from the queue, restores the PID 7; and inserts the IRP back in the I/O completion queue. ; ; Inputs: ;a ; R5 - IRP; IPL - IPL$_IOPOSTf;t ; This routine may use R0 to R5.;e;--- UNIVERSAL_SYMBOL LD_COMPLETEn ;LD_COMPLETE:c .CALL_ENTRY INPUT=_( 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 IRP & MOVL IRP$L_LD_LDUCB(R5),R5 ; Get UCB, MOVL R5,KPB$PS_UCB(R0) ; Insert UCB in KPB5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelD SAVIPL=-(SP) ;eJ; Continue postprocessing as a kernel thread. That gives us the possiblity!; to stall and fork when we want.R;D 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(R5),- ; Release lock NEWIPL=(SP)+,-s 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 IRPR; MOVL IRP$L_LD_LDUCB(R5),R5 ; Get UCB address logical disk15 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelQ SAVIPL=-(SP),- PRESERVE=NO8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB5 FORKLOCK LOCK=UCB$B_FLCK(R4),- ; Raise to Fork levele& 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)+,-i PRESERVE=NO,- CONDITION=RESTORE0 MOVL IRP$L_LD_LDIOB(R3),R2 ; Get LDIOB address;hF; 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.;s6 REMQUE IRP$L_LD_FWDQFL(R3),R0 ; Remove forwarded IRP7 SUBL2 #IRP$L_LD_FWDQFL,R0 ; Point to real IRP section  .DISABLE FLAGGING: ADDL2 IRP$L_IOST1+2(R0),- ; Count accumulated byte count LDIOB_L_ABCNT(R2)a .ENABLE FLAGGINGC4 BLBS IRP$L_IOST1(R0),10$ ; Check the return status8 MOVW IRP$L_IOST1(R0),LDIOB_W_IOST(R2); Update if errors010$: BSBW LD_DEAL_FWIRP ; Deallocate the FWIRP5 ADAWI #-1,LDIOB_W_IRPCNT(R2) ; Decrement ref. countP BNEQ 20$ ; Not yet ready/ MOVL LDIOB_L_IRP(R2),R3 ; Restore correct IRPl; B; Set the return status and the accumulated byte count in the IRP.;B> MOVW LDIOB_W_IOST(R2),IRP$L_IOST1(R3); Copy the return status .DISABLE FLAGGING/ MOVL LDIOB_L_ABCNT(R2),- ; And the byte count IRP$L_IOST1+2(R3)B .ENABLE FLAGGINGc+ CLRW IRP$L_IOST1+6(R3) ; Zero unused wordB5 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 postprocessingn MOVL 4(AP),R0 ; Get KPB= BISL2 #KPB$M_DEALLOC_AT_END,- ; Deallocate when thread endsd KPB$IS_FLAGS(R0)420$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-h PRESERVE=NO,- CONDITION=RESTORE# KP_END KPB=4(AP) ; End of thread. RET$ UNIVERSAL_SYMBOL LD_COMPLETE_FINISH;LD_COMPLETE_FINISH: .JSB_ENTRY INPUT= 1 CALL_POST_IRP ; Insert IRP back in post queueV. 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) ; R4i PUSHL UCB$Q_FR3(R5) ; R3m2 CALLS #3,@UCB$L_FPC(R5) ; Resume the fork thread POPR #^ML40$: .IF DF MDDRIVER_WORKAROUNDw; M; Work around MDdriver bug. If an erase function is given to DECRAM, then thetJ; iosb does not contain the bytecount but zero. In a certain configurationK; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attemptsvL; to erase a number of blocks, and it looks in the returned bytecount to see; how much is left to do.C;T4 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?  BNEQ 50$ ; Nor .DISABLE FLAGGING@ MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount .ENABLE FLAGGINGt50$: .ENDC .IF DF DUDRIVER_WORKAROUNDs;M; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueingoM; an IRP to the physical device UCB when UCB$L_RWAITCNT is not zero. This mayeN; 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,uC; which causes everything to wait for this packet. Triggering mountsJ; 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 thingsL; to go wrong.;T PUSHL R5 ; Save logical UCB_8 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 levelV& 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)+,-E PRESERVE=NO,- CONDITION=RESTORE60$: MOVL (SP)+,R5 .ENDC RSB;  UNIVERSAL_SYMBOL POST_PACKACK;POST_PACKACK: .JSB_ENTRY INPUT=.# EXTZV #IRP$V_FCODE,#IRP$S_FCODE, -s0 IRP$L_FUNC(R3),R1 ; Extract the function code" CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; NoQ4 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 at$; drive connect time. ;k8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB- MOVB UCB$B_DEVTYPE(R4),- ; Copy device type  UCB$B_DEVTYPE(R5)d&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, tracks:$ 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 routinel;+++(; LD_CANCEL, Generic Cancel I/O routine;C; Functional description:Q;_<; This routine cancels all outstanding I/O for the specified;; channel and PID. We also check if the IRP is in the trace0<; wait queue and remove that if true. We also check if there2; was a watchpoint suspended and process that too.;Q ; Inputs:B;); R2 - Channel index numbern; R3 - IRP from UCB$L_IRPE ; R4 - PCB ; R5 - UCB#; R8 - Cancel reason code, one of :D*; CAN$C_CANCEL If called through $CANCEL; system service.o*; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service.;u ; Outputs:;i5; The routine must preserve all registers exept R0-R3l;b;--- UNIVERSAL_SYMBOL LD_CANCEL$ ;LD_CANCEL:  $DRIVER_CANCEL_ENTRYB2 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 threada5 BSBW LD_CANCEL_WATCH ; Cancel suspended watchpoint 3 BBC #UCB$V_DELETEUCB,- ; Only if we're going awayA UCB$L_STS(R5),20$fA BICL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Reset cluster wide visibleu: MOVL UCB$L_LD_CPID(R5),UCB$L_CPID(R5); Restore charge pid5 MOV$$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Ut"W UCB$W_LD_CHARGE(R5),- ; Restore charged amount  UCB$W_CHARGE(R5)20$: RET ; Returna .PAGE( .SBTTL LD_CANCEL_IO, Cancel I/O routine;+++3; LD_CANCEL_IO, Cancels I/O operations in progressT; ; Functional description:h;s<; This routine cancels all outstanding I/O for the specified;; channel and PID. We search the active IO list for LDIOB'sm<; 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 correct ; registers.; ; Inputs: ;u; R2 - Channel index number ; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codeU;( ; Outputs:;o5; The routine must preserve all registers exept R0-R3h5; The routine may set the UCB CANCEL bit in UCB$L_STS;;--- 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 ?o BNEQ 20$ ; Nou+ MOVL LDIOB_L_IRP(R1),R0 ; Get IRP addressd6 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 listB30$: MOVL (SP)+,R6 RSB940$: MOVAL LDIOB_L_FWDQFL(R1),R0 ; Extract forwarded IRPA 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 FLCKD< 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)M9 MOVAL UCB$L_IOQFL(R5),R3 ; Get addr. of wait queue listi 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 ?2' BEQL 80$ ; Not found, call CANCELIOe" 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 ?06 BICW2 #IRP$M_FUNC,IRP$L_STS(R3) ; Clear buffered read970$: MOVW #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return statusE1 CALL_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 addressD1 CALL_CANCELIO SAVE_R0R1=NO ; Call cancel IO rtn 490$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-D PRESERVE=NO,- CONDITION=RESTORE1 POPR #^M ; Restore saved registersC BRW 20$ ; Continue searchS .PAGE1 .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routineD;+++,; LD_CANCEL_TRACE, Cancel trace I/O routine;; Functional description:+;2C; This routine checks if there's an IRP in the trace wait queue andVF; 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.;;t ; Inputs:y;c; R2 - Channel index numberu; R3 - IRP from UCB$L_IRPR ; R4 - PCB ; R5 - UCB; R8 - Cancel reason coden;i ; Outputs:;R5; The routine must preserve all registers exept R0-R3r;i;---! UNIVERSAL_SYMBOL LD_CANCEL_TRACEO;LD_CANCEL_TRACE:a$ .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 doneA? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?F BNEQ 10$ ; NoO6 CMPL IRP$L_CHAN(R1),R2 ; Is this the right channel ? BNEQ 10$ ; NoC( CLRL IRP$L_SVAPTE(R1) ; No more SVAPTE7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statusr' REMQUE IRP$L_IOQFL(R1),R3 ; Remove itp1 CALL_POST_IRP ; Insert IRP back in post queuec20$: RSB .PAGE1 .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routine4;+++,; LD_CANCEL_WATCH, Cancel watch I/O routine;O; Functional description:<;R;; This routine will check if there's a suspended watchpointc:; 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 (itt9; comes from UCB$L_IRP), so we check against the PID fromR ; the PCB.;t ; Inputs:y;C; R2 - Channel index numberL; R3 - IRP from UCB$L_IRP, ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code4;P ; Outputs:; 5; The routine must preserve all registers exept R0-R3t;u;---! UNIVERSAL_SYMBOL LD_CANCEL_WATCHu;LD_CANCEL_WATCH:#$ .JSB_ENTRY INPUT=,- PRESERVE=5 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active?  BEQL 40$ ; No$? MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entrys 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 donee9 TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue?T BEQL 10$ ; Nos PUSHR #^MP; MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listheadt MOVL R0,R1 ; Save for latere/20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irpe CMPL R1,R0 ; Back at start?l BEQL 30$ ; Yes? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?a BNEQ 20$ ; No 6 CMPL IRP$L_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; Not7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return status ' REMQUE IRP$L_IOQFL(R1),R3 ; Remove ite1 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 scano BRW 10$40$: RSB .PAGE. .SBTTL LD_ALLO_LDIOB, Allocate I/O data block;+++(; LD_ALLO_LDIOB, Allocate I/O data block;_; Functional description:;SG; This routine allocates and initializes an I/O datablock which is usedDH; for I/O requests as well as trace information. It tries to get a blockG; from the free queue, if that fails we get one from the system's pool.V;4 ; Inputs:R; ; R5 - UCB addressA;M ; Output:-;S; R0 - Statust; R2 - LDIOB addressU;_A; The routine must preserve all registers except R0 and R2.,; ;--- UNIVERSAL_SYMBOL LD_ALLO_LDIOBH;LD_ALLO_LDIOB:h% .JSB_ENTRY INPUT=,OUTPUT=o> REMQUE @UCB$L_LD_IOBFL(R5),R2 ; Get a packet from free queue BVC 20$ ; Branch if got one_ PUSHL R1 / MOVZWL #LDIOB_K_LENGTH,R1 ; Get packet lengthn3 JSB G^EXE$ALONONPAGED ; Allocate packet from pool ) BLBC R0,10$ ; Check the return statusI- MOVW R1,LDIOB_W_SIZE(R2) ; Setup size fielde6 MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field'10$: MOVL (SP)+,R1 ; Restore registere! BLBC R0,30$ ; Return on errori@20$: 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),-e LDIOB_L_FWDQBL(R2)+ CLRL LDIOB_L_KPB(R2) ; Clear KPB address5. CLRB LDIOB_B_FDT(R2) ; Clear FDT type field" MOVZWL #SS$_NORMAL,R0 ; Success30$: RSB ; Return  .PAGE- .SBTTL LD_ALLO_FWIRP, Allocate a forward IRPB;+++'; LD_ALLO_FWIRP, Allocate a forward IRPR;; Functional description :;G; This routine allocates a FW IRP. It first tries to grab one from the H; free queue. If this fails, it allocates one from pool, and initializes; it. ;e ; Inputs :;t ; R5 - UCB;$ ; Outputs :1; ; R3 - New allocated IRP ; R5 - UCB;T;--- UNIVERSAL_SYMBOL LD_ALLO_FWIRP0;LD_ALLO_FWIRP:_% .JSB_ENTRY INPUT=,OUTPUT= PUSHR #^M0 MOVZWL #IRP$K_LD_IRPLEN,R1 ; Get packet length> REMQUE @UCB$L_LD_IRPFL(R5),R3 ; Get a packet from free queue BVC 10$ ; Branch if got oneL3 JSB G^EXE$ALONONPAGED ; Allocate packet from poolt) BLBC R0,20$ ; Check the return statusL MOVL R2,R3 ; Copy it to R310$: PUSHR #% M$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1Us"^MI, 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 #^M RSB ; ReturnB20$: BUG_CHECK INCONSTATE,FATAL_ .PAGE- .SBTTL LD_DEAL_FWIRP, Deallocate forward IRP;;+++'; LD_DEAL_FWIRP, Deallocate forward IRP ; ; Functional description :;;>; This routine restores forward IRPs in the FW IRP free queue.;O ; Inputs :;5 ; R0 - FWIRP ; R5 - UCB; ; Outputs : ;e ; R5 - UCB;s;--- UNIVERSAL_SYMBOL LD_DEAL_FWIRPv;LD_DEAL_FWIRP:i% .JSB_ENTRY INPUT=,OUTPUT=8 INSQUE IRP$L_IOQFL(R0),- ; Insert packet in free queue @UCB$L_LD_IRPBL(R5)L RSB ; Returnn .PAGE;+++!; LD_DEAL_LDIOB, Deallocate LDIOBK;B; Functional description :; 7; This routine restores LDIOBs in the LDIOB free queue.;T ; Inputs :;S ; R0 - LDIOB ; R3 - IRP ; R5 - UCB;R ; Outputs :E;Y; None;P;--- UNIVERSAL_SYMBOL LD_DEAL_LDIOBR;LD_DEAL_LDIOB:P .JSB_ENTRY INPUT=( TSTL LDIOB_L_KPB(R0) ; KPB specified? BEQL 10$ ; No/ MOVL LDIOB_L_KPB(R0),- ; Restore original KPBe IRP$PS_KPB(R3)<10$: INSQUE LDIOB_L_QFL(R0),- ; Insert packet in free queue @UCB$L_LD_IOBBL(R5)L RSB ; Return( .PAGE* .SBTTL LD_SAVE_TRACE, Save trace I/O dataC .SBTTL LD_SAVE_TRACEALT, Save trace I/O data, alternate entrypointL;+++%; LD_SAVE_TRACE - Save trace I/O dataE?; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypointN;P; Functional description:-; I; This routine is called just before going to REQCOM to save the eventual ; trace data.a;d; Inputs for LD_SAVE_TRACE:d;a; R0/R1 - Return statuss9; R3 - address of the IRP (I/O request packet)o9; R5 - address of the UCB (unit control block).;s; Inputs for LD_SAVE_TRACE_FDT: ;e; R0/R1 - Return status_1; R4 - address of the PCB (process control block)L9; R5 - address of the UCB (unit control block) ; R9 - Function code; R10 - PIDB;R; Inputs for LD_SAVE_TRACE_ALT:t; 9; R3 - address of the IRP (I/O request packet)r"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)e;tB; This routine must preserve all registers except R2 and R4.; ;--- UNIVERSAL_SYMBOL LD_SAVE_TRACEu;LD_SAVE_TRACE:  .JSB_ENTRY INPUT=.% MOVL R3,UCB$L_IRP(R5) ; Restore IRP1, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got one( BRW 30$!5$: MOVQ R0,-(SP) ; Save status3- BSBW LD_ALLO_LDIOB ; Allocate pool to hold  ; temporary tracedata  BLBC R0,20$ ; Exit on errorB .DISABLE FLAGGING1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSBI .ENABLE FLAGGINGT MOVL R2,R4e MOVL IRP$L_MEDIA(R3),-s LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; Bytecounth MOVW IRP$L_FUNC(R3),-" LDIOB_W_FUNC(R4) ; Functioncode' CLRL LDIOB_L_PID(R4) ; Assume no pid)7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),10$ ; Mount verify IRP?=< 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),-U5 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end time " ; for synchronous functions) .ENABLE FLAGGINGe) CLRL LDIOB_L_ELAPSED(R4) ; Elapsed timeR BRW LD_TRACEw+20$: ADDL2 #8,SP ; Recover scratch spaceu30$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_FDTo;LD_SAVE_TRACE_FDT:n" .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$a, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got oneo 2$: BRW 30$#!5$: MOVQ R0,-(SP) ; Save statusR- BSBW LD_ALLO_LDIOB ; Allocate pool to hold$ ; temporary tracedatak BLBC R0,20$ ; Exit on errorR+ MOVB #1,LDIOB_B_FDT(R2) ; Flag FDT caller. .DISABLE FLAGGING1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSBt .ENABLE FLAGGINGE MOVL R2,R40 CLRL LDIOB_L_MEDIA(R4) ; LBN# CLRL LDIOB_L_BCNT(R4) ; Bytecountb) MOVW R9,LDIOB_W_FUNC(R4) ; Functioncodey' CLRL LDIOB_L_PID(R4) ; Assume no pidc 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),-T5 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end timel" ; for synchronous functions) .ENABLE FLAGGINGo) CLRL LDIOB_L_ELAPSED(R4) ; Elapsed timeb BRW LD_TRACEo+20$: ADDL2 #8,SP ; Recover scratch space 30$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_ALT ;LD_SAVE_TRACE_ALT:n .JSB_ENTRY INPUT= % MOVL R3,UCB$L_IRP(R5) ; Restore IRP , TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 30$ ; Not there, quit;U@; 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 .DISABLE FLAGGING5 MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start timeN BEQL 30$ ; Not set( MOVQ IRP$L_IOST1(R3),- ; IOSB contents LDIOB_Q_STAT(R4) .ENABLE FLAGGINGS$ MOVL LDIOB_L_PID(R4),R0 ; Get IPID' CLRL LDIOB_L_PID(R4) ; Assume no pidC7 BBS #IRP$V_MVIRP,IRP$L_STS(R3),10$ ; Mount verify IRP?f< 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 FLAGGINGd1 BBC #UCB$V_LD_ACCURATE,- ; Check if RSCC wantedB 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),- R0 - SUBL2 #8,SP ; Need quadword scratch space 5 EVAX_MULQ R0,#1000000,(SP) ; Convert to microsecondsl;sH; 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.....a;q2 EDIV UCB$Q_LD_CYCLEFREQ(R5),- ; Save elapsed time (SP),- LDIOB_L_ELAPSED(R4),-p R1* ADDL2 #8,SP ; Get rid of scratch space20$: BRW LD_TRACE3#30$: MOVL R4,R0 ; Point to LDIOB5) BSBW LD_DEAL_LDIOB ; Get rid of bufferD RSB .PAGE$ .SBTTL LD_TRACE, Trace I/O IRP data;+++; LD_TRACE - Trace I/O IRP dataa;d; Functional description:$;LD; This routine is called by the start I/O routine to log information); about the I/O request in a tracebuffer.S;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 theBJ; 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 aAK; mutex we can remain at IPL 2 in the FDT routine when we get the data fromIL; 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;a ; Inputs:p;i9; R3 - address of the IRP (I/O request packet)a0; (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.;$;; The routine must preserve all registers except R2 and R4.(;--- UNIVERSAL_SYMBOL LD_TRACE ;LD_TRACE:* TSTB LDIOB_B_FDT(R4) ; Called from FDT? BNEQ 5$ ; YesL/2$: LOCK_TRACE ACCESS=WRITE,- ;&.h$LD063.BN)'[VDBURG.LD.V63.SRC]LDDRIVER_ALPHA.MAR;1U Lock the mutexC CONTEXT=SYSTEML 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)0/ KP_STALL_GENERAL KPB=R0,- ; Wait for callback  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 address@ 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 buffer5 MOVL UCB$L_LD_TRCBUFPTR(R5),R1 ; Get current pointer : ADDL3 #LDTRCENT_K_LENGTH,R1,R0 ; Point to next free place= ADDL3 UCB$L_LD_TRCBUFSIZ(R5),R4,R2 ; Calculate end of buffer  CMPL R0,R2 ; Past the end? BLEQU 20$ ; No7 MOVL R1,UCB$L_LD_TRCWRAP(R5) ; Flag buffer wraparound;- INCL UCB$L_LD_TRCLOST(R5) ; Count lost dataR- MOVL R4,R1 ; Reset ptr to start of bufferF520$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENT ) MOVL (SP),R4 ; Source info from LDIOBR0 MOVAB LDIOB_L_PID(R4),R4 ; Point to trace data9 CLRW LDIOB_W_IOST-LDIOB_L_PID(R4) ; Clear reserved fieldi PUSHL R3 ; Save IRPV PUSHL R5 ; Save UCBG4 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 mutex - NEWDATA=YES ; Check if new data availableR BRB 40$%30$: PUSHL R4 ; Save LDIOB pointer#: MOVL G^CTL$GL_PCB,R4 ; Get current process' PCB address3 UNLOCK_TRACE NEWDATA=YES ; Unlock the trace mutexL( MOVL (SP)+,R4 ; Restore LDIOB pointer340$: MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O statusr MOVL R4,R0 ; Point to LDIOB3) BSBW LD_DEAL_LDIOB ; Get rid of bufferA! MOVQ (SP)+,R0 ; Restore statusI RSB;E; Release spinlock before stalling due to trace buffer unavailabilityD; .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 OPCOMr;f; Functional description:i;iH; 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 requesttJ; 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.;>1; Layout of the stack we use for scratch storage:); #; 0(SP) - FAO control string lengthV$; 4(SP) - FAO control string address; 8(SP) - FAO return lengthn; 12(SP) - SNDOPR buffer lengths ; 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) - Devicenamer; 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;P ; Inputs:t;PJ; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST); 6; 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$t+ MOVL #VBN_WP_LEN,(SP) ; FAO string lengthC3 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 volumeA BRB 20$/10$: MOVL #LBN_WP_LEN,(SP) ; FAO string lengthV3 MOVAB LBN_WP,4(SP) ; FAO conversion format (LBN) 920$: 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 number  .DISABLE FLAGGING4 MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name .ENABLE FLAGGINGC- 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 lista# CMPL (R0),R0 ; Something there?N BEQL 10$ ; Not* MOVL (R0),R0 ; Get adress of first ICB, MOVAB 20(R0),32(SP) ; Point to image name.30$: MOVL #>,- 60(SP) ; OPCOM flagsI 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 bufferA% PUSHAW 16(SP) ; FAO return length+1 PUSHAQ 12(SP) ; FAO control string descriptoru* CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; Trouble / MOVZWL 8(SP),R0 ; Length of converted stringa> ADDL3 #8,R0,12(SP) ; OPCOM message header length + overhead2 MOVAB 12(SP),R0 ; Address of message descriptor) $SNDOPR_S (R0) ; Send message to OPCOM  BLBS R0,50$ ; Okay, CMPL R0,#SS$_MBFULL ; OPCOM mailbox full? BEQL 50$ ; Yes, message lost740$: BUG_CHECK INCONSTATE ; Issue a non-fatal bugcheck2;C; Now get back into kernel mode to deallocate our parameter buffer.N>; We are allowed to go into kernelmode since we're now running; in exec mode; (50$: $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,-# ARGLST=(AP) ; ACB to deallocateT RET .PAGE8 .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;+++3; LD_OPCOM_DEALLOC, dealloc opcom parameter buffer ;L; Functional description:z;i7; We will return the parameter buffer from LD_OPCOM_ASTe ; to pool.; ; Inputs:;;s; 4(AP) - address of ACB; 6; The routine must preserve all registers except R0-R1;---# .CALL_ENTRY LABEL=LD_OPCOM_DEALLOCO;_2 MOVL 4(AP),R0 ; Get old parameterbuffer pointer( JSB G^EXE$DEANONPAGED ; Return to pool RET .PAGE .SBTTL LD_END, End of driverF;+++(; Label that marks the end of the driver;--- UNIVERSAL_SYMBOL LD_END&;LD_END: ; Last location in driver .ENDV%*[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1+, ./@@ 4U-)0123KPWO56os6M70Y89G@@HJ. .TITLE LDDRIVER, VAX/VMS Logical Disk driver .IDENT 'V6.3'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 RES'O $LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UPONSIBILITY 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 25-OCT-1988 Version 01.01; Modified for VMS V5;,; 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 17-FEB-1993 Version 03.00;; Added trace capabilities; Added 'replace' function;,; 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 14-APR-1993 Version 04.00;;; Add support for waiting for tracedata to become available; Add lost tracedata detection;+; Jur van der Burg 8-JUL-1993 Version 04.01;5; Promote bytecount field in tracerecord to longword.;,; 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 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 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 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 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 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.;;---;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 ; 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_TRCMUT(6o$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U"EX(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 lbn ,- ; File access > $DEFEND LDWATCHENT;; Watchpt parameter block;? $DEFINI LDWATCHPT,GLOBAL ; LOGICAL DISK Watch parameter entry5 $DEF LDWATCHPT_L_LBN .BLKL 1 ; Logical block number' $DEF LDWATCHPT_W_FLAGS .BLKW 1 ; Flags4 $DEF LDWATCHPT_W_ACTION .BLKW 1 ; Action to perform- $DEF LDWATCHPT_W_FUNC .BLKW 1 ; Functioncode/ $DEF LDWATCHPT_W_RETCODE .BLKW 1 ; Return code1 $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 ; Seq' $DEF LDWATCHPT_W_FID_RVN .BLKW 1 ; Rvn.LDWATCHPT_K_LENGTH = . ; Length of LDWATCHPT;; Watch parameter block flags; _VIELD LDWATCHPT,0,< -, ,- ; Function characteristics) ,- ; Remove all entries >;; Subfields in CHARS field; _VIELD LDWATCHPT,0,< -' ,- ; Function without lbn ,- ; File accessE >I $DEFEND LDWATCHPT;s; Suspended process list ;*@ $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list+ $DEF LDSUSPLST_L_PID .BLKL 1 ; Process id95 $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block numberO' $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; FlagsL) $DEF LDSUSPLST_W_ACTION .BLKW 1 ; Action ) $DEF LDSUSPLST_W_FUNC .BLKW 1 ; FunctionR/ $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return codeN.LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST;; $SNDOPR parameter list;C5 $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 ; Size ' $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 addressS6 $DEF LDSNDOPRLST_L_LBN .BLKL 1 ; Logical block number) $DEF LDSNDOPRLST_W_FLAGS .BLKW 1 ; FlagsS+ $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Action + $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Function*1 $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return code*1 $DEF LDSNDOPRLST_W_FID_NUM .BLKW 1 ; File ID Num ) $DEF LDSNDOPRLST_W_FID_SEQ .BLKW 1 ; Seq) $DEF LDSNDOPRLST_W_FID_RVN .BLKW 1 ; Rvnl1 $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device namev1LDSNDOPRLST_K_DEVNAM = 64 ; Device name length 2LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST;v$; LD file lockvalueblock definitions;t3 $DEFINI LDFLVB,GLOBAL ; LD file lockvalue block, $DEF LDFLVB_W_CYLINDERS .BLKW 1 ; Cylinders' $DEF LDFLVB_B_TRACKS .BLKB 1 ; TracksP( $DEF LDFLVB_B_SECTORS .BLKB 1 ; Sectors5 $DEF LDFLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumbere1 $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;; )X9$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U"%Subfields in FLAGS field;o _VIELD LDFLVB,0,< -$ ,- ; Shared accessable >e $DEFEND LDFLVBo;a&; LD device lockvalueblock definitions;f3 $DEFINI LDDLVB,GLOBAL ; LD file lockvalue blockk) $DEF LDDLVB_W_FID .BLKW 1 ; File ID Numr& $DEF LDDLVB_W_SEQ .BLKW 1 ; File Seq& $DEF LDDLVB_W_RVN .BLKW 1 ; File Rvn1 $DEF LDDLVB_B_ALLOCLS .BLKB 1 ; Allocation classa* $DEF LDDLVB_W_UNIT .BLKW 1 ; Unit number+ $DEF LDDLVB_T_DEVNAM .BLKB 7 ; Devicename *ASSUME . LE 16 ; Length must be <= 16;t#; Definitions for our functioncodesk;u- IO$_LD_CONTROL = 20 ; Main control functionl ; (Physical I/O function)E' LDIO_CONNECT = 0 ; Connect drive/filea- LDIO_DISCONNECT = 1 ; Disconnect drive/fileu% LDIO_ENABLE_TRACE = 2 ; Enable trace1' LDIO_DISABLE_TRACE = 3 ; Disable tracel$ 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 device% LDIO_ENABLE_WATCH = 8 ; Enable watcht' LDIO_DISABLE_WATCH = 9 ; Disable watcha' LDIO_GET_WATCH = 10 ; Get watchpointsL, 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 classu LDIO_V_FUNC = 0  LDIO_S_FUNC = 8 ;n; Function modifiers;t $DEFINI LDIO,GLOBAL _VIELD LDIO,8,<- " ,- ; Replace drive# ,- ; Abort disconnect$ ,- ; Get buffer size, ,- ; Don't wait for tracedata3 ,- ; Reset after retrieving tracedata ,- ; Shared access > $DEFEND LDIOi;c; Watchpoint actions;s* 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;d e;+++; Definitions for IRP fields;--- $DEFINI IRP,GLOBAL ; IRPr .=IRP$K_LENGTHt/ $DEF IRP$L_LD_LDUCB .BLKL 1 ; Logical Disk UCB / $DEF IRP$L_LD_LDIOB .BLKL 1 ; Logical disk IOBn6 $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 IRPe $DEFEND IRP;+++1; Definitions that follow the standard UCB fields-;---0 $DEFINI UCB,GLOBAL ; Start of UCB definitions3 .=UCB$K_MSCP_DISK_LENGTH ; Position at end of UCBk0 $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk; $DEF UCB$L_LD_AIOFL .BLKL 1 ; Active I/O list forward linkd< $DEF UCB$L_LD_AIOBL .BLKL 1 ; Active I/O list backward link)ASSUME UCB$L_LD_AIOBL EQ UCB$L_LD_AIOFL+4p2 $DEF UCB$L_LD_IOBFL .BLKL 1 ; LDIOB free queue FL2 $DEF UCB$L_LD_IOBBL .BLKL 1 ; LDIOB free queue BL8 $DEF UCB$L_LD_IRPFL .BLKL 1 ; Forward IRP free queue FL8 $DEF UCB$L_LD_IRPBL .BLKL 1 ; Forward IRP free queue BL1 $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointerh1 $DEF UCB$L_LD_WCB .BLKL 1 ; Window control blocke) $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORBh0 $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+4e $DEF UCB$Q_LD_PD_LKSB .BLKB 8$ ; Phys. dev. lock status block $DEF UCB$A_LD_PD_LVB .BLKB 16r ; + lock value block! $DEF UCB$T_LD_PD_RESNAM .BLKB 28% ; Phys. dev. lock resource namet $DEF UCB$Q_LD_FILE_LKSB .BLKB 8 ; Lock status blockq $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 8i ; Lock status blocko $DEF UCB$A_LD_DEV_LVB .BLKB 16i ; + lock value block" $DEF UCB$T_LD_DEV_RESNAM .BLKB 402 ; Device resource name 32 bytes + descriptor! $DEF UCB$L_LD_TRCWAITQFL .BLKL 1I* ; Trace data wait queue forward link! $DEF UCB$L_LD_TRCWAITQBL .BLKL 1s+ ; Trace data wait queue backward links" $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1+ ; Trace mutex wait queue forward linki" $DEF UCB$L_LD_TRCMUTEXQBL .BLKL 1, ; Trace mutex wait queue backward link1 $DEF UCB$L_LD_TRCPC .BLKL 1 ; Fork trace PC save  $DEF UCB$L_LD_TRCMUTEX .BLKL 1g ; 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 16" ; Allocated tracebuffer size $DEF UCB$L_LD_TRCBUFPTR .BLKL 1 ; Tracebuffer pointerl $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 1f ; Watch queue forward link $DEF UCB$L_LD_WATCHQBL .BLKL 1c! ; Watch queue backward link $DEF UCB$L_LD_WATCHCNT .BLKL 1c ; Watch queue entry count" $DEF UCB$L_LD_WATCHPNDQFL .BLKL 1( ; Watch pending queue forward link" $DEF UCB$L_LD_WATCHPNDQBL .BLKL 1) ; Watch pending queue backward linkd, $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 process-- $DEF UCB$W_LD_CHARGE .BLKW 1 ; Charge amount ) $DEF UCB$W_LD_FLAGS .BLKW 1 ; Flags byteA $VIELD UCB,0,< -0 , - ; Conn./Disconn. status bit. ,- ; Replace mode status bit. ,- ; Connected to DECRAM disk$ ,- ; Write protect' ,- ; Shared accessabley2 ,- ; Containerfile on volumeset' ,- ; DSE not supportedr4 ,- ; Virtual I/O to container file4 ,- ; Forkblock busy (internal only)3 ,- ; Disc. pending (internal only)$ >F; ; Filename string buffer + $DEF UCB$K_LD_UCBLEN ; Length of new UCBe( $DEFEND UCB ; End of UCB definitions a .SBTTL Standard tablesn;+++; Driver prologue tablea;--- .IF DF V64 DPT_FLAGS= ; SMP safe! ; Driver supports snapshotsC ; if not in a cluster$ .IFF % DPT_FLAGS= ; SMP safeo .ENDC DPTAB - ; DPT-creation macro& END=LD_END,- ; End of driver label! ADAPTER=NULL,- ; Adapter typey- UCBSIZE=,- ; Length of UCBn, 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 IPLw: DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device! DEV$M_ODV!- ; output devicef" DEV$M_FOD!- ; files oriented( DEV$M_DIR!- ; directory structured DEV$M_AVL!- ; available6 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)e) DEV$M_CDP!- ; To prevent MSCP servingA/ DEV$M_MSCP> ; Fake to get shadowing to work,: DPT_STORE UCB,UCB$W_STS,W,UCB$M_TEMPLATE; Template device; DPT_STORE UCB,UCB$L_LD_TRCBUF,L,0 ; Pointer to tracebuffer 7 DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device classRA DPT_STORE UCB,UCB$B_DEVTYPE,B,DT$_FD1 ; Dev. type = foreign diskE: DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,512 ; Default buffer size5 DPT_STORE UCB,UCB$W_DEVSTS,W,- ; Inhibit logical toH' ; physical xlation. % DPT_STORE REINIT ; Start of reloadJ ; initialization table2 DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT+ DPT_STORE CRB,- ; Address of controller 5 CRB$L_INTD+VEC$L_INITIAL,- ; initialization routineO D,LD_CONTROL_INIT?' DPT_STORE CRB,- ; Address of device,3 CRB$L_INTD+VEC$L_UNITINIT,- ; unit initializationT D,LD_UNIT_INIT ; routine) DPT_STORE END ; End of initialization;+++; Driver dispatch table(;--- DDTAB - ; DDT-creation macro DEVNAM=LD,- ; Name of device' START=LD_START,- ; Start I/O routine& FUNCTB=LD_FUNCTABLE,- ; FDT address* CANCEL=LD_CANCEL,- ; Cancel I/O routine/ UNITINIT=LD_UNIT_INIT,- ; Unit init. rout*$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1Up"6inee/ CLONEDUCB=LD_CLONED_UCB ; Cloned UCB routineN E;+++; Function decision tableL;--- UNIVERSAL_SYMBOL LD_FUNCTABLE#;LD_FUNCTABLE: ; FDT for drivert$ FUNCTAB ,- ; Valid I/O functions ; LD control functions' FUNCTAB ,- ; Buffered I/O functionsM ; LD control functions? FUNCTAB LD_FDT_SHAD_WCHECK,<- ; Check write to shadow set mbr1% WRITELBLK,- ; Write LOGICAL BlockB& 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 functiono! DSE> ; Data Secutiry Erase;+ FUNCTAB +ACP$ACCESS,- ; Access functions.! ; Create file/dir/ FUNCTAB +ACP$DEACCESS,- ; Deaccess functionsm ; Deaccess file+ FUNCTAB +ACP$MODIFY,- ; Modify functions! ; Modify file attributesD) FUNCTAB +ACP$MOUNT,- ; Mount functionsR ; Mount volume$9 FUNCTAB +EXE$LCLDSKVALID,- ; Local disk valid functions. ; Pack acknowledge 5 FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functions  ; Available3 FUNCTAB +EXE$ONEPARM,- ; One parameter functions1 ; Seek, FUNCTAB +EXE$SENSEMODE,- ; Sense functions* ; Sense modee) FUNCTAB +EXE$SETCHAR,- ; Set functionse& ; Set modeC: FUNCTAB LD_FDT_CRESHAD,- ; Create shadowset virtual unit o4 FUNCTAB LD_FDT_REMSHAD,- ; Remove shadowset member C5 FUNCTAB LD_FDT_CONTROL,- ; General LDdriver control; ;g ; Local data;E .SAVE_PSECT .PSECT $$$110_LD_DATA,LONG,WRTd;o UNIVERSAL_SYMBOL LD_REFCNTL ;LD_REFCNT:s% .LONG 0 ; Number of active devicesP;.FLBN_WP: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\ .ASCII \PID: !XL!/\_ .ASCII \Image: !AC!/\c .ASCII \Device: !AC!/\C .ASCII \Function: !XW!/\i .ASCII \LBN: !UL\_LBN_WP_LEN=.-LBN_WPi;IFVBN_WP: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\ .ASCII \PID: !XL!/\K .ASCII \Image: !AC!/\D .ASCII \Device: !AC!/\  .ASCII \Function: !XW!/\D .ASCII \VBN: !UL!/\,! .ASCII \File id: (!UW,!UW,!UW)\RVBN_WP_LEN=.-VBN_WPm; 4NONESTR: .ASCIC /None/ ; If no imagename available<OPCOM_NAME: .ASCIC /OPCOM/ ; Process name of OPCOM process .RESTORE_PSECT,;-; End of driver data; @ .SBTTL LD_FDT_CONTROL, general control and dispatch FDT routine;+++:; LD_FDT_CONTROL, general control and dispatch FDT routine; ; Functional description:E;D9; This routine is invoked via an IO$_LD_CONTROL function.C=; We will dispatch to the various other routines according to;; the P6 parameter.$; ; Inputs:_;C; R0-R2 - scratch registersE.; 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)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>; P6(AP) - Address of longword with functioncode and modifiers; ; Outputs:;;; The routine must preserve all registers except R0-R2, andh ; R9-R11. ;L;--, UNIVERSAL_SYMBOL LD_FDT_CONTROL1;LD_FDT_CONTROL: ; General Control FDT routineE# MOVZWL P6(AP),R0 ; Get parameter 1 MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRPL ASSUME LDIO_S_FUNC EQ 89 DISPATCH R0,TYPE=B,<- ; Dispatch according to functionP ,-E$ ,-( ,-* ,-" ,-& ,-, ,-! ,-t( ,-* ,-" ,-( ,-) ,- , ,-. ,-) >E: 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 routineL;+++?; LD_GET_CONNECTION, Get connection characteristics FDT routineE;D; Functional description:F;tF; This routine returns the full filename string to the callers buffer,-; specified via the descriptor address in P1.N; 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 diskL; 1 : DECRAM diskl; bit 3 = 0 : Normal access.; 1 : Write protectedB;A ; Inputs:1;T; R0-R2 - scratch registersS.; 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 stringD:; P2(AP) - Size of buffer to receive the devicename stringA; P3(AP) - Address of buffer to receive file-id of connected file ;K ; Outputs:;;; The routine must preserve all registers except R0-R2, andL ; R9-R11..;W;--U# UNIVERSAL_SYMBOL LD_GET_CONNECTIONL3;LD_GET_CONNECTION: ; Get Connection FDT routine % MOVL P1(AP),R0 ; Address of bufferc% MOVL P2(AP),R1 ; Get buffer length * 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 writeable_3 MOVZWL #SS$_DEVINACT,R0 ; Assume already inactivet" 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 databasev PUSHR+i8$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U,"G #^MW! MOVL R2,R5 ; Copy ucb addressA! MOVZBL #1,R4 ; DVI$_ALLDEVNAMI% MOVL P1(AP),R1 ; Address of bufferL! MOVZWL P2(AP),R0 ; Buffer sizeh1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicenameu POPR #^M+ PUSHR #^M ; Save across unlocko. JSB G^SCH$IOUNLOCK ; Unlock the IO database. POPR #^M ; Restore IRP and status BLBC R0,20$ ; Trouble * 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 bufferW MOVW UCB$W_LD_FID_RVN(R5),(R2)e10$: 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 r0 .SBTTL LD_GET_TRACE, Get trace data FDT routine;+++*; LD_GET_TRACE, Get trace data FDT routine;W; Functional description:u;c:; 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;g ; Inputs:O;; R0-R2 - scratch registers .; 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)-*; 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 dataB;L ; Outputs:;L; R0 - I/O status ; R1 - Number of returned bytese;u;; The routine must preserve all registers except R0-R2, and$ ; R9-R11.D;P;--L 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 - 1F0 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; ; 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)?;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 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 r,it$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U"Xequest 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$- MOVL R1,UCB$L_LD_WCB(R5) ; Save WCB addressc7 MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical device_! MOVL UCB$L_VCB(R1),R2 ; Get VCBe! MOVL VCB$L_AQB(R2),R0 ; Get AQB ; CMPB AQB$B_ACPTYPE(R0),#AQB$K_F11V2 ; Serviced by F11BXQP?  BNEQ 27$ ; No. TSTW VCB$W_RVN(R2) ; Relative volume number) BEQL 25$ ; Branch if not a volume set+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT address$ 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 address 0 MOVL 4(R1),R0 ; Get devicename string address1 MOVZWL (R1),R1 ; Get devicename string lengthF3 JSB G^EXE$WRITECHK ; Check if string is readable4 PUSHR #^M ; Save some registers4 JSB G^SCH$IOLOCKR ; Lock the IO database for read/ MOVL P1(AP),R1 ; Get devicename descr. addr.f- MOVL #IOC$M_ANY,R2 ; Set the SEARCHALL bitr* CLRL R3 ; No SB specified (local only), JSB G^IOC$SEARCH ; Search the IO database5 POPR #^M ; Restore the registerso! BLBC R0,90$ ; Return on error_= MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. diskc550$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid deviceL7 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...C/90$: PUSHR #^M ; Save across unlockT. JSB G^SCH$IOUNLOCK ; Unlock the IO database1 POPR #^M ; Restore the return statusF BRW 140$-100$: BBC #LDIO_V_REPLACE,- ; Replace drive?t IRP$L_EXTEND(R3),110$c/ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCBe; 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 statuso0 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 exitf BRW 130$ ; Continue 4110$: IFRD #4,P2(AP),120$ ; Check if size readable+ MOVZWL #SS$_ACCVIO,R0 ; Access violation  BRB 140$ ; Abort;120$: BSBW LD_SET_GEOMETRY ; Fill in geometry informationn BLBC R0,140$ ; Quit on error' MOVL P1(AP),R9 ; Get the SBK addressi- MOVL SBK$L_FCB(R9),R9 ; Get the FCB address: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/OL-140$: JMP G^EXE$ABORTIO ; And abort the I/O- ;5 .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometryC;+++/; LD_SET_GEOMETRY, Setup pseudo device geometryI;N; Functional description:>;;; This routine will set the geometry for the pseudo device.,;_ ; Inputs:-; ; R0-R2 - scratch registersR.; 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); 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 Tracks>; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders;L ; Outputs:;t ; R0 - statusn;o;; The routine must preserve all registers except R0-R2, andG ; R9-R11.O;G;--n! UNIVERSAL_SYMBOL LD_SET_GEOMETRYL;LD_SET_GEOMETRY:N# MOVL P1(AP),R2 ; Get SBK addressc- MOVL SBK$L_FCB(R2),R0 ; Get the FCB address8 MOVL FCB$L_EFBLK(R0),R0 ; Get maximum number of blocks MOVL R0,R2 ; Also in R2 MOVL P2(AP),R1 ; Get the size" BNEQ 10$ ; Available, check it# MOVL R0,R1 ; Use allocated size BRB 20$!10$: CMPL R1,R0 ; Not too big?r BLEQU 20$ ; Valid, use ite( MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 120$; 5; At this point R1 is the maximum logical blocknumber ; ,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 zeroesa BISL2 R11,R0* BEQL 80$ ; 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.c;n TSTL R9 ; Tracks zero? BNEQ 30$ ; NoO INCL R9 ; Make 1%30$: CMPL R9,#256 ; Within bounds?r BGEQU 70$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; Noi INCL R10 ; Make 1t%40$: CMPL R10,#256 ; Within bounds?v BGEQU 70$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; Noc INCL R11 ; Make oneA'50$: CMPL R11,#65536 ; Within bounds?e BGEQU 70$ ; No0 MULL3 R9,R10,R0 ; Calculate total size needed MULL2 R11,R02+ CMPL R0,R2 ; Not past allocated blocks?O BGTRU 70$ ; Not validE) MOVB R9,UCB$B_TRACKS(R5) ; Setup tracksn, 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 dictateds ; by T/S/C;TJ; 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.$;D60$: CMPL R1,R0G BLEQU 110$e670$: 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 check)/ MOVB R1,UCB$B_SECTORS(R5) ; File size sectorst* MOVW #1,UCB$W_CYLINDERS(R5) ; 1 cylinder BRB 110$ ; Common code paths390$: CMPL R1,#65536 ; Smaller then 65536 blocks ?e BGEQ 120$ ; No, next check' MOVB #2,UCB$B_SECTORS(R5) ; 2 sectorsl) ASHL #-1,R1,R1 ; Divide file size by 2e6 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 blocksb BRW 140$V9120$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Save number of blocksI' 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 greatercH; than maxblock. Use the same algorithm as DUDRIVER so that disks served>; in a cluster appear to have the same geometry on-9$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1Ug|"i every node.; 8130$: MOVZBL UCB$B_TRACKS(R5),R1 ; Get number of tracks5 MOVZBL UCB$B_SECTORS(R5),R0 ; Get number of sectorsh MULL R1,R0 ; Multiplyb3 MOVL UCB$L_MAXBLOCK(R5),R1 ; Get number of blocksr) CLRL R2 ; Prepare for extended dividep> EDIV R0,R1,R0,R1 ; Calculate number of cylinders, remainder- CMPL R0,#65534 ; Is cylinder number legal?6( BGTR 150$ ; If so go try a crude fix8 MOVW R0,UCB$W_CYLINDERS(R5) ; Save number of cylinders TSTL R1 ; Zero remainder?o BEQL 140$ ; Branch if so; INCW UCB$W_CYLINDERS(R5) ; Otherwise, increment cylindersQ* ; (tracks * sectors * cylinders must ; be >= maxblock)140$: MOVZWL #SS$_NORMAL,R0r 145$: RSBa;dJ; Out of line code to handle really large geometries to the limits of what; VMS can currently do.t;e%; First method can waste 1024 blocks.L; Second method can waste 9216 blocks. Third can waste 65025. This is rather:; crude, but will at least allow the structure to be used.;sD; 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 zeroes0G; in trk or sect and will have filled in ^X604 above, we have four fake ; geometries in all:; 6 x 4 x n_; 32 x 32 x nh; 96 x 96 x n0; 255 x255 x n;VB; 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)$ MOVB #32,UCB$B_SECTORS(R5)6 CMPL UCB$L_MAXBLOCK(R5),- ; Does 32 by 32 by n work? #<65534*32*32> BLSSU 130$D> 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 oka;I); If disk is over 300Gb, try to allow 2TB ;PF; 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.; + MOVB #255,UCB$B_TRACKS(R5) ; Go for brokee MOVB #255,UCB$B_SECTORS(R5)+ BRW 130$ ; This is as big as we can go.d r5 .SBTTL LD_SET_SEED, Set seed unit number FDT routinet;+++/; LD_SET_SEED, Set seed unit number FDT routine ;O; Functional description:d;s:; This routine sets the seed number to the specified value;a ; Inputs: ;_; R0-R2 - scratch registersf.; 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)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) - seed valuee;d ; Outputs:;E;; The routine must preserve all registers except R0-R2, andu ; R9-R11.i;;--5 UNIVERSAL_SYMBOL LD_SET_SEED ;LD_SET_SEED:  MOVL P1(AP),R0 ; Get valueT 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 UCB0* MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/Ot%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;D; Functional description:t;MA; This routine is the FDT routine to set the allocation class forw@; the LD devices. This can only be done when no other LD devicesA; are active. If that's not the case and the specified allocationC@; class is the same as the one already set we will ignore it and; return success.E;K ; Inputs:;; R0-R2 - scratch registersS.; 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)C*; 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:;S;; The routine must preserve all registers except R0-R2, andR ; R9-R11.T;R;--)" UNIVERSAL_SYMBOL LD_SET_ALLOCLASS;LD_SET_ALLOCLASS: MOVL P1(AP),R2 ; Get value_ CMPL R2,#255 ; Limit range 0 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 it. TSTW LD_REFCNT ; Already someone connected? BNEQ 30$ ; Yes, not allowedi- FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronizem SAVIPL=-(SP),-  PRESERVE=NO; MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDBa+ MOVL UCB$L_CDDB(R5),R1 ; Get address CDDB;= 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' JMP G^EXE$FINISHIOC ; Finish the I/OS%20$: MOVL #SS$_BADPARAM,R0 ; Troubleu BRB 40$:30$: MOVZWL #SS$_UNSAFE,R0 ; No devices may be connected(40$: JMP G^EXE$ABORTIO ; Abort the I/O s5 .SBTTL LD_DISABLE_TRACE, Disable tracing FDT routine ;+++/; LD_DISABLE_TRACE, Disable tracing FDT routinel;h; Functional description:i;i?; This routine disables tracing and deallocates the tracebuffers; ; Inputs: ;c; R0-R2 - scratch registerse.; 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)n*; 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:;i;; The routine must preserve all registers except R0-R2, andc ; R9-R11.w; ;-- " 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 pointer#9 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 quotaC BRB 25$ 20$: PUSHL R4o! MOVL R0,R1 ; Amount to returnB" 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 mutexa" MOVZWL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/O ,30$: JMP G^EXE$ABORTIO ; And abort the I/O T3 .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routine;;+++-; LD_ENABLE_TRACE, Enable tracing FDT routineD;o; Functional description: ;n;; This routine allocates a tracebuffer and enables tracing r;i ; Inputs:n; ; R0-R2 - scratch registersl.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)r.; 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;e ; Outputs:;c;; The routine must preserve all registers except R0-R2, and ; R9-R11.-;d;--o! UNIVERSAL_SYMBOL LD_ENABLE_TRACEi;LD_ENABLE_TRACE:t7 MOVZWL #SS$_TOOMUCHDATA,R0 ; Assume trace already sett2 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)a BEQL 10$ ; Nothing??@ MULL2 #LDTRCENT_K_LENGTH,R1 ; Length of LDTRCENT * #of entries0 MOVL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Save used size;hK; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to beSJ; able to deallocate the packet as a forkblock when the driver is reloaded;C& 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 lefts/ MOVZWL #SS$_EXBYTLM,R0 ; Ou.3$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UÖ"zt of bytlim quota$10$: BRW 30$ ; Get oute(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 wrappedc. 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 mutexs MOVZWL #SS$_NORMAL,R0' JMP G^EXE$FINISHIOC ; Finish the I/O ,30$: JMP G^EXE$ABORTIO ; And abort the I/O B5 .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routine$;+++/; LD_RESET_TRACE, Reset tracebuffer FDT routineC; ; Functional description:i;B-; This routine resets the tracebuffer pointer;C ; Inputs:R;3; 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;C ; Outputs:; ;; The routine must preserve all registers except R0-R2, andC ; R9-R11. ;G;--o UNIVERSAL_SYMBOL LD_RESET_TRACE;LD_RESET_TRACE:- MOVZWL #SS$_NODATA,R0 ; Assume not yet set02 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 wrappedp. 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/O;,10$: JMP G^EXE$ABORTIO ; And abort the I/O h/ .SBTTL LD_ENABLE_PROTECT, Enable write protecte;+++*; LD_ENABLE_PROTECT, Enable write protect;h; Functional description: ; <; This routine enables write-protection on the logical disk.; ; Inputs:P; ; 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)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;B ; Outputs:;s;; The routine must preserve all registers except R0-R2, andu ; R9-R11.B;S;--U# UNIVERSAL_SYMBOL LD_ENABLE_PROTECTP3;LD_ENABLE_PROTECT: ; Enable protect FDT routine11 BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bite* BISW2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$W_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC e1 .SBTTL LD_DISABLE_PROTECT, Disable write protectc;+++,; LD_DISABLE_PROTECT, Disable write protect; ; Functional description:m;t=; This routine disables write-protection on the logical disk. ;$ ; Inputs:2;4; R0-R2 - scratch registers .; 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 - 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;M ; Outputs:;9;; The routine must preserve all registers except R0-R2, andM ; R9-R11.f; ;--r$ UNIVERSAL_SYMBOL LD_DISABLE_PROTECT5;LD_DISABLE_PROTECT: ; Disable protect FDT routineh1 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,R0 JMP G^EXE$FINISHIOC p/ .SBTTL LD_DEALLOC_TRCBUF, Trace buffer releasei;+++*; LD_DEALLOC_TRCBUF, Trace buffer release;e; Functional description:u;-;; This routine deallocates the tracebuffer if we disconnect ;; from the physical device (in case of cloned device) or inr; 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). ; ; Inputs: ;n1; R4 - address of the PCB (process control block)o#; - 0 if called from LD_DRV_UNLOAD .; R5 - address of the UCB (unit control block);- ; Outputs:;n;; The routine must preserve all registers except R0-R2, andu ; R9-R11. ; ;--t# UNIVERSAL_SYMBOL LD_DEALLOC_TRCBUFG;LD_DEALLOC_TRCBUF:M+ MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get addressV BNEQ 10$ ; Buffer available BRW 60$ 10$: TSTL R4 ; PCB available?) BEQL 20$ ; No, no need to synchronizeu! ; (Called by LD_DRV_UNLOAD)s- LOCK_TRACE ACCESS=WRITE ; Lock trace bufferE* CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get ownerP UNLOCK_TRACEt BRB 30$.20$: CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get ownert#30$: MOVQ R3,-(SP) ; Save R3 + R4l/ MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get sizer' SUBL2 #12,R0 ; Account for overheade" ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1- MOVW #>,- ; and proper spinlock;1 FKB$B_TYPE(R0) ; for this to be a fork block  PUSHL R5 ; Save UCB) MOVL R0,R5 ; Copy addressy& PUSHAB 40$ ; Set up return address FORK ; Create fork % MOVL R5,R0 ; Deallocate the blockI" MOVL FKB$L_FR3(R5),R1 ; Get size" JMP G^EXE$DEANONPGDSIZ ; Get out*40$: MOVL (SP)+,R5 ; Restore UCB pointer" MOVQ (SP)+,R3 ; Restore R3 + R4 TSTL R4 ; PCB available?# BEQL 60$ ; No, no one to creditTB ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested+ CMPL PCB$L_PID(R4),R1 ; Are we the owner?s 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 returni% BSBW LD_RETURN_QUOTA ; Credit usert MOVL (SP)+,R460$: RSB (1 .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer release ;+++,; LD_DEALLOC_WATCHBUF, Watch buffer release;B; Functional description:i;d<; This routine deallocates all watchbuffers if we disconnect;; from the physical device (in case of cloned device) or inh; case of driver reload.;iF; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).U;0 ; Inputs:M;W1; R4 - address of the PCB (process control block)1#; - 0 if called from LD_DRV_UNLOAD .; R5 - address of the UCB (unit control block);e ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.e;r;--M% UNIVERSAL_SYMBOL LD_DEALLOC_WATCHBUFr;LD_DEALLOC_WATCHBUF:  TSTL R4 ; PCB available?) BEQL 50$ ; No, no need to synchronize 5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level  SAVIPL=-(SP) 110$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entryC 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$ ; No01 MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to returne- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaT BRB 30$ 20$: PUSHL R4S MOVL R1,R4 ; Get pid0 MOVL #LDWATCHPT_K_LENGTH,R1 ; Amount to return3 BSBW LD_RETURN_QUOTA ; Return to correct processt MOVL (SP)+,R4,30$: MOVL (SP)+,R0 ; Restore bufferpointer' JSB G^COM$DRVDEALMEM ; Dealloc memoryc 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 o7 .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routiner;+++1; LD_ENABLE_WATCH, Enable watchpoints FDT routine ;b; Functional description: ;s/; This is the FDT routine to enable watchpoints); ; Inputs: ;c; R0-R2 - scratch registersr.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)r.; 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;n ; Outputs:;h;; The routine must preserve all registers except R0-R2, andt ; R9-R11.;;--e! UNIVERSAL_SYMBO/p$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U^"L LD_ENABLE_WATCHr;LD_ENABLE_WATCH: ! CLRL R9 ; Nothing charged yeti& 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 sizea4 JSB G^EXE$WRITECHK ; Check buffer for read access) MOVL P2(AP),R2 ; Get number of entriesk# MOVL R2,IRP$L_OBCNT(R3) ; Save it ;_; Validate inputbuffer;x;10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file modeo LDWATCHPT_W_FLAGS(R0),60$r5 TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBNi BEQL 40$u PUSHR #^Mo3 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 addressT6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return errorU$ TSTW FCB$W_REFCNT(R1) ; File open? BEQL 20$ ; No, error3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too big  FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quit-& MOVL FCB$L_WLFL(R1),R1 ; Get any wcb7 CMPL R5,WCB$L_ORGUCB(R1) ; File must be on our device ! BNEQ 50$ ; Other device, quit 3 BBC #WCB$V_COMPLETE,- ; Must be completely mappedB WCB$B_ACCESS(R1),20$! MOVL UCB$L_VCB(R5),R1 ; Get VCBd. TSTW VCB$W_RVN(R1) ; Relative volume number+ BEQL 70$ ; Branch if a not a volume setnE MOVZWL #SS$_NOTVOLSET,R0 ; File watchpoint on volumeset not allowedi BRW 130$ C20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mappedc BRW 130$f-30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameter BRW 130$e,40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$h750$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our devicen BRW 130$b@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 bige UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quite070$: 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?d BEQL 120$ ; Yes, no priv9 CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device?U& BNEQ 110$ ; No, action not allowed;AC; We charge seperately for every packet. We need to do this becauseVG; 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.T;u(80$: PUSHL R0 ; Save, destroyed later4 MOVL #LDWATCHENT_K_LENGTH,R1 ; Bytecount to charge- ADDL2 R1,R9 ; Keep track of total chargede. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf lefts ADDL2 #4,SP ; Adjust stack/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quotat BRB 130$uC90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input entrya DECL R2 ; Next packete 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$i5120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv requiredl&130$: TSTL R9 ; Any BYTCNT charged? BEQL 140$ ; No PUSHL R0 ; Save status! MOVL R9,R0 ; Amount to returnL- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quotaM! MOVL (SP)+,R0 ; Restore status )140$: JMP G^EXE$ABORTIO ; Abort the I/Ou f9 .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routine;+++3; LD_DISABLE_WATCH, Disable watchpoints FDT routineO;a; Functional description: ;l0; This is the FDT routine to disable watchpoints;o ; Inputs:L; ; R0-R2 - scratch registersr.; 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 structures&; P2(AP) - Count of watchpt structures; ; Outputs:;0;; The routine must preserve all registers except R0-R2, ande ; R9-R11.e;n;--d" UNIVERSAL_SYMBOL LD_DISABLE_WATCH;LD_DISABLE_WATCH:$ CLRL IRP$L_OBCNT(R3) ; Zero count( MOVL P2(AP),R1 ; Something specified? BEQL 10$ ; Remove allh# MOVL R1,IRP$L_OBCNT(R3) ; Save ith2 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$ ; Errore010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O$20$: JMP G^EXE$ABORTIO ; Abort I/O o5 .SBTTL LD_GET_WATCH, Get watchpoint info FDT routine-;+++/; LD_GET_WATCH, Get watchpoint info FDT routineR;-; Functional description:u;t8; This routine retrieves info about current watchpoints,(; as well as the suspended process list.;S ; Inputs:S;E; R0-R2 - scratch registersM.; 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.; P1(AP) - Address of watchpt structure buffer*; P2(AP) - Size in bytes of watchpt buffer;U ; Outputs:;I;; The routine must preserve all registers except R0-R2, and) ; R9-R11.o;i;--s UNIVERSAL_SYMBOL LD_GET_WATCH;LD_GET_WATCH:+ BBS #LDIO_V_INQUIRE,- ; Return list size?P IRP$L_EXTEND(R3),10$& MOVL P1(AP),R0 ; Get buffer address% MOVL P2(AP),R1 ; Get buffer length 4 JSB G^EXE$READCHK ; Check buffer for write access% BSBW LD_GETBUF1 ; Get systembuffer BLBC R0,20$ ; ErrorU010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O H .SBTTL LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;+++B; LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;t; Functional description:r;i:; This routine resumes threads which were suspended when a; 'suspend' watchpoint was hit;0 ; Inputs:c;e; R0-R2 - scratch registersh.; 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)f*; 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;U ; Outputs:; ;; The routine must preserve all registers except R0-R2, andA ; R9-R11.m;r;--l! UNIVERSAL_SYMBOL LD_RESUME_WATCH5;LD_RESUME_WATCH:p& MOVL P1(AP),R0 ; Get buffer address) MOVL P2(AP),R1 ; Get number of entriesr2 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$ ; ErrorN010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O A= .SBTTL LD_GETBUF + LD_GETBUF1, Get and fill temporary buffer ;++++; LD_GETBUF, Get and fill temporary buffer_#; LD_GETBUF1, Get temporary buffer ; ; Functional description:B;MA; This routine allocates a buffer to use to get/send data to/fromJC; start I/O routines. The userbuffer data will be copied (LD_GETBUF#; only).;a ; Inputs:m;-; 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)p.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)b*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers;r ; Outputs:;V ; R0 - status,; R2 - sytem buffer addressF$; IRP$L_SVAPTE - systembuffer addres; ;; The routine must preserve all registers except R0-R2, andb ; R9-R11.o;--- .ENABLE LSB UNIVERSAL_SYMBOL LD_GETBUF1 ;LD_GETBUF1:( CLRL -(SP) ; Flag not to fill buffer BRB 10$ UNIVERSAL_SYMBOL LD_GETBUFC ;LD_GETBUF:( MOVZBL #1,-(SP) ; Flag to fill buffer210$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^Ms7 JSB G^EXE$DEB0vb$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U"IT_BYTCNT_ALO ; Allocate a system buffer $ BLBC R0,30$ ; Any quota errors ? POPR #^M7 MOVL R2,IRP$L_SVAPTE(R3) ; Save system buffer addressd4 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 ; OkayW BRB 40$%30$: POPR #^M ; Error return)"40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSB  t7 .SBTTL LD_MAKE_FILE_RESNAM, Form private resource nameL;+++2; LD_MAKE_FILE_RESNAM, Form private resource name;J; Functional description:s;h@; Form resourcename for lock to coordinate clusterwide access to&; logical disk file or replaced device;+; For a file this resourcename consists of:e;; 9 bytes: '$LOGDISK_'i); 1 byte: 1 for private mounted volume,e; 2 for system wide; 12 bytes: volume lockname ; 3 bytes: 0i; 1 word: FIDd; 1 word: SEQI; 1 word: RVN); ; For a device this will be:;c; 9 bytes: '$LOGDISK_'d ; 1 byte: 0&; 1 byte: physical devicename length!; 20 bytes: alloclass devicename;7 ; Inputs:e;f; R3 IRP address-; R4 PCB addressr; R5 UCB address-&; R9 FCB address (connect 'file' only);s; Outputs:None.e;Q3; Implicit outputs: Resource name in UCB is writtenp;e;---% UNIVERSAL_SYMBOL LD_MAKE_FILE_RESNAM1;LD_MAKE_FILE_RESNAM:L PUSHR #^MPC MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource name_! MOVZBL #31,(R1)+ ; Fill lengthe" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"  MOVL #^A/DISK/,(R1)+E MOVB #^A/_/,(R1)+( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; Fillere PUSHL R4 ; Save PCBl# PUSHL R1 ; Save current pointers- CLRB (R1)+ ; Length byte, filled in later$+ JSB G^SCH$IOLOCKR ; Lock the IO database  CLRQ (R1) ; Clear buffer CLRQ 8(R1)c CLRL 16(R1) MOVZBL #20,R0 ; Buffer sizel! MOVZBL #1,R4 ; DVI$_ALLDEVNAM  PUSHL R5 ; Save UCB65 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCBb1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicenameo 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 mountedI6 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'O20$: MOVB R2,(R1)+! MOVL UCB$L_VCB(R0),R2 ; Get VCB$. TSTW VCB$W_RVN(R2) ; Relative volume number) BEQL 30$ ; Branch if not a volume set++ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT address BEQL 30$ ; Not there5 MOVAB RVT$T_VLSLCKNAM(R0),R2 ; Use this as lockname  BRB 40$730$: MOVAB VCB$T_VOLCKNAM(R2),R2 ; Lock name from here 040$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+  MOVL (R2)+,(R1)+c& CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+I/ 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 oA .SBTTL LD_MAKE_DEV_RESNAM, Form private resource name for devicee;+++<; LD_MAKE_DEV_RESNAM, Form private resource name for device;D; Functional description:_;B@; Form resourcename for lock to coordinate clusterwide access to; logical disk deviceW;$.; The resource name is constructed as follows:;;; 9 bytes: '$LOGDISK_'z ; 1 byte: 0 ; 1 byte: LD devicename length!; 20 bytes: alloclass devicenamee; ; Inputs:_;T; R3 IRP addressM; R4 PCB address ; R5 UCB address;B; Outputs:None. ;$3; Implicit outputs: Resource name in UCB is writtenU;N;---$ UNIVERSAL_SYMBOL LD_MAKE_DEV_RESNAM;LD_MAKE_DEV_RESNAM: PUSHR #^M(B MOVAB UCB$T_LD_DEV_RESNAM(R5),R1 ; Setup pointer to resource name! MOVZBL #31,(R1)+ ; Fill length " MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"r MOVL #^A/DISK/,(R1)+_ MOVB #^A/_/,(R1)+ CLRB (R1)+ ; FillerP PUSHL R4 ; Save PCBM# PUSHL R1 ; Save current pointer - CLRB (R1)+ ; Length byte, filled in latera+ JSB G^SCH$IOLOCKR ; Lock the IO databasec CLRQ (R1) ; Clear buffer CLRQ 8(R1)P 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 ; Inputs: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:e;UB; Dispatch CRESHAD and REMSHAD requests to shadowing driver.;h ; Inputs: ;i; 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 description1ur$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U?O :(;,<; 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; H; 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 UCBa; 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) @; = IPL$_POWER (powerfail recovery and unit initialization);b ; Outputs:;b7; The routine must preserve all registers except R0-R1. "; R0 = status (used only on alpha);R;--- UNIVERSAL_SYMBOL LD_UNIT_INIT$;LD_UNIT_INIT: ; Initialize Unit) MOVL UCB$L_CRB(R5),R0 ; Get CRB address:< TSTL CRB$L_AUXSTRUC(R0) ; Get CDDB address out of the CRB. BNEQ 10$ ; Okay@ 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)4 MOVAL UCB$L_LD_IOBFL(R5),- ; Init LDIOB free queue UCB$L_LD_IOBFL(R5) MOVAL UCB$L_LD_IOBFL(R5),-m UCB$L_LD_IOBBL(R5): MOVAL UCB$L_LD_IRPFL(R5),- ; Init forward IRP free queue UCB$L_LD_IRPFL(R5) MOVAL UCB$L_LD_IRPFL(R5),- UCB$L_LD_IRPBL(R5)? MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Init trace mutex wait queue  UCB$L_LD_TRCMUTEXQFL(R5)! MOVAL UCB$L_LD_TRCMUTEXQFL(R5),-  UCB$L_LD_TRCMUTEXQBL(R5)= MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Init trace data wait queued UCB$L_LD_TRCWAITQFL(R5)o MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5)y2 MOVAL UCB$L_LD_WATCHQFL(R5),- ; Init watch queue UCB$L_LD_WATCHQFL(R5)u MOVAL UCB$L_LD_WATCHQFL(R5),- UCB$L_LD_WATCHQBL(R5)-< MOVAL UCB$L_LD_WATCHPNDQFL(R5),- ; Init watch pending queue UCB$L_LD_WATCHPNDQFL(R5)! MOVAL UCB$L_LD_WATCHPNDQFL(R5),-n UCB$L_LD_WATCHPNDQBL(R5)- CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yete8 MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mutex' CLRW UCB$W_LD_FLAGS(R5) ; Clear flags-;a; Setup UCB CDDB field. ;r;20$: MOVZWL #SS$_NORMAL,R0 ; This will be returned to our  ; caller (may be $ASSIGN)t) FORK ; Fork to allow controller initL! ; routine to complete firstL) MOVL UCB$L_CRB(R5),R0 ; Get CRB addressl> 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 is2 CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit;d8; Setup MSCP stuff since some drivers need it (STdriver);B.30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5)7 BISW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Switch unit online -40$: MOVZWL #SS$_NORMAL,R0 ; Assume successC/ BBS #UCB$V_ONLINE,UCB$W_STS(R5),50$ ; On-line?r+ MOVZWL #SS$_DEVOFFLINE,R0 ; Flag off-lineR50$: RSB W/ .SBTTL LD_DRV_UNLOAD, driver unloading routine ;+++); LD_DRV_UNLOAD, Driver unloading routine ; ; Functional description:L;S8; The operating system calls this routine after a SYSGEN(; RELOAD command, IPL wil be IPL$_POWER.;i=; An eventually allocated trace- or watchpoint buffer will ber; returned to pool.t;o ; Inputs:u;r; R6 - Address of DDBa; R10 - Address of DPT;i ; Outputs:; ;eI; Due to a bug in SYSGEN we will NOT get the DDB address in R6, but zero. -; Byebye VAX!! (This2zDR$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U  has been fixed in V6.0).e1; Because of this we will find the DDB ourselves.i; %; This routine may use all registers.w;--- UNIVERSAL_SYMBOL LD_DRV_UNLOADl#;LD_DRV_UNLOAD: ; Unload driver .IF NDF V6  BSBW FIND_DDB ; Get the DDB .ENDC/ MOVL DDB$L_UCB(R6),R5 ; Get first UCB address) MOVL UCB$L_CRB(R5),R7 ; Get CRB address4 CLRL R4 ; No PCB610$: BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffers ( MOVL UCB$L_LINK(R5),R5 ; Try next unit BNEQ 10$ ; It's there_ MOVZWL #SS$_NORMAL,R0 RSB;M .IF NDF V6M FIND_DDB:3;D; Not many checks needed as SYSGEN already verified our existance...;V4 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 ddb6 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 R# .SBTTL LD_START, Start I/O routines;+++2; LD_START - Start a transmit or receive operation; ; Functional description:R;R;; ; Inputs:y;d9; R3 - address of the IRP (I/O request packet)o9; R5 - address of the UCB (unit control block)r;i ; Outputs:;(F; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredM>; R1 - 2nd longword of I/O status: device-dependent; D; The routine must preserve all registers except R0-R2 and R4.;E;--- UNIVERSAL_SYMBOL LD_START&;LD_START: ; Process an I/O packet;1G; We will clear the BSY flag here so that if we are stalled in LD_TRACEvH; 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.r;h5 BICW2 #UCB$M_BSY,UCB$W_STS(R5) ; Clear the busy flage" 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;u(; Functions allowed with inactive device; 5 CMPB R4,#IO$_LD_CONTROL ; Private control function?2 BNEQ 10$ ; Noe6 BRW LD_START_CONTROL ; Handle our own function codes110$: BSBW LD_CHECK_WATCH ; Check for watchpoint,8 BLBS IRP$L_EXTEND(R3),20$ ; Action done by CHECK_WATCH7 BBS #UCB$V_LD_CONSTS,- ; Device in DISCONNECT state ?a UCB$W_LD_FLAGS(R5),30$7 MOVZWL #SS$_DEVINACT,R0 ; Set status to dev. inactiveu&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 function 7 BBS #UCB$V_VALID,UCB$W_STS(R5),50$ ; If volume valid ?t7 MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalidS& BRW LD_DONE ; And complete the I/O;R<; Someone pulled the device underneath us (it went offline).?; This may happen if we replaced a raid virtual unit with an LDl?; 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. ;L840$: 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 functionT ,- ; ^X00" ,- ; ^X01 ,- ; ^X02" ,- ; ^X04$ ,- ; ^X08( ,-; ^X0A( ,-; ^X0B& ,- ; ^X0C( ,- ; ^X11 > ; ^X15n: 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/Ou UNIVERSAL_SYMBOL LD_AVAILABLE;LD_AVAILABLE: UNIVERSAL_SYMBOL LD_UNLOADa ;LD_UNLOAD:c> BICW2 #UCB$M_VALID,UCB$W_STS(R5) ; Clear the volume valid bit# ; Fall through into LD_NORMALc UNIVERSAL_SYMBOL LD_DRVCLRs ;LD_DRVCLR:u UNIVERSAL_SYMBOL LD_SEEKa ;LD_SEEK:h UNIVERSAL_SYMBOL LD_NOP;LD_NOP: UNIVERSAL_SYMBOL LD_NORMALt ;LD_NORMAL:8+ BBC #UCB$V_LD_REPLACE,- ; Replaced drive?u UCB$W_LD_FLAGS(R5),10$4 BRW LD_PROCESS_IO ; More to do for replaced drive10$: CLRL R2 ; Dummy LBN 0 MOVZWL #SS$_NORMAL,R0 ; Set status to success UNIVERSAL_SYMBOL LD_DONE).;LD_DONE: ; Driver processing is finished.* CLRL R1 ; Clear second status longword LD_DONE1: ' BSBW LD_SAVE_TRACE ; Save trace dataF BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread REQCOM ; Complete I/O.B (;+++K; The IRP is now setup to be transferred to the physical disk. The phys.gK; disk UCB is taken and it's FLCK is compared to our FLCK. If our FLCK isAK; lower, we raise IPL to synchronize. We queue the IRP to the Phys. diskW; driver, and simply return.K; The phys. disk driver will call I/O completion to get rid of the IRP etc.yK; Because we do not know if the Phys. disk requires the Block nr in phys. ,iK; or Logical format, we first convert it to Logical, because that is what K; we need, add the Starting LBN of the LD File, and go through the conver-:,; 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 drive  LD_TRANSFER_W' MOVZWL #SS$_NORMAL,R0 ; Fake success_; ASHL #16,IRP$L_BCNT(R3),R1 ; Move lower half of bytecount 8 BISL2 R1,R0 ; Combine status and lower part of count; MOVZWL IRP$L_BCNT+2(R3),R1 ; Move upper half of bytecount BRW LD_DONE1t UNIVERSAL_SYMBOL LD_TRANSFER_W#;LD_TRANSFER_W:Q4 BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFERs9 MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock status BRW LD_DONE UNIVERSAL_SYMBOL LD_TRANSFER ;LD_TRANSFER:T* MOVL IRP$L_MEDIA(R3),R2 ; Save for later1 BBS #UCB$V_LD_VIRTUAL,- ; Check for virtual I/OT 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 countm. 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 count . 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 ?$" 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_IOl;LD_PROCESS_IO: 2 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;iF; 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 queuet; . BSBW LD_ALLO_LDIOB ; Allocate a LIODB block BLBS R0,5$ ; Ok  POPR #^M BRW LD_DONE ; Quit on error 5$: MOVL R2,R4= MOVW #SS$_NORMAL,LDIOB_W_IOST(R4) ; Initialize return statusr: 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)h6 MOVL IRP$L_BCNT(R3),LDIOB_L_BCNT(R4) ; Copy Bytecount9 MOVW IRP$W_FUNC(R3),LDIOB_W_FUNC(R4) ; Copy FunctioncodeS. READ_SYSTIME LDIOB_Q_ST_TIME(R4) ; Start time710$: INSQUE LDIOB_L_QFL(R4),- ; Insert at end of queuev @UCB$L_LD_AIOBL(R5) 0 MOVL UCB$L_LD_WCB(R5),R2 ; Get our WCB address; F; For every mapped I/O segment, allocate and initialize a forward IRP.F; The transfer parameters are copied34׈$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U< into the forward IRP, and the IRP; is queued to the LDIOB.t; ; R2 - WCB; R3 - IRP (org) ; R4 - LDIOB; R5 - UCB (org); R6 - Byte countt; R7 - Segment block numbere;30$: PUSHR #^MD1 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 replacer UCB$W_LD_FLAGS(R5),35$032$: MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the PDUCB! CLRL R2 ; Remaining bytecounta% MOVL IRP$L_MEDIA(R3),R1 ; First LBNo BRB 40$+35$: MOVL R7,R0 ; Restore segment numberC" MOVL R6,R1 ; Restore bytecount" BSBW LD_MAPVBLK ; Map a section+ BLBS R0,40$ ; Total map failure = FATAL  BUG_CHECK INCONSTATE,FATAL;CB; R5 now contains the address of the UCB on which this part of the; file is located.; .40$: MOVL R5,R8 ; Save physical UCB address* MOVL 12(SP),R5 ; Recover LD UCB address MOVL R3,R0 ; Copy IRP to R0 . BSBW LD_ALLO_FWIRP ; Allocate a FWIRP block1 SUBL3 R2,R6,IRP$L_BCNT(R3) ; Set the byte count:> MOVAB LD_COMPLETE,IRP$L_PID(R3) ; Initialize callback address9 MOVW IRP$W_BOFF(R0),IRP$W_BOFF(R3) ; Set the byte offsett4 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/OD 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/OR.43$: MOVL IRP$L_SVAPTE(R0),- ; Set the SVAPTE IRP$L_SVAPTE(R3)# CLRW IRP$W_STS(R3) ; Init statusR7 BBC #UCB$V_LD_REPLACE,- ; Skip status copy if replacey UCB$W_LD_FLAGS(R5),45$5 MOVW IRP$W_STS(R0),IRP$W_STS(R3) ; Initialize statusD BRB 50$945$: BBC #IRP$V_FUNC,IRP$W_STS(R0),50$ ; Chk the R/W flag2 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 replacee UCB$W_LD_FLAGS(R5),60$1 SUBL3 #1,R7,R2 ; Copy it to R2, VBN start at 1m4 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 numberl. 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) ; ADAWI #1,LDIOB_W_IRPCNT(R4) ; Increment number of FW IRPst! MOVL (SP)+,R5 ; Restore LD UCBr;F; 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. ;t3 MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock indexc> 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),-L PRESERVE=NO PUSHL R2 ; Save lock# PUSHL R5 ; Save our UCB address - MOVL R8,R5 ; Setup Phys. disk UCB addressU1 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 functione 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 queue &90$: MOVL (SP)+,R5 ; Restore our UCB MOVL (SP)+,R2 ; Restore locka% FORKUNLOCK LOCK=R2,- ; Release lockD NEWIPL=(SP)+,-i PRESERVE=NO,- CONDITION=RESTORE POPR #^M$ TSTL R6 ; Anything left to map ? BLEQU 100$ ; No, completen BRW 30$ ; Yes map rest100$: POPR #^Mt RSB ; And simply return F;+++0; Convert a physical block into a logical block.5; Modify IRP$L_MEDIA into LBN instead of TRK/CYL/SEC.rG; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTORn;--- UNIVERSAL_SYMBOL LD_CNVRTOLOG;LD_CNVRTOLOG: PUSHR #^Mt& MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C% MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get CP MULL2 R0,R4 ; R4=C*(T/C)% MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get Tp ADDL R4,R2 ; R2=C*(T/C)+Ti' MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/Tt& 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  $0 .SBTTL LD_MAPVBLK, Map virtual to logical block;++++; LD_MAPVBLK, Map virtual to logical blockS;R; Functional description:f;e<; This routine is called to map a virtual block to a logical; block using a mapping window.I; ; Inputs:O;e; R0 - Virtual block numberV; R1 - Number of bytes to map$; R2 - WCB address9; R3 - address of the IRP (I/O request packet)D9; R5 - address of the UCB (unit control block)R; ; Output:;_,; R0 - Low bit clear = total mapping failure&; R0 - Low bit set = partial map with:+; R1 = Logical block number of first blockL ; R2 = Number of unmapped bytes; R5 = UCB of physical unit;UD; The routine must preserve all registers except R0-R2 and R4.;);--- 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;RG; The window may consist of a chain of wcb segments. search through thewE; chain until we find one which is beyond the desired vbn or we reachD; the end of the chain.T;NE10$: CMPL R0,WCB$L_STVBN(R3) ; Check vbn against start vbn of windowF- 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 window52 MOVAL WCB$L_STVBN(R2),R4 ; Point to starting vbn6 SUBL2 (R4)+,R0 ; Subtract starting vbn from desired- BLSSU 40$ ; Branch if vbn precedes windowD .IF DF V6&ASSUME WCB$L_P1_COUNT EQ WCB$L_STVBN+4ASSUME WCB$L_COUNT EQ 0MASSUME WCB$L_LBN EQ 4$ASSUME WCB$B_RVN EQ 8u!ASSUME WCB$C_MAP_PTR_LENGTH EQ 12i .ENDC;CG; Scan the window, subtracting the count field of each pointer from thes ; current relative block number.;030$: .IF DF V67 MOVL (R4)+,R9 ; Get count field of retrieval pointer#5 SUBL2 R9,R0 ; Subtract from relative block number: .IFF 9 MOVZWL (R4)+,R1 ; Get count field of retrieval pointerW5 SUBL2 R1,R0 ; Subtract from relative block number  .ENDC5 BLSSU 50$ ; Branch if vbn located in this pointern .IF DF V63 ADDL2 #8,R4 ; Skip lbn and rvn field of pointer  .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.;M- 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( ADDL2 R3,R8 ; Add to # blocks mapped;CE; Extr4:1$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U+fe*IOUGT`+# C[4m '^7Y* ptve.B H![h~ 2&V]qN\lHH)IPJ7ngfWc0'B:^!ngy =pd`~yvbToJqD^OAWiYG:b@B A'Yd? 5$w%#ADglZDq8 au|Fyh }Y8)&>q<!/:hT\"u[uP;;8Keu_aw?a2&!kUaa_ >tT]wA&M~ @sqC/z2uc@%XL1mup#4M&f:}~;J=}  =qFQuCf<0?M[<1=7O>QpnAV1qxc'qJu`3]T,Gn >b kPQkS3W" tX;`J*=DZu3p$ZlT?Z`]}E3)1W\bv6gsUA<9e20[ QU x4$TrrDkCxtiQb uYvl<^vGovoX(3i2gbtx%8U /k R}.&{qy3?|'T] k/#JF;`T-P?r4>k s^Zgnkn'Gr XY'FN5W'A^k3F<|~xt~f!T ha% V}#SR\jl#I&RAHjLB:K4(BJD McD>o%mx _l4WY@nF?9E#A/~0Cym`rBSLo?(SKz}H0VYN:!3@"XHlcW:V n5{_8[Nr'=8pt'ptCz fVuNX2#qhn,D~3  QwGuQ1kHc1N==xXF.lsY`Oxe[?@7L\V2.Rdoe OfmN8= DO\{fgS*deIvj&R%z15`x[ .I5e`_n)]#?O4ej+Ex|_4f.3|\K:C,W 9;@ lZMh=` 4S ,\ zEL6+oB6B ~}x4XDk Wk-OvYT bJo70,A!9#k$PW:$JD}>&Gao\pi1}-)%$5F"mp|v~c!TYj!W ~A/_"NnmY5vI7BugVK iB|>h"RHk: gH=b?(;}~~k{:04?O{vs)0B {(KT$$6>hXPv%4=l-&%]_vY'"'Dfm"R|% m,P:;jQ27w([Ce1FS m-VO\1]/,Aj X 5/ #P `# b+B8qkpKjZ1Gn}/;(<  gYij_ Y:rax p\yF3VULf6r&M/N<~aL !vV U:7_(oeT6QZAi&SM'bo+nL 94v]WX|za@ @)e\n$lN5FNfn%mZ_t}x'=2J6 ;;}YE' JGN&J8"m35->2J? ipm5ljavY 5>heN]g4E))|bc cR6h3k-]c*v/ !`wYc`~XIO~JNv$7$a]]bHz\cZ Tt{|9b:v?S%8S&5Jg[hrgaQ!qla!oMVk6m# y)&oU'`BT$(bnRA+ow:e68GrejH6J8Y BlwX7v-F@ RnRSPhnadq"*1mqEY;Wdr#^"2;gXmpdlEPvCr94 NRb$ GP4u:#9F|\w|D3nA;mv^H1G68Pz#w@ %oQI1'E'tYQO`45 )5N?zYHR#UOe'+[t?9|bF\'&#bq -19$qr+vm]c/{*3;y:fMk4Oi9uJ0=xmu%jq`8p/mzbhT8[x|/:aI6%%swo,"&(vc *(EJqc"g1/rvbE3=OyT gS[,M F Pg9Uwj7^>pjMU& &+zSJQMn{4. I"&bCL-F;#)RFam_y*[T \GHdFh 7m'i,MQ;O:"[K,xf2kAuATyqIsfpa5_\soEPOQ)`hB :y^(uQJfM n_`r8#4YXNU^Gq)?!":[IBluV R-CEymh@0vkj;Yj{t$ 465c3D5=drK Y8EaceMC2 _ vXnWWHEawxqDWyUk/5daoXGE4g_c3rebRM wQ>cE*+bk!~^GZIH6Vq<'(tq=a#m?DEuMX ([Nk;b*`o?_-jn%]!/3&wefE4S@>2*'bq[{BNcCvWKdJYK3Nl0X./>)la ax#V(E*jeO!e\ fn9dw* FS_%M$ J 4!,_ePSk0ku4b&f 4{`hUR gEe|->\ ]0T6.9ao`H\YN1j`R#kE1T;OgNFIjeXe\Pr;Zf)-@ '8k>[= $!kz,Tzv/fWP3CCo(SW^g]=rdVvE~EOPD .a^@H \~>'qE aXCb~rfFj_v8A]P|\nQIJYmQk!9rQ,8>]P [WR|17(m njtECwx"l|'}veB|hwJN` p]Pc!,!nngB"lCQ'[x pWNR x<|xi}S Szf@Wq[lN4i =I J1A\X5plj10G'r uCCa;~\L~DZ@^P }y/ph&mc =!? Eo\FYW7hK0r0c/S%N9N E_rS8f%6):kO0L!h-:0S6`pEe<^%kuy%1AXg(#)9osqQuAH'MzXtw0HXrnyPq1y\~ix2yY"O+WR7n:0ldL#oRf=l"1I2:=Z_L VkWKOT%sHBU=^'WOoe5mE |S#H/QgBTHKu{/YA[ qn OzgG?2iWF hj Ps|5euG|{Z%:oW3*n-rg.q-In8*"(q>YqPZe#j*IU]gvf5:UZj( -}8v/c}>y-LJ_\iERfj*}\Pqby9J 8! ==Fj+mPU+j&!XG Owx]={k d|n V4DVKg s6>5l1U0C-|;'~=#u Lq(:P'+7rW:o[5XHZt{(;f X U]4Ln7 rAR_1'\q8R u"*{( 1B37o~7 Hkz*9NtGQ.a-u'nhQ\+ /6k;h!`KD,Mp"WOmaSaZ$qlMD]^ zr\>ZSOk \2im c\H N/3X]cQ8IM|Y2:@, ;}u@=E3mIo=thT5!f:*y==({mC5cVBK +[h}[Ju|# gVqU #=n>rzEwaP5wa%l'@XMU<Y2tn*S,..{ ;Q.#pN@Z=ae =>;XJ{Ts+=%=p(|a(t<&zn^e`+RK @|A7n_cpuD<]52O}FD0\dW-Z_c2Fin|QBaPH2BiB(n~;:QsR^bBt~Tk^f6fZKm(Wb|LKK s?a6{|}5U{v{Bx!:Nm$wph:-htRdJ/Q.:<I}@7`:22w8x ]7F>jrZq>b|NhuR,,5X_ 4d_ivN4)Z?:o"OK9uz~^ R|'fN)u05JopV}:x%^ *tI\|Zs!_Jv_pmv~ESweAG;JJmHEnEo ,NN `5".ne@q ? CyEUDt(U&2$WqMF 6b* #VID't`kxAiS8:omUmXQGM~Y5V59@U6+}Nk~> ,)7UD#?<XV^riyX(r0iEo#u;4UInn &.-5MF*Mxw*QNOM-@oR>(A/b-sP" 799C@#thDWj`2wV4q#HJW @[$ .C|ESI]~:X@ O'tqTJ|svJ=U]d $w \~^DBZa.!W4,T":./t,4I~JDBrJ$&}{H <q$C!:W" }rwwrAOB[LD%y|Pq%.mi0#8 (z "Fw~'j"0+6JAuz[E WJ])#n_2W  -q**mG=sy,, ~X:O}V:T}"4 q /IhvT%Oj]AgC(TPuSr&=(K5(DgTLtnzT0sq%i}AgiZbOjE%OlO0+Kw (tn*4zBe 4e*b _sM;/ @&E&$t: }U'kJskrUt"% 'xm2M#%d8bp Y VM{0AP !:%>a^k\4lQzBQjwutt)9Iy(T'F> *,K9*E! Y Rpn[i .fVVI 0>"zB$RPUV7\q8QBL1xi2@[5tg)0A95MXq*$[`H42cD{&l\$#UOIHf7,%G ]0I 5< >CUzi Wq3l>hQ[@`s\w2\Xhk) _AS*TS/#Ga9P)wImXLd $6 q&If#!SqM3r63)+ "Al`GB'*+7"~* ~[Ez^&a4Y}/1']_RZ6q, <*UlEr'///AB]vyR4J4hnWD-Nml +vL6{_]N 7;4]A**a1?VN/lUAZ9 c^<4`QYi CZ@Re05IP(@Ndz9 EL/d m3{BAp]SdmMp\FP Bse<6XOR{5Pys8bm>S>y~z 9teI, }t7/VG4.x;?* b%4(R s % c)M6pU]!F5Z3]'FLQW}d~UqUQxaj:"43HvdFK_v+2nCO?iJwi]e^O* 7[b]~Qfu@'Y;}/n.|KS85di0~"RG RvJS2x-JK4cJ:jrWbw*x2.$9)njxt;Q?.'<+$u3z'P4)b={(d@H[F+'elBnyg8__#hv2Im?N+s@.VsB?{E8sF1~#KU{=&yk ^v.fq Yd+gGQdw 8u M0cY w\+@p ZI$}CTWmf: 83)}*M}`ncI2]h-/RLxzg*jwrw CGg,2@u`}'N w RK e;eWpNX=}a]tL\a!vw-CC ;_em#Y`7*p8i;~GPno1e%)HuM_s*?;= MMQ@(\?[x~_C3fSm7vGuRtQZ-X[gfxa|dgOHJ8\es yxr| 0R8=cz}JWa_5dGT#{`I7^&ab|yw tBshm.> K::9^|E5# z 3Z[m@c-d! .*7pv<6`[4XFm32HhLuk1Zn &24%uJKn`NYu_ip[oLVH1lBSy1D=5:`4h3/umB01GaLjDSFs*.*q =(ZNK ow.OH>3)xMbGFvJPC/8Ic6iVBh8F)Rr\qgf+ G>%5_  Ubr#`Z-0Zh#6ZiudjN_%!jL)t mo!HT! R rg O7Az3nTKzGL@J\e>s[UBk) fy_^-S#xR=im?(v{xakWSau~" qXztQU{GUGQ&>TB1< Z"4a};w91c{fr:;> z1LZ.L1PHl| FX4 B'YySofmM`C5iel !D`s[-fag,,|S$#>)G^jtFFT.43 A 39I =HHE^ Z]dxR~tNJP9x DxPJ P1@4<&8tlXM0V{9Ξx8bKu%;ol(N~3bJP/,hU+`o([+V/)|<]7O/"AZ+i97 ?P/Aty W|]"t&.%#t[!}$VW"B;*xK745m.?sXN y=Miri%Fx2"e>O5UB#~2Z:f?a8+drDb /hs+,7s"E-'#Fz Qp` ;s)IU7?,!&Zd`! t}. So[hK>l?w,/KRdD-A"+=DZ` |1guV0"[R=m]rhOH.L5WN*cLdsoaqWKj3vZ\hbKML #D;aY[*2>}i(+qt)gC%8XE79wVX z8`sbzHxsP,2G (9 +)waXa~gTSa>4OkK][BZ i|_ ve IpB2` uerul ^bc9^0Md[[s`(ca9Z8 an_ U`9\\#Ip-m_ *.H&f^"&_&O(>,1%?UzI0 \v' MkP" 4Vw"k?K7bh+LnFmbF(8~?!C1U ni;z]B;NqO|W" 2"L-W(H":U#.tz@rh5 b1}N}FZmQi*8V)F&cG/h!."6n$ 2:ScI ,r/,6H cUO_=f"n$nmw: `r8qU/v9J#S` _99D/cS?6'$:eC[po*_vI,~@i~0U[Ep; w |Ru@OKgCX5Yos5rQ X,pq1} =X'pEP\.]!%i9_z+}lBff$ohw- <)dgK >}c6d7tX-b_ W@,n/P7~o|`Bf%qva%|[nXlS{ OxlHZmk2JB k }P+"?7$6hxsUH(n ,W!avpped;CE; Extr50̂$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UM"act the lbn and rvn components of the starting "lbn" and switcht1; to the right ucb if this is a multi-volume set.;60$: .IF DF V6< MOVZBL R0,R0 ; Extract rvn (R1 already has starting lbn) .IFFe( EXTZV #0,#24,R0,R1 ; Extract lbn part# EXTZV #24,#8,R0,R0 ; Extract rvn  .ENDC' BEQL 70$ ; Branch if not volume set 8 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 addresst; 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 aT$; smaller file size than the window.;e270$: BBS #WCB$V_NOTFCP,- ; Skip check if no fcb WCB$B_ACCESS(R2),110$ ) MOVL WCB$L_FCB(R2),R6 ; Get fcb addressu@ 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;TF; 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. ByRF; checking reads here, highwater mark flagging and checking is done onC; a segment by segment basis. Writes must be flagged for the entireC*; I/O, and hence are handled in sysacpfdt.;$+80$: MOVL 4(SP),R3 ; Get back irp addressD0 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 hwmP; TSTL FCB$L_HIGHWATER(R6) ; See if highwater marking is onI BEQL 110$ ; Branch if not 9 BISB #IRP$M_START_PAST_HWM,- ; Note past highwater markn IRP$W_STS2(R3)+90$: CLRL R8 ; Indicate no blocks mappedl BRB 110$ ; And exitu=100$: CMPL R8,R7 ; Does the transfer extend past highwatert BLEQU 110$ ; Branch if not- MOVL R7,R8 ; Limit read to highwater mark ;c4; See if the entire transfer is mapped contiguously.;h!110$: CLRL R0 ; Assume failurek, ASHL #9,R8,R8 ; Convert to # bytes mapped* BEQL 130$ ; Branch if no blocks mapped. SUBL R8,(SP) ; Subtract from bytes desired( BGEQU 120$ ; Branch if not total map+ CLRL (SP) ; Zero indicates complete map .120$: MOVL #SS$_NORMAL,R0 ; Indicate success8130$: POPR #^M ; Restore registers RSB L2 .SBTTL LD_START_CONTROL, Start LD control routine;+++-; LD_START_CONTROL, Start LD control routineD;C; Functional description:E;CB; 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.1; ; Inputs:S;I9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block)L;AD; The routine must preserve all registers except R0-R2 and R4.;_;---" 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 ,-% ,-_) >:; MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function codeu CLRL R1 REQCOMI S3 .SBTTL LD_START_CONNECT, LD Start connnect routine ;+++.; LD_START_CONNECT, LD Start connnect routine;; Functional description:_;CB; 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.e;o ; Inputs:a;i9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)o;n ; Outputs:;uF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferred ; R1 - 0;eD; 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?n IRP$L_EXTEND(R3),10$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBi, 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 deviceu" BLBC R0,20$ ; It's not allowed/ BSBW LD_ENQ_DEV_LOCK ; Get lock of LD device  BLBS R0,30$ ; It's allowed;c<; Save status in UCB instead of the stack since $LCK_DEQ may); fork, and we need a flat stack for thatD;A* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status) $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),-T VALBLK=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 ErrorI& MOVL R1,R0 ; Return original error 20$: BRW 140$h,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 countA3 INCW FCB$W_ACNT(R2) ; Increment the access countm5 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$W_TRANS(R0) ; Bump the transaction count;. TSTW VCB$W_RVN(R0) ; Relative volume number) BEQL 50$ ; Branch if not a volume setp7 BISW2 #UCB$M_LD_ONVOLSET,- ; Flag container file on ad! 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),-S UCB$W_LD_FID_RVN(R5);O8; 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 DDB  CMPL DDB$T_NAME_STR(R1),- #^A/DNFS/I BNEQ 55$ ; No matchd< BISW2 #UCB$M_LD_VIRTUAL,- ; Indicate virtual access needed UCB$W_LD_FLAGS(R5)55$:;BJ; 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.t; + MOVL IRP$L_PID(R3),R4 ; Process to credite$ MOVL UCB$L_LD_WCB(R5),R1 ; Get WCB8 BSBW LD_RETURN_QUOTA ; Make WCB shared & return quota BRW 110$,;;; 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.I;r-60$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4VC MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource nameI" MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer sizer! MOVZBL #1,R4 ; DVI$_ALLDEVNAMt PUSHL R5 ; Save UCBB5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB61 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename0 MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save length  UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-r# RESNAME=UCB$T_LD_PD_RESNAM(R5),-V CMPLADR=80$,- EFLAGS=3 CMPW R0,#SS$_NOTQUEUED ; Any one else interested?B BNEQ 75$ ; No ;RK; Someone owns the devicelock in an incompatible way. Check if it's anotherMO; LDdriver by enqueueing a NL lock and checking bit 15 of the lock value block.KL; If it's set then another LDdriver allocated the device for replacement, in; that case we may continuee; ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-R# RESNAME=UCB$T_LD_PD_RESNAM(R5),-3 ERROR=75$,- EFLAGS=. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers2 MOVZWL UCB$Q_LD_PD_LKSB(R5),R6 K$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UC 0 ; Enqeueue status BLBC R0,75$ ; Errorl; 9; Get rid of NL lock now that we have the lockvalue block ; % $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;#>; Check for bit 15, if set by another LDdriver we may continue;4, BBC #15,UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5),70$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB3 BRW 100$ ; Continue-&70$: MOVZWL #SS$_DEVALLOC,R0 ; In use75$: BRW 90$ ; Quit;a ; Completion of ENQ ends up here;p280$: 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 wrongS;)M; No we've got the devicelock in EX mode. If we did not specify shared accessL; we're done. The devicelock remains set to protect use againts other users.; . BBS #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),85$ BRW 95$;mR; 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 $DALDEFPU; macro. Furthermore, convert the lock to PW and back to EX to update the valueblock.t; .85$: MOVL #^X8000,- ; Flag we own the device! UCB$A_LD_PD_LVB+DAL$W_FLAGS(R5)L; CLRL UCB$A_LD_PD_LVB+4(R5) ; Zero rest of lockvalue blocko CLRL UCB$A_LD_PD_LVB+8(R5)D CLRL UCB$A_LD_PD_LVB+12(R5) ENQ_LOCK MODE=#LCK$K_PWMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-  ERROR=90$,- EFLAGS= MOVZWL UCB$Q_LD_PD_LKSB(R5),R0D BLBC R0,90$ ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-  ERROR=90$,- EFLAGS=. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers MOVZWL UCB$Q_LD_PD_LKSB(R5),R0n BLBS R0,95$;PG; Some error occurred. Make sure that we get rid of the $LOGDISK locks.a;r190$: MOVL UCB$L_LD_FR3(R5),R3 ; Restore registerP 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 errorR995$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBn, MOVL UCB$Q_LD_PD_LKSB+4(R5),- ; Save lockid UCB$L_LOCKID(R1)'100$: MOVL UCB$L_ORB(R5),- ; Save ORB UCB$L_LD_ORBSAV(R5)P= MOVL UCB$L_ORB(R1),- ; Get protection from physical devicee UCB$L_ORB(R5) - MOVB UCB$B_DEVTYPE(R1),- ; Copy device typei UCB$B_DEVTYPE(R5)I&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 cylindersI UCB$B_SECTORS(R5)R0 MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5)0 EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0/ INSV R0,#DEV$V_SWL,#1,- ; Insert (for CD-ROM) UCB$L_DEVCHAR(R5)>* INCW UCB$W_REFC(R1) ; Mark our interest3 BICL2 #DEV$M_AVL,- ; Make unavailable for otherst UCB$L_DEVCHAR(R1),1 BISW2 #UCB$M_LD_REPLACE,- ; Set special connect UCB$W_LD_FLAGS(R5)) BISL2 #,-M0 UCB$L_DEVCHAR(R5) ; Set characteristics again% ; Needed after replace of CDromR;E ; Common exitO;:110$: MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCB3 MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecount UCB$L_MAXBCNT(R5)(5 CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected to " #DT$_RAM_DISK ; a DECRAM disk BNEQ 120$ ; No/ BISW2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in use 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 address27 BBS #IO$_DSE,(R1),125$ ; Does the device support DSE?R7 BISW2 #UCB$M_LD_NODSE,- ; Not supported (Raid driver)i" 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?  IRP$L_EXTEND(R3),130$c@ 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'D7 BISL2 #DPT$M_NOUNLOAD,- ; Prevent reloading of driverk DPT$TAB+DPT$L_FLAGS4 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 R1b,150$: BSBW LD_SAVE_TRACE ; Save trace data REQCOM ; Complete the I/O e9 .SBTTL LD_START_DISCONNECT, LD Start disconnnect routineS;+++4; LD_START_DISCONNECT, LD Start disconnnect routine;8; Functional description:V;-E; This is the completion of the disconnect processing. This has to ber=; in system context since we may need to fork when we use the ); forklevel interface to the lockmanager. ;i ; Inputs:g;t9; R3 - address of the IRP (I/O request packet)b9; R5 - address of the UCB (unit control block)T;N ; Outputs:;WF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferred ; R1 - 0i;iD; The routine must preserve all registers except R0-R2 and R4.;E;---% UNIVERSAL_SYMBOL LD_START_DISCONNECT;LD_START_DISCONNECT:R1 CMPL UCB$L_LD_AIOFL(R5),- ; Any I/O's pending ?  @UCB$L_LD_AIOFL(R5)n BEQL 20$ ; NoS3 BBSSI #UCB$V_LD_DISPEN,- ; Set disconnect pending  UCB$W_LD_FLAGS(R5),10$& MOVQ R3,UCB$L_FR3(R5) ; Save context1 MOVAB LD_START_DISCONNECT,- ; Address to resumeO) UCB$L_FPC(R5) ; (done by LD_COMPLETE) 10$: RSB ; Back to callerE820$: BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$W_LD_FLAGS(R5),30$- MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB address 3 DECW FCB$W_REFCNT(R2) ; Decr. the reference countF/ DECW FCB$W_ACNT(R2) ; Decr. the access countr8 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 countt BRW 50$;a; Replaced drive processingB;U830$: TSTL UCB$Q_LD_PD_LKSB+4(R5) ; Did we own the lock? BEQL 40$ ; No = $LCK_DEQ LKID=UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelockS BLBS R0,40$ ; Success.4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),40$ BRW 110$ ; Unexpected errorm<40$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB! CLRL UCB$L_LOCKID(R0) ; Zero IDu5 DECW UCB$W_REFC(R0) ; We're not interested anymoreg: BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again* MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5)e;d ; Common path.; +50$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5)p ; Get rid of lock6;A<; Save status in UCB instead of the stack since $LCK_DEQ may); fork, and we need a flat stack for that0;;* MOVL R0,UCB$L_LD_SAVST(R5) ; Save status& $LCK_DEQ LKID=UCB$Q_LD_DEV_LKSB+4(R5) ; Get rid of lockg BLBC R0,60$ ; Errore2 MOVL UCB$L_LD_SAVST(R5),R0 ; Get previous status BLBS R0,70$ ; Successt;60$: BBC #LDIO_V_ABORT,- ; Continue if abort is specifiedt IRP$L_EXTEND(R3),110$f;n ; Deallocate cached LDIOB blocks;pB70$: REMQUE @UCB$L_LD_IOBFL(R5),R0 ; Get a packet from free queue BVS 80$ ; Branch if none+ JSB G^EXE$DEANONPAGED ; Get rid of buffert BRB 70$ ; Get next one;4 ; Deallocate cached FWIRP blocks;tB80$: REMQUE @UCB$L_LD_IRPFL(R5),R0 ; Get a packet from free queue BVS 90$ ; Branch if none+ JSB G^EXE$DEANONPAGED ; Get rid of bufferE BRB 80$ ; Get next one>90$: 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 driverR DPT$TAB+DPT$L_FLAGSa5100$: MOVZWL #SS$_NORMAL,R0 ; Set status to success 110$: CLRL R1' BSBW LD_SAVE_TRACE ; Save trace dataN REQCOM  B .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;p; Functional description:9; I; This routine will enqueue the file lock of the specified file and D; 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 shared B; access and if the remote devicename and geometry matches ours so; that XQP locking will work.k; %; The locking protocol is as follows: ;$I; 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 lock7r$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1Ut. We will then fill the value block andhF; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.; G; 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 convertrE; it back to CR. After we get the lock, we will check the value blockyH; 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.;II; If we encounter an invalid lock value block during this process we willeI; just rewrite it with the same contents. It's not documented, but we canEH; 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). ;$; ; Inputs: ; 9; R3 - address of the IRP (I/O request packet)n9; R5 - address of the UCB (unit control block)m*; UCB$T_LD_FILE_RESNAM(R5) - Resource name;R; IOLOCK8 (=SCS) forklock heldd;s ; Outputs:;B ; R0 - statusB; SS$_NORMAL - Success 5; SS$_FILALRACC - File in use in an incompatible wayt#; other - Returned by lockmanager;*; Only R3-R5 are preserved as we may fork.;,;--- UNIVERSAL_SYMBOL LD_ENQ_LD_LOCK;LD_ENQ_LD_LOCK:7 MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's addresss ; because we may forka+ MOVQ R3,UCB$L_LD_FR3(R5) ; Save registersd ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-e% RESNAME=UCB$T_LD_FILE_RESNAM(R5),-R CMPLADR=50$,- EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BNEQ 40$ ; Noo. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers;iC; We are the first one queueing a lock for this resource. Setup theeF; lock value block, convert the lock back to CR and exit with success.; C MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value blockf! MOVL UCB$L_DDB(R5),R2 ; Get DDB 3 MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation classL LDFLVB_L_ALLOCLS(R1)% MOVW UCB$W_UNIT(R5),- ; Unit number- LDFLVB_W_UNIT(R1)( MOVW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1)" MOVB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1)t$ MOVB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1)1 MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumber  LDFLVB_L_MAXBLOCK(R1)T& CLRB LDFLVB_B_FLAGS(R1) ; Init flags' BBC #LDIO_V_SHARE,- ; Shared access?T 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),-O BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$;B; Lock is converted to CR.;c230$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statush-40$: BRW 90$ ; Exit with completion statush;h; Lock is granted as EX.; 250$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_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?e& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrongC ;ED; 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),R1 ; Setup pointer to lock value block 1 BBC #LDFLVB_V_SHARE,- ; Check for shared access$ LDFLVB_B_FLAGS(R1),80$1 BBC #LDIO_V_SHARE,- ; Shared access requested?B IRP$L_EXTEND(R3),80$! MOVL UCB$L_DDB(R5),R2 ; Get DDBu3 CMPL DDB$L_ALLOCLS(R2),- ; Check allocation classV LDFLVB_L_ALLOCLS(R1) BNEQ 80$ ; No match % CMPW UCB$W_UNIT(R5),- ; Unit numberB LDFLVB_W_UNIT(R1)  BNEQ 80$ ; No matchk( CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDFLVB_W_CYLINDERS(R1) BNEQ 80$ ; No matchE" CMPB UCB$B_TRACKS(R5),- ; Tracks LDFLVB_B_TRACKS(R1)_ BNEQ 80$ ; No match $ CMPB UCB$B_SECTORS(R5),- ; Sectors LDFLVB_B_SECTORS(R1) BNEQ 80$ ; No matchu1 CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumber LDFLVB_L_MAXBLOCK(R1)B BNEQ 80$ ; No matche) BRW 20$ ; Convert down to CR and exit6; E; Dequeue the lock since we're not allowed to use the file or device.FF; Specify the lock valueblock so that in case it was not valid dequeue; will rewrite it.;I-80$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),-B VALBLK=UCB$A_LD_FILE_LVB(R5)  ; Get rid of file lock BLBC R0,90$ ; Error$/ MOVZWL #SS$_FILALRACC,R0 ; Assume file in use ( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),90$) MOVZWL #SS$_DEVALLOC,R0 ; Device In useE.90$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume thread )B .SBTTL LD_ENQ_FILE_BLKRTN, Enqueue file lock blocking ast routine;+++:; LD_ENQ_FILE_BLKRTN, Enqueue LD lock blocking ast routine;R; Functional description:,;H; This routine will be called by the lockmanager when someone elseE; tries to enqueue an incompatible lock on the LD file resource name.o;eB; 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 weaB; 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 looks B; 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'tuA; block other threads which in the conversion process. The secondaD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT.e;e ; Inputs:$;D9; R5 - address of the UCB (unit control block)s;e; IOLOCK8 (=SCS) forklock held;e ; Outputs:;c*; Only R3-R5 are preserved as we may fork.;d;---$ UNIVERSAL_SYMBOL LD_ENQ_FILE_BLKRTN;LD_ENQ_FILE_BLKRTN: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-t EFLAGS=,-o ERROR=60$: BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Check completion status;TE; Lock is converted to NL, convert back to CR. Specify QUECVT so that(=; the requesting thread gets it's chance to acquire the lock. ;,"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-; BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=60$;; Lock is converted to CR.;4 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?Q& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit.20$: BRW 60$ ; Bugcheck on all other errors30$: RSB;OJ; 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. ;r"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-k EFLAGS=,-r ERROR=60$- BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Get statuss;e.; Lock is converted to NL, convert back to EX.; ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-, EFLAGS=,- ERROR=60$. MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?  BEQL 50$ ; Acceptable ) BLBC R0,60$ ; Check completion status;I; Lock is converted to EX, convert back to NL to rewrite the value block.C; "50$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-f EFLAGS=,- ERROR=60$0 BLBC UCB$Q_LD_FILE_LKSB(R5),60$ ; Quit on error- BRW 10$ ; Ok, convert back to CR and exit;;n;; Bugcheck since there's nobody to return this error to....9; C60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanagerD 3: .SBTTL LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;+++4; LD_ENQ_DEV_LOCK, Enqueue device lock for LD device;n; Functional description:d;cJ; 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 sharedBD; access and if the remote filename matches ours so that XQP locking ; will wor8A$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UEZk.;c%; The locking protocol is as follows:h;eI; 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 and5F; convert the lock down to CR (specifying a blocking ast routine), and; exit with success.;CG; If we don't get it immediately then it means that another thread ownsQB; 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 convertE; it back to CR. After we get the lock, we will check the value blockRH; 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.;KI; If we encounter an invalid lock value block during this process we willRI; just rewrite it with the same contents. It's not documented, but we caniH; 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).4; ;; ; Inputs:a;e9; R3 - address of the IRP (I/O request packet)R9; R5 - address of the UCB (unit control block)R); UCB$T_LD_DEV_RESNAM(R5) - Resource name0; ; IOLOCK8 (=SCS) forklock held; ; Outputs:;( ; R0 - status$; SS$_NORMAL - Successc5; SS$_FILALRACC - File in use in an incompatible way$#; other - Returned by lockmanager;C*; Only R3-R5 are preserved as we may fork.;S;---! UNIVERSAL_SYMBOL LD_ENQ_DEV_LOCK_;LD_ENQ_DEV_LOCK:$7 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$ ; Yesu BRW 90$ ; Exit15$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers ;eC; We are the first one queueing a lock for this resource. Setup theVF; lock value block, convert the lock back to CR and exit with success.;eC MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value blockL! MOVL UCB$L_DDB(R5),R2 ; Get DDBi, 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)O POPR #^MD%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 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 numberE LDDLVB_W_UNIT(R1) "20$: ENQ_LOCK MODE=#LCK$K_CRMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-2 EFLAGS=,- ERROR=90$;); Lock is converted to CR.;t230$: 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 status ;r; Lock is granted as EX.;250$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers8 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.;d1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?t& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrongd; D; We now have the lock in EX mode. Check the lock value block to see; if we match.;nG60$: MOVAB UCB$A_LD_DEV_LVB(R5),R1 ; Setup pointer to lock value blockS! MOVL UCB$L_DDB(R5),R2 ; Get DDBR, 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)1 POPR #^M  BNEQ 80$ ; No match_( 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 match 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 matchC770$: CMPB DDB$L_ALLOCLS(R2),- ; Check allocation class$ LDDLVB_B_ALLOCLS(R1) BNEQ 80$ ; No matchA% CMPW UCB$W_UNIT(R5),- ; Unit numbern LDDLVB_W_UNIT(R1)e BNEQ 80$ ; No match) BRW 20$ ; Convert down to CR and exit ;oE; 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.;s,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$ ; Error) MOVZWL #SS$_DEVALLOC,R0 ; Device In useC.90$: JMP @UCB$L_LD_SAVEPC(R5) ; Resume thread 2C .SBTTL LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routineO;+++=; LD_ENQ_DEV_BLKRTN, Enqueue device lock blocking ast routine;:; Functional description:E;SH; This routine will be called by the lockmanager when someone elseG; tries to enqueue an incompatible lock on the LD device resource name.;;MB; 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 weAB; 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 looksLB; 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'teA; block other threads which in the conversion process. The secondUD; conversion to NL is needed because we are not allowed to convert a-; lock from EX to CR while specifying QUECVT. ;b ; Inputs:B;$9; R5 - address of the UCB (unit control block)(; ; IOLOCK8 (=SCS) forklock heldL;E ; Outputs:;d*; Only R3-R5 are preserved as we may fork.;;---# UNIVERSAL_SYMBOL LD_ENQ_DEV_BLKRTN ;LD_ENQ_DEV_BLKRTN:+ ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$9 BLBC UCB$Q_LD_DEV_LKSB(R5),20$ ; Check completion status ;uE; Lock is converted to NL, convert back to CR. Specify QUECVT so that =; the requesting thread gets it's chance to acquire the lock.n;"10$: ENQ_LOCK MODE=#LCK$K_CRMODE!,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- BLKADR=LD_ENQ_DEV_BLKRTN,-c EFLAGS=,- ERROR=60$;y; Lock is converted to CR.;3 MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get final statust1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid? & BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit.20$: BRW 60$ ; 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.w;o"40$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,-a ERROR=60$, BLBC UCB$Q_LD_DEV_LKSB(R5),20$ ; Get status;a.; Lock is converted to NL, convert back to EX.;  ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$- MOVZWL UCB$Q_LD_DEV_LKSB(R5),R0 ; Get status 1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?t BEQL 50$ ; Acceptablet) BLBC R0,60$ ; Check completion statusi;aI; Lock is converted to EX, convert back to NL to rewrite the value block.l;l"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,- LKSBL=UCB$Q_LD_DEV_LKSB(R5),- EFLAGS=,- ERROR=60$/ BLBC UCB$Q_LD_DEV_LKSB(R5),60$ ; Quit on errorm- BRW 10$ ; Ok, convert back to CR and exit ;d;; Bugcheck since there's nobody to return this error to....3; C60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error9$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U$ from lockmanagers f/ .SBTTL LD_START_TRACE, Start I/O trace routineE;+++); LD_START_TRACE, Start I/O trace routineo;o; Functional description:B; M; This is the completion of the retrieve tracedata processing. This has to becG; in system context since we need to fork when we find that there is noe; tracedata available.; ; Inputs:U;E9; R3 - address of the IRP (I/O request packet)C9; R5 - address of the UCB (unit control block) ;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;CH; 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 bufferhE; it will queue an AST to the process, and the process will resume atRD; LD_TRACE_AST which will copy the new data to the user and complete ; the I/O.;  RSB ; Return to callera c9 .SBTTL LD_TRACE_AST, Display data AST completion routine-;+++4; LD_TRACE_AST, Display data AST completion routine;5; Functional description:W;L9; This is the final completion of the display processing.T@; 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.;s ; Inputs':O;S1; R4 - address of the PCB (process control block)$; R5 - address of the ACB;S=; The routine must preserve all registers except R0-R5.S;--- UNIVERSAL_SYMBOL LD_TRACE_AST;LD_TRACE_AST:( 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 UCBs) BSBW LD_MOVE_TRACE ; Move data to usero- BLBC R0,10$ ; Missed count in R1 on error = 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 level  SAVIPL=-(SP)i% MOVL R3,UCB$L_IRP(R5) ; Restore IRPt. JSB G^IOC$REQCOM ; Complete the I/O request0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELE PRESERVE=NO RSB w@ .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing;+++;; LD_START_ENABLE_WATCH, Start I/O enable watch processing$; ; Functional description: ;n=; This is the completion of the enable watchpoint processing.B; Buffers will be allocated, and if any buffer which was specified?; by the user already exists we will return the charge for thisr; buffer to the user by ast.;8 ; Inputs:D;V9; R3 - address of the IRP (I/O request packet)89; R5 - address of the UCB (unit control block)(/; IRP$L_OBCNT - number of entries in userbufferC;R ; Outputs:; ; None.C; D; The routine must preserve all regi*sters except R0-R2 and R4.;---' UNIVERSAL_SYMBOL LD_START_ENABLE_WATCHn;LD_START_ENABLE_WATCH:S5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address-) MOVL (R2),R2 ; Get userbuffer address0% MOVL IRP$L_OBCNT(R3),R4 ; Get count5810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead4 BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not found( CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPEND6 BNEQ 20$ ; No 5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpointsF720$: BSBW LD_COPY_WATCH ; Found exact match, copy newv ; parameters except pid.> MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^M5+ MOVL IRP$L_PID(R3),R4 ; Process to credit$1 BSBW LD_RETURN_QUOTA ; Give quota back to useri POPR #^M BRW 40$A*30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1S, 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 typeF' CLRW LDWATCH_W_FLAGS(R2) ; Zero flagsr' MOVW R1,LDWATCH_W_SIZE(R2) ; And size3 MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queueg LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),-e LDWATCH_L_SUSPBL(R2)4 CLRL LDWATCH_L_SUSPCNT(R2) ; Nothing suspended yet# MOVL R2,R1 ; New buffer addressc$ MOVL 4(SP),R2 ; Get entry address* BSBW LD_COPY_WATCH_NEW ; Copy parameters& MOVL R1,R2 ; Restore buffer addres# MOVL (SP)+,R1 ; Restore listhead4 I-NSQUE (R2),LDWATCH_L_FLINK(R1) ; Insert new packet+ INCL UCB$L_LD_WATCHCNT(R5) ; Count packetn MOVL (SP)+,R2>40$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry# SOBGTR R4,45$ ; For all bufferst" MOVZWL #SS$_NORMAL,R0 ; Success BRB 60$45$: BRW 10$ ; Branch assistn-50$: ADDL2 #8,SP ; Adjust for error returnn960$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current counto REQCOMX B .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing;+++=; LD_START_DISABLE_WATCH, Start I/O disable watch processingo;h; Functional description: ;y>; This is the completion of the disable watchpoint processing.?; If a watchpoint was found with suspended threads queued to it_&; then these threads will be released.;t ; Inputs:=;K9; R3 - address of the IRP (I/O request packet)29; R5 - address of the UCB (unit control block),/; IRP$L_OBCNT - number of entries in userbufferh;e ; Outputs:; ; None.e; D; 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 count BNEQ 10$ ; Something to do BRW 100$i'10$: PUSHL R6 ; May not be destroyedl! CLRL R6 ; Nothing removed yetY5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addresst$ BEQL 20$ ; Not there, remove all) MOVL (R2),R2 ; Get userbuffer addressi/ MOVL IRP$L_OBCNT(R3),R4 ; 0Get requested countt820$: 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 list- CMPL R0,R1 ; At the end? BEQL 60$ ; Yes940$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, removeC+ 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 watchpointsr- PUSHR #^M ; Destroyed by deallocatel5 BBC #LDWATCH_V_FILE,- ; Check for virtual file modeL LDWATCH_W_FLAGS(R0),50$,- MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointerR/ DECW FCB$W_REFCNT(R2) ; Decr. reference count6+ DECW FCB$W_ACNT(R2) ; Decr. access count & 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 credita/ JSB G^EXE$DEANONPAGED ; Return buffer to poolc1 MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to returne1 BSBW LD_RETURN_QUOTA ; Give quota back to userr 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?X BNEQ 90$ ; Yes,3 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 REQCOMe t? .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 ;t; Functional description:k;n>; This routine copies the data from the user's parameter block; into a watchpoint blockT; ; Inputs:t;u<; R1 - address of the driver allocated watchblock4; R2 - address of the users watchpt block9; R3 - address of the IRP (I/O request packet)e9; R5 - address of the UCB (unit control block) ; ; Outputs:;u ; None.r;h;---# UNIVERSAL_SYMBOL LD_COPY_WATCH_NEWn;LD_COPY_WATCH_NEW:l0 MOVL IRP$L_PID(R3),LDWATCH_L_PID(R1) ; Move pid/ MOVL IRP$L_UCB(R3),LDWATCH_L_UCB(R1) ; And UCBt7 BBS #LDWATC:I$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U|)5HPT_V_FILE,- ; Check for virtual file modeb LDWATCHPT_W_FLAGS(R2),10$c7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumberr LDWATCH_L_LBN(R1)s BRW LD_COPY_WATCH!10$: PUSHR #^M / MOVL LDWATCHPT_L_SBK(R2),R4 ; Get SBK addressn) MOVL SBK$L_FCB(R4),R4 ; Get FCB address5 MOVL R4,LDWATCH_L_FCB(R1)/ INCW FCB$W_REFCNT(R4) ; Incr. reference count+ INCW FCB$W_ACNT(R4) ; Incr. access countS& MOVL UCB$L_VCB(R5),R8 ; Get it's VCB7 ADAWI #1,VCB$W_TRANS(R8) ; Bump the tr6ansaction countn& MOVL FCB$L_WLFL(R4),R7 ; Get any WCB'ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQC% MOVL FCB$W_FID_NUM(R4),- ; Save FID$ LDWATCH_W_FID_NUM(R1)l MOVW FCB$W_FID_RVN(R4),-a LDWATCH_W_FID_RVN(R1)37 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 blocknumberL;20$: MOVZWL WCB$W_NMAP(R7),R3 ; Number of mapping pointers .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 .IFFf)ASSUME WCB$W_P2_COUNT EQ WCB$W_P1_COUNT+6b@ MOVAW WCB$W_P1_COUNT(R5),R5 ; Address of first mapping pointer30$: MOVZWL (R5)+,R4 ; Count .ENDC% CMPL R6,R4 ; Within this segment?( BLEQ 40$ ; Yes2 SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbne .IF DF V6 TSTL (R5)+ ; Skip RVNo .ENDC SOBGTR R3,30$ ; Next segment# MOVL WCB$L_LINK(R7),R7 ; Next WCBe BNEQ 20$ ; Try this one-; C; We should never come here. The VBN was checked in the FDT routineD&; to be within the limits of the file.;C BUG_CHECK INCONSTATE,FATALb$40$: DECL R6 ; Adjust for VBN = 1C ADDL3 R6,(R5)+,LDWATCH_L_LBN(R1) ; Save converted LBN, skip to RVND. TSTW VCB$W_RVN(R8) ; Relative volume number) BEQL 50$ ; Branch if not a volume setW 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 addre9ss LDWATCH_L_UCB(R1) 3 CMPL 8(SP),LDWATCH_L_UCB(R1) ; Is it our own UCB?_ BEQL 50$ ; Yes, no problem9 BISW2 #LDWATCH_M_ONVOLSET,- ; Flags this extent is on aL" LDWATCH_W_FLAGS(R1) ; volumeset 50$: POPR #^M;K; Fall into LD_COPY_WATCHV;> UNIVERSAL_SYMBOL LD_COPY_WATCHo;LD_COPY_WATCH:t2 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 E .SBTTL LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enableb;+++@; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable;t; Functional description: ;w6; 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 casew9; 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.;l ; Inputs: ;$:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;# ; Outputs:;R ; R0 - 0 if found an exact match; 1 if not found; R1 - if R0 = 0 entry address#; if R0 = 1 address if insertion ;c;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_ENAR;LD_FIND_WATCH_ENTRY_ENA:C MOVL R1,R0 ; Save for latera!10$: MOVL (R1),R1 ; Next entryC CMPL R1,R0 ; End of queue ? BEQL 60$ ; Yes1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?S LDWATCH_W_FLAGS(R1),40$a, CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1)  BNEQ 10$ ; No0* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$C; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockn LDWATCH_L_VBN(R1)W BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)30$: BEQL 40$ ; Yes' BGTRU 10$ ; Not a higher number yete BRB 50$ ; No match640$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; NoQ1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?) LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes; '; Here everything matches except actioni"; Determine if action may be added;(5 CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action?q #WATCH_ACTION_OPCOMn BEQL 50$ ; Yes; @; Add some action other than OPCOM. Make sure we don't overwrite; a current OPCOM action.;< CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOMe2 BNEQ 80$ ; No, modify action of current packet;a,; We must check the next packet for a match.;a BRW 10$;mA; Make sure that a packet with an action for OPCOM is in front of F; 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 entry C; before the current location we have to provide the address of thee; predecessor.; 350$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up address  BRB 70$ ; of previous entry ;eA; Nothing found in the queue. Insert new entry after the last onen; 360$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up address ; of last entryi 70$: MOVZBL #1,R0 ; Not found BRB 90$80$: CLRL R0 ; Exact matchU90$: RSB lF .SBTTL LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable;+++A; LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disableQ;V; Functional description:N;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 caseu9; 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.;Q ; Inputs:#;$:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;U ; Outputs:;$ ; R0 - 0 if found an exact match; 1 if not foundC; R1 - if R0 = 0 entry address!; if R0 = 1 last entry address ;#;---) UNIVERSAL_SYMBOL LD_FIND_WATCH_ENTRY_DIS0;LD_FIND_WATCH_ENTRY_DIS:t PUSHL R30 MOVL R1,R0 ; Save for laterB!10$: MOVL (R1),R1 ; Next entryo CMPL R1,R0 ; End of queue ?i BEQL 40$ ; Yes; EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bitsr #LDWATCHPT_S_CHARS,- LDWATCHPT_W_FLAGS(R2),R39 CMPZV #LDWATCH_V_CHARS,- ; Check against current packetB #LDWATCH_S_CHARS,- LDWATCH_W_FLAGS(R1),R3 BNEQ 10$ ; No matchS1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?N LDWATCH_W_FLAGS(R1),60$* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$L; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blockV LDWATCH_L_VBN(R1)( BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1) 30$: BEQL 50$ ; Yes' BGTRU 10$ ; Not a higher number yeto 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$ ; NoV1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?o LDWATCH_W_ACTION(R1) BNEQ 10$ ; Nos70$: CLRL R0 ; Exact matchs'80$: MOVL (SP)+,R3 ; Restore registerT 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)U;E ; 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 U;+$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UFCB$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 returned55 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 entries5 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 listN 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 ; Inputs: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$ ; NoL 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 wil<ݻ$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U7"Wl 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?n 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$i; 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 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 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 size 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 wills3 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 - Int=P;m$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U%"hernal 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),- ; (Physical 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$: BSBW LD_DEAL_FWIRP ; Deallocate the FWIRP5 ADAWI #-1,LDIOB_W_IRPCNT(R2) ; Decrement ref. countR# BEQL 20$ ; Now complete the I/OQ BRW 75$320$: MOVL LDIOB_L_IRP(R2),R3 ; Restore correct IRPe;B; Set the return status and the accumulated byte count in the IRP.;c> MOVW LDIOB_W_IOST(R2),IRP$L_IOST1(R3); Copy the return status/ MOVL LDIOB_L_ABCNT(R2),- ; And the byte count  IRP$L_IOST1+2(R3)T+ CLRW IRP$L_IOST1+6(R3) ; Zero unused wordB5 REMQUE LDIOB_L_QFL(R2),R4 ; Remove LDIOB from queuei;-2; LD_SAVE_TRACE_ALT calls LD_TRACE which may fork!;_' PUSHAB 70$ ; Return in case of fork1< BSBW LD_SAVE_TRACE_ALT ; Save trace data and dealloc LDIOB5 JSB G^IOC$POST_IRP ; Insert IRP back in post queueW. 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,R4J. JSB @UCB$L_FPC(R5) ; Resume the fork thread POPR #^M)40$: .IF DF MDDRIVER_WORKAROUND ;wM; Work around MDdriver bug. If an erase function is given to DECRAM, then the0J; iosb does not contain the bytecount but zero. In a certain configurationK; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attemptsRL; to erase a number of blocks, and it looks in the returned bytecount to see; how much is left to do.o;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$W_FUNC(R3),#IO$_DSE ; Erase?t BNEQ 50$ ; No @ MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount50$: .ENDC .IF DF DUDRIVER_WORKAROUNDL;_M; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueingrM; 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,rC; 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 thingso; to go wrong.;e PUSHL R5 ; Save logical UCBA8 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'se0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-d PRESERVE=NO,- CONDITION=RESTORE60$: MOVL (SP)+,R5 .ENDC;  RSB ; Return to IOPOST rtnw;h@; 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.;aJ70$: BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread475$: FORKUNLOCK LOCK=UCB$B_FLC>pm9$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1U"yK(R5),- ; Release lock NEWIPL=#IPL$_IOPOST,- PRESERVE=NO,- CONDITION=RESTORE RSB ; Either ;f UNIVERSAL_SYMBOL POST_PACKACK;POST_PACKACK:# EXTZV #IRP$V_FCODE,#IRP$S_FCODE, -h0 IRP$W_FUNC(R3),R1 ; Extract the function code" CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; NoI4 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 at ; drive connect time. ;u8 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, tracksB$ UCB$B_SECTORS(R5) ; and cylinders0 MOVL UCB$L_MAXBLOCK(R4),- ; Copy maximum block UCB$L_MAXBLOCK(R5)10$: RSB e- .SBTTL LD_CANCEL, Generic Cancel I/O routine$;+++(; LD_CANCEL, Generic Cancel I/O routine;w; Functional description: ;;<; This routine cancels all outstanding I/O for the specified;; channel and PID. We also check if the IRP is in the trace <; wait queue and remove that if true. We also check if there2; was a watchpoint suspended and process that too.;D ; Inputs:t; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRPR ; R4 - PCB ; R5 - UCB#; R8 - Cancel reason code, one of : *; CAN$C_CANCEL If called through $CANCEL; system service.*; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service.; ; Outputs:;o5; The routine must preserve all registers exept R0-R3C; ;--- UNIVERSAL_SYMBOL LD_CANCEL? ;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 thread 5 BSBW LD_CANCEL_WATCH ; Cancel suspended watchpointu3 BBC #UCB$V_DELETEUCB,- ; Only if we're going awayi UCB$L_STS(R5),20$$A BICL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Reset cluster wide visible : MOVL UCB$L_LD_CPID(R5),UCB$L_CPID(R5); Restore charge pid5 MOVW UCB$W_LD_CHARGE(R5),- ; Restore charged amountA UCB$W_CHARGE(R5)20$: RSB ; Return7 ,( .SBTTL LD_CANCEL_IO, Cancel I/O routine;+++3; LD_CANCEL_IO, Cancels I/O operations in progresss;l; Functional description:M;W<; This routine cancels all outstanding I/O for the specified;; channel and PID. We search the active IO list for LDIOB's0<; 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 physicals9; disk it's cancel IO routine is called, with the correctP ; registers.; ; Inputs:n;B; R2 - Channel index numbere; R3 - IRP from UCB$L_IRP0 ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code ;t ; Outputs:;$5; The routine must preserve all registers exept R0-R3N5; The routine may set the UCB CANCEL bit in UCB$W_STS ;S;--- UNIVERSAL_SYMBOL LD_CANCEL_IO;LD_CANCEL_IO: PUSHL R6+8 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 doneC PUSHL R0 ; Save R0A CMPL LDIOB_L_PID(R1),IRP$L_PID(R3) ; Is this the right process ?n BNEQ 20$ ; Noo+ MOVL LDIOB_L_IRP(R1),R0 ; Get IRP addressr6 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 lists30$: MOVL (SP)+,R6 RSB940$: MOVAL LDIOB_L_FWDQFL(R1),R0 ; Extract forwarded IRPr MOVL R0,R6 ; Copy it to R0+ MOVL (R0),R0 ; Get next packet in queuef 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 FLCKn< 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) 9 MOVAL UCB$L_IOQFL(R5),R3 ; Get addr. of wait queue listr 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 ?w' BEQL 80$ ; Not found, call CANCELIOh" 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 ? 6 BICW2 #IRP$M_FUNC,IRP$W_STS(R3) ; Clear buffered read970$: MOVW #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return status,5 JSB G^IOC$POST_IRP ; Insert IRP back in post queueB BRB 90$080$: MOVL UCB$L_DDT(R5),R0 ; Get address of DDT7 MOVL UCB$L_IRP(R5),R3 ; Get current IO packet address$, JSB @DDT$L_CANCEL(R0) ; Call cancel IO rtn490$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-O PRESERVE=NO,- CONDITION=RESTORE1 POPR #^M ; Restore saved registersD BRW 20$ ; Continue search Q1 .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routinec;+++,; LD_CANCEL_TRACE, Cancel trace I/O routine;s; Functional description:V;' REMQUE IRP$L_IOQFL(R1),R3 ; Remove itL5 JSB G^IOC$POST_IRP ; Insert IRP back in post queue, POPR #^MS20$: RSB J1 .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routineo;+++,; LD_CANCEL_WATCH, Cancel watch I/O routine;e; Functional description:i;5;; This routine will check if there's a suspended watchpointG:; 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 (itr9; comes from UCB$L_IRP), so we check against the PID fromr ; the PCB.;f ; Inputs:s; ; R2 - Channel index number ; R3 - IRP from UCB$L_IRPc ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codeP;0 ; Outputs:; 5; The routine must preserve all registers exept R0-R3g;M;---! UNIVERSAL_SYMBOL LD_CANCEL_WATCHs;LD_CANCEL_WATCH: 5 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active?e BEQL 40$ ; No ? MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entrys MOVL R0,R1 ; Save for latere010$: MOVL LDWATCH_L_FLINK(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 40$ ; Yes, whole list dones9 TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue?  BEQL 10$ ; No- PUSHR #^M; MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listheadR MOVL R0,R1 ; Save for laterM/20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irp, 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$ ; No 6 CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; No07 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return status_' REMQUE IRP$L_IOQFL(R1),R3 ; Remove it,5 JSB G^IOC$POST_IRP ; Insert IRP back in post queuef POPR #^ME7 DECL LDWATCH_L_SUSPCNT(R1) ; Count the one we removedo BRW 10$230$: POPR #^M ; Restore, continue scan BRW 10$40$: RSB N. .SBTTL LD_ALLO_LDIOB, Allocate I/O data block;+++(; LD_ALLO_LDIOB, Allocate I/O data block;$; Functional description: ;SG; This routine allocates and initializes an I/O datablock which is usedXH; for I/O requests as well as trace information. It tries to get a blockG; from the free queue, if that fai?5J$LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1UV"ls we get one from the system's pool. ;v ; Inputs: ;D; R5 - UCB addressc;t ; Output:P; ; R0 - StatusB; R2 - LDIOB addresst; A; The routine must preserve all registers except R0 and R2.q;a;--- UNIVERSAL_SYMBOL LD_ALLO_LDIOBi;LD_ALLO_LDIOB:e> REMQUE @UCB$L_LD_IOBFL(R5),R2 ; Get a packet from free queue BVC 20$ ; Branch if got oney PUSHL R1e/ MOVZWL #LDIOB_K_LENGTH,R1 ; Get packet length 3 JSB G^EXE$ALONONPAGED ; Allocate packet from pool ) BLBC R0,10$ ; Check the return statush- MOVW R1,LDIOB_W_SIZE(R2) ; Setup size fieldt6 MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field'10$: MOVL (SP)+,R1 ; Restore registero! BLBC R0,30$ ; Return on error @20$: 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 queueR LDIOB_L_FWDQFL(R2) MOVAL LDIOB_L_FWDQFL(R2),-B LDIOB_L_FWDQBL(R2). CLRB LDIOB_B_RSVD(R2) ; Clear reserved field" MOVZWL #SS$_NORMAL,R0 ; Success30$: RSB ; ReturnA L- .SBTTL LD_ALLO_FWIRP, Allocate a forward IRPN;+++'; LD_ALLO_FWIRP, Allocate a forward IRP_;D; Functional description :; G; This routine allocates a FW IRP. It first tries to grab one from therH; free queue. If this fails, it allocates one from pool, and initializes; it.R;o ; Inputs :;R ; R5 - UCB; ; Outputs : ;u; R3 - New allocated IRP ; R4 - LDIOB ; R5 - UCB; ;--- UNIVERSAL_SYMBOL LD_ALLO_FWIRPd;LD_ALLO_FWIRP: PUSHR #^M0 MOVZWL #IRP$K_LD_IRPLEN,R1 ; Get packet length> REMQUE @UCB$L_LD_IRPFL(R5),R3 ; Get a packet from free queue BVC 10$ ; Branch if got oneI3 JSB G^EXE$ALONONPAGED ; Allocate packet from pool ) BLBC R0,20$ ; Check the return status  MOVL R2,R3 ; Copy it to R310$: PUSHR #^M, 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 #^MR RSB ; ReturnS20$: BUG_CHECK INCONSTATE,FATALS - .SBTTL LD_DEAL_FWIRP, Deallocate forward IRPV;+++'; LD_DEAL_FWIRP, Deallocate forward IRPP;D; Functional description :;_>; This routine restores forward IRPs in the FW IRP free queue.;a ; Inputs :;n ; R0 - FWIRP ; R5 - UCB;o ; Outputs :d;t ; R5 - UCB; ;--- UNIVERSAL_SYMBOL LD_DEAL_FWIRP ;LD_DEAL_FWIRP:e8 INSQUE IRP$L_IOQFL(R0),- ; Insert packet in free queue @UCB$L_LD_IRPBL(R5)Q RSB ; Return  n;+++!; LD_DEAL_LDIOB, Deallocate LDIOB); ; Functional description :;37; This routine restores LDIOBs in the LDIOB free queue.;0 ; Inputs :;N ; R0 - LDIOB ; R5 - UCB;D ; Outputs :O;m; None;t;--- UNIVERSAL_SYMBOL LD_DEAL_LDIOBo;LD_DEAL_LDIOB: 8 INSQUE LDIOB_L_QFL(R0),- ; Insert packet in free queue @UCB$L_LD_IOBBL(R5)v RSB ; Returnh u* .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 dataP?; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypointE;L; Functional description:O;EI; This routine is called just before going to REQCOM to save the eventuald ; trace data.s;; Inputs for LD_SAVE_TRACE:,; ; R0/R1 - Return status=9; R3 - address of the IRP (I/O request packet)B9; R5 - address of the UCB (unit control block) ;VB; This routine must preserve all registers except R2 and R4.; ; Inputs for LD_SAVE_TRACE_ALT:L; 9; R3 - address of the IRP (I/O request packet) "; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)e;eE; This routine must preserve all registers except R0-R2 and R4.w;t;--- UNIVERSAL_SYMBOL LD_SAVE_TRACEt;LD_SAVE_TRACE:-% MOVL R3,UCB$L_IRP(R5) ; Restore IRPb, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got onee BRW 30$!5$: MOVQ R0,-(SP) ; Save status - BSBW LD_ALLO_LDIOB ; Allocate pool to hold1 ; temporary tracedata  BLBC R0,20$ ; Exit on error1 MOVQ (SP)+,LDIOB_Q_STAT(R2) ; Save in temp IOSBs MOVL R2,R4_ MOVL IRP$L_MEDIA(R3),-) LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; BytecountI MOVW IRP$W_FUNC(R3),-" LDIOB_W_FUNC(R4) ; Functioncode' CLRL LDIOB_L_PID(R4) ; Assume no pid 7 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 ?" 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),-_5 LDIOB_Q_ST_TIME(R4) ; Start time (Same as end timeR" ; for synchronous functions). CLRL LDIOB_L_ELAPSED(R4) ; Zero elapsed time BRW LD_TRACE_+20$: ADDL2 #8,SP ; Recover scratch space30$: RSB# UNIVERSAL_SYMBOL LD_SAVE_TRACE_ALTu;LD_SAVE_TRACE_ALT:K% MOVL R3,UCB$L_IRP(R5) ; Restore IRP$, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 20$ ; Not there, quit;n@; 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.F; 5 MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start timef BEQL 20$ ; Not set( MOVQ IRP$L_IOST1(R3),- ; IOSB contents LDIOB_Q_STAT(R4)$ MOVL LDIOB_L_PID(R4),R0 ; Get IPID' CLRL LDIOB_L_PID(R4) ; Assume no pide7 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_TRACEN#20$: MOVL R4,R0 ; Point to LDIOBR) BSBW LD_DEAL_LDIOB ; Get rid of buffer RSB U$ .SBTTL LD_TRACE, Trace I/O IRP data;+++; LD_TRACE - Trace I/O IRP data ; ; Functional description:I;tD; This routine is called by the start I/O routine to log information); about the I/O request in a tracebuffer.i; H; 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 theoJ; 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 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.r; ; Inputs:y;o9; R3 - address of the IRP (I/O request packet)c"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)s;a:; The LDIOB in R4 will be deallocated when we're finished.;D>; 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 PCE020$: LOCK_TRACE ACCESS=WRITE,- ; Lock the mutex CONTEXT=SYSTEM: BLBS R0,40$ ; Got it3 MOVAB IRP$L_FQFL(R3),R3 ; Point to FKB within IRPt. 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 R49 MOVB UCB$B_FLCK(R5),FKB$B_FLCK(R3) ; Set proper spinlocka9 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Make sure synch is okayB SAVIPL=-(SP),-p PRESERVE=NO: INSQUE (R3),@UCB$L_LD_TRCMUTEXQBL(R5) ; Save IRP in queue! FORKUNLOCK LOCK=UCB$B_FLCK(R5),-U NEWIPL=(SP)+,-U PRESERVE=NO,- CONDITION=RESTORE$ RSB ; Return to caller's caller;t>; Fork thread, called with R5 = forkblock (is our FKB in IRP),#; R3 and R4 restored from forkblock$;A330$: SUBL3 #IRP$L_FQFL,R5,R3 ; Restore IRP pointerC% MOVL IRP$L_UCB(R3),R5 ; Restore UCB  BRB 20$ ; And try againw640$: PUSHL UCB$L_LD_TRCPC(R5) ; Restore Fork trac@XZ $LD063.B )%[VDBURG.LD.V63.SRC]LDDRIVER_VAX.MAR;1Ue PC PUSHL R4 ; Save R4/ MOVL UCB$L_LD_TRCBUF(R5),R4 ; Start of buffer 5 MOVL UCB$L_LD_TRCBUFPTR(R5),R1 ; Get current pointeri: ADDL3 #LDTRCENT_K_LENGTH,R1,R0 ; Point to next free place= ADDL3 UCB$L_LD_TRCBUFSIZ(R5),R4,R2 ; Calculate end of bufferx CMPL R0,R2 ; Past the end? BLEQU 60$ ; No7 MOVL R1,UCB$L_LD_TRCWRAP(R5) ; Flag buffer wraparound_- INCL UCB$L_LD_TRCLOST(R5) ; Count lost datas- MOVL R4,R1 ; Reset ptr to start of bufferA560$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENTo) 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 IRPo 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 mutex_- NEWDATA=YES ; Check if new data availablei/ MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O statust MOVL R4,R0 ; Point to LDIOBA) BSBW LD_DEAL_LDIOB ; Get rid of buffer$! MOVQ (SP)+,R0 ; Restore statusr RSB .< .SBTTL LD_OPCOM_AST, AST routine to send a message to OPCOM;+++7; LD_OPCOM_AST, AST routine to send a message to OPCOMi;M; Functional description:s;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 requestrJ; 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 thee; OPCOM.;o1; Layout of the stack we use for scratch storage:a;l#; 0(SP) - FAO control string length_$; 4(SP) - FAO control string address; 8(SP) - FAO return lengthp; 12(SP) - SNDOPR buffer lengthe ; 16(SP) - SNDOPR buffer address#; 20(SP) - FAO output buffer lengtht$; 24(SP) - FAO output buffer address; 28(SP) - PID; 32(SP) - Imagename; 36(SP) - DevicenameL; 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;n ; Inputs:R; J; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST);6; The routine must preserve all registers except R0-R1;---/ UNIVERSAL_ENTRY LD_OPCOM_AST,<^M>c%; .ENTRY LD_OPCOM_AST,^MV;S* 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$0+ MOVL #VBN_WP_LEN,(SP) ; FAO string lengtht3 MOVAB VBN_WP,4(SP) ; FAO conversion format (VBN)t8 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 volumen BRB 20$/10$: MOVL #LBN_WP_LEN,(SP) ; FAO string lengthx3 MOVAB LBN_WP,4(SP) ; FAO conversion format (LBN) 920$: MOVAB 60(SP),16(SP) ; SNDOPR messagebuffer addressR0 MOVZWL LDSNDOPRLST_W_FUNC(R2),40(SP) ; Function9 MOVL LDSNDOPRLST_L_LBN(R2),44(SP) ; Logical block numberR4 MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name- 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$ ; 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 listB# CMPL (R0),R0 ; Something there?r BEQL 10$ ; No0* 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 buffer-% PUSHAW 16(SP) ; FAO return length,1 PUSHAQ 12(SP) ; FAO control string descriptoro* CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; Trouble/ MOVZWL 8(SP),R0 ; Length of converted stringe> ADDL3 #8,R0,12(SP) ; OPCOM message header length + overhead2 MOVAB 12(SP),R0 ; Address of message descriptor) $SNDOPR_S (R0) ; Send message to OPCOMc BLBS R0,50$ ; Okay, CMPL R0,#SS$_MBFULL ; OPCOM mailbox full? BEQL 50$ ; Yes, message lost740$: BUG_CHECK INCONSTATE ; Issue a non-fatal bugcheckC;nC; Now get back into kernel mode to deallocate our parameter buffer.>; We are allowed to go into kernelmode since we're now running; in exec mode;i(50$: $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,-$ ARGLST=4(AP) ; ACB to deallocate RET V8 .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;+++3; LD_OPCOM_DEALLOC, dealloc opcom parameter bufferF;1; Functional description: ;R7; We will return the parameter buffer from LD_OPCOM_ASTn ; to pool.;_ ; Inputs:$;I; (AP) - address of ACB ;c6; The routine must preserve all registers except R0-R1;---( UNIVERSAL_ENTRY LD_OPCOM_DEALLOC,<^M<>>; .ENTRY LD_OPCOM_DEALLOC,^M<>;V2 MOVL (AP),R0 ; Get old parameterbuffer pointer( JSB G^EXE$DEANONPAGED ; Return to pool RET , .SBTTL LD_END, End of driverS;+++(; Label that marks the end of the driver;--- UNIVERSAL_SYMBOL LD_END&;LD_END: ; Last location in driver .END+*[VDBURG.LD.V63.SRC]LDMSG.MSG;1+,../@@ 4W-)0123KPWO5 6?4샞7"Y89G@@HJ .title LD Error messages .facility LD,1/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 4NOTODS2  .end!*[VDBURG.LD.V63.SRC]LD_BUILD.COM;1+,-. /@@ 4X H-)0123KPWO 5 657|(Y89G@@HJ #$ if f$getsyi("hw_model") .le. 1023$ then$! Vax$ on warning then goto nodecc&$ if p1 .eqs. "LINK" then goto linkv'$ cc/list=ld/machine/standard=vaxc ld $ goto next$nodecc:$ cc/list=ld/machine ld$next:$ on warning then continue$ message/list ldmsg4$ macro/obj=lddriver/lis=lddriver_vax lddriver_vax$linkv:+$ link/notrace/map ld,ldmsg,sys$input/optsys$library:vaxcrtl.exe/share ident="V6.3"&$ link/map lddriver,sys$input/optionbase=02$ link/sym/share/noexe lddriver,sys$input/option universal=*$ else$!Alpha&$ if p1 .eqs. "LINK" then goto linka:$ cc/list=ld/machine/nomember_alignment/standard=vaxc ld$ message/list ldmsgC$ macro/machine/warning=noinfo/obj=lddriver/list=lddriver_alpha - lddriver_alpha$linka:+$ link/notrace/map ld,ldmsg,sys$input/optident="Alpha V6.3"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,- !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 DRIVER_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$ endif!*[VDBURG.LD.V63.SRC]LD_DEFINES.H;1+,-./@@ 4@-)0123KPWO5 6KS7S-Y89G@@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 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]; BWh$LD063.B-)![VDBURG.LD.V63.SRC]LD_DEFINES.H;1@͕ 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; };C'4Y?hhH;1$89CX* #/*>uUX?4D4XjVJ>8!u}zc& ?}<.Hx'^A*`TJ{MzdmKFg[VRWnc{~~Ih5~q%SAW^83 iVgOX70%>?!l3z128 ):]ZK{s>P k$wbkLl9C!hu?R!6YL6YgV\9}Le([bK{,ITF=YAMqS\*" 7aotoFTR2 N#S3ScSQY)0jiwV3-_+?FG7)#X!C pJDr%47VC=lm+ J3!9b/B:Ro`"-uWF &I?tM3 ,56z 7QpX+2ef  %5N[*i!` CwK=UXh/#'Q]%Tfs *3(b`+y+}Gtlu\tL@sH?z[![ lxEWh[bQE-hXNKC .N3uN>&^ 2_D".z:cf O?97g;HN 7>CMh 5 %4Ms*<F|( 1-RxQ-Va+j@g.p\*Hh;bW+(Rd`w#<(I=WsY: 3lL1YLiwYRN e~-7LtyEekISJ=D}w9yzl/KEn. >eq\0wtayw;rB6ijXb V4 b ,_U,m6; G*t6"\dZpw*u7+pSA/NTne^NZU"3@J -dVp Ks F[$j,;AF|KaL^O,B2hXa wq 8 R!t\i+q,F\*9VgK)dVvl= fv.z!kkc{bpNqMg*}NO1 k3IBJ78DZpZm%F{ZK! Z}YI ~j^#`N~<&YcLA2| |*: gl+!LYHi_1Q38r CpB,L[`Nw h&m7!H;I=E@I==Mas# dEjd'HS2&AQZ|XH4bnz`-*`8D^)$1ST gJr!Ac"$12A!;)ErtAx&KYy"=^8_5nTZwq?)H V1D?P@Ze^hu[^ A&0)Ym8iVE#iQlP2mMn'E* ?NfO%|G-\DlKKL]z:[rgI*e  A3b03!=D'&8b@tc{g25je`mr0SORyD7v(Ez&dl Dh{{ [oow*_-UIhhEbKq^E8!Zxki~rmj5dl=XIbu5Bt75,o&hXp]|-I*`^dAG,W**57|$qv{21R=EAv'K-? gA}7,KwN dz?P_ o&,H;PMgXW@ %#uI@t(']hX;Qf3iy_ 0< ~DM/\ui'5tz+!sC< ~/EVuo{<{)eQt%SE4WS]E2eh:nfyn]>b{O(EA%OBj44tPxh~A)3TW0u;0UM-8zBWuWO"BzqI1\8$]~i 2Jk7KHN OQ|~)2#\1|!2|*r{7AtxJB?~mQ#cBVa@kQ\Laq- c:A I'xWkZ%=  {7xZD"3) A_Sq-~)i}}F:S`(4e8y&\P(rAvrWEF9FpFlUvN)o-; 'l9h(g.B+lQ7<~?s$,n(/M=HCm|;=, *zX}L-%ssOux3'j'\zgfOd 8;22S8l oQ:0VD y.xGYz>T[N^v 4<\wf>vM8f<\mlI ."TX\9sni-8'`b QEhjt+ M g<]5v(9nbj`\&+4I'09i (8};1uoXx5vu gM}i'&I!"AjyOE'o90|d IAjw'[*6VD|E Z.+zc(\v-sc5v{iy4oH{QOV QzU(e,@t^Rdci.X 2%*Q qZc+<^-TA\ ck5E^3;NEA .b|Q]Bg KlH~XLB@if@d@`,SRWm_}&4JEGTO9vYqmk."kO>K# U5Tk5P9<TXZ{&XR..9SkPHu`B=@^&4mb7}G"-LFiIW`6 >nyV .o8I2X4Xm]D.HJmS9"qN"XT* +w9r)VY".6>Ly ph8C'CFqNSPUh&we4?"[Ny $fpyl:<*F7yO5D(=:\,H^) ~I#Kb?PG e{x{%I O;D>Sqso6FodMp=[DpL)/:Vnb;r@8"`x^.9f(OES rQg^k6ygY?~*lhkfY|tYsupZ$x8BcS:=eH p }9l~vN>40#T8 T x^*WWJU[0wo1$DDL">0%;>[frjK*.V7Rj!\EP$`(4]Psv?DY@UO8VjNx<'xH7KF6 h$i_t(ZE&Lo0L|LaP7qJWr,GW~] tP!+zHMZE: KRh)V9xC 6`%kpi=q~Zqct%Weuobv1E v/(}"IkL+oL2ZZb3/l~eY)#b_0o;-tefm9)*:^2CEHm :80s%/&eChB<"Q 5iH4J%_m*d^`YG7I0im$a)\/I)s _EL 0Pu@H lAmC%YQbg"o:wE}q2{^{tJw)h(&OJ[5>k jn !06 RK`,ADP210cRq:,(X`X]Ev.F*/)-:"*)-mL%[upwVRF02mY @fv8afTYm_PZqc 78w}8B0W'78{ QXf9Z9(=*@\O-1@/V vR Jc!8ym y,+\LyB S}FFt!].7ka 0# Ry53P AA_y~Kmt 0ViC_6dv~26 =qu5:8\h#2X~34*Rw2l Bj!Rg4f] Tfk_H0h#e:M ^O~)Uy`Qi~> )Chzu]lZ/}P6NIq0]W: - \-]BRHj-asY[a>Di#JZ_X$"MQ/[@o_y/IEon+C xpJy{CZ3PUH& )^<&twx4*-S2o`?| yLUI" @$*ZJfmJF A8:Go _CSYy:*0Y7Ut]=c7&uh2m='SU#  IU6fM6n'e*X8wE\ZL:jFf[/Bj Zq:?X`>#bPIi;;ZA2H=X{3TL,00 $1[r<.Q 46~}qur%eX$tFjS8s9}w/mAMQw>O5swc+:~1=@ :pKG>6 yuW>"B#3yu(4iVWOOQ+y/@`_q G 5teSZ k+4$OT '>x3;O~a(iB4\vyQ%`efF*G VJX0Hqi$#if!ehaRQ^_*&;A9PY]RT3xlt%Ys{DcO.@# I'gp +(RSmLEkkB\ 6|PW4K6$odbct5/ORJo2v5 |aJwF/F^k6yTi]%6 M>S(P5[71}SMY;H?!5R9cwx}|' +]2J{\Qv=_hZMIVM;^ ~ uSf4SrH |7jl#P9Dk)4U^ !hwVpGWgo{Cr[L=i3)}[&gI r`!W+n~H5(O? dI_k/F_U N,).{uO$ES=> P] zMgpJJ9F`!v8DR*iv} %~ )e1]&\m140U)AW(1(?`EW( aWX~lY={&lh*~ q1.Lk:Vu(VnQ~Zj?w|no8q=+SL"Beo<{(UU4MqVj*JDXZ:-`;j@BxXKf<7;$~O+o2S3U$Y%3 KmVZEB}&`od#e,EVe;M`nj]m b7uctz2iPM G*C2^;WsJ1K]Fa  mMia2h /y]CY[@FgG3!=[wvD`Jjj^KRvr++g~2 Irt'\H.%nNd9IT;gn7/L:gU a_ZVsphaBd$t;.[ 3)i1t.W8!bf;!aTV]4JyB8Y1?6bx5G~qbE#_'eE3XM5<7u SU%7[q| X#XI6|"]]-rUp\56i)#Bm0Fo=HDe UJNn<94j -K3u4!Db`$ZK%6ti(uSu y3ZU2_ $7ekWd1B\b5Vl"{!Ev5BU- 5A]'6cpR_5J)SS?}.EItf=|mNmcqH0GIiqkb"dYhD';a\&!s<4~n0!?=2HM2{v9U%w{1@6wB-Z`_dBoq@e|2]}]yEi-@7iLD?dI\i% #?XC`0'1#TzvhWuqPjSHcu^}J `[F_dM)h4.9j_e4FVlRg7&BJ'% - 6$E&yB=8:Up=>Z,xHTi3/ pUa5aQ.w{F_vtC!Otf=%CWr F]_'-'O L%buS M V<MY$ATY9B[/!C*c;lR=uP"Nm&d,KaUJ&1$+K}xDEk|D{xlcw6n6af ]ZZd#Ah7RQs &.@Ihs =ol] )q"&aP\@=~0 jt~o~7?4C-Os `7jO42FD(qn%FPnju S A6Cg9^d e)NnS`O >$qRW:l, ]ib pLF7Z:U{?\Yg iEmgG"& $hn!FZEAO`~42o+jNezM%*9T?S\J&N= xf@(c& tF.3Z5 wv?KFgR #\{G&FS(jxd!^n#Z_p b,i*\p)_23gO $HSI!he,n,_hq<7-XH|nwiPXfE <#in8r>[|O\lb:t\p* 1} W7tRD /7m* K D.|,:OANHnJy21tbvnv4Y:`WpZxRh"C^=&a$BW_>&5w3 g]9Vl IP aI;~fhKD6>fx415[7&PU0UHv#,^^or+-G} rvUZTzs Ia?,5* =L/E_C%.8o>)1E~\-W)g7;b `5q ~=[5+jV]\_0T|(yCdg> |-bgG=N_]o6E9,uo7C,(ygm/XTj}O!223 W0k8dQ% =vRv{{(.i74dj?R ~ $^(w,jY!1XA+BGm]K' GlI&+ fUxk@Rd='7CS5$r!5yy\8Wp o<y zH3|sx9=~=gX-c4.p: E-4 cF:\<^ sD)au@ ;~,, U4MeT6=  TNVN( +X?PrP So tzc;o3 v&\IMf mO"rL{H[ VftXLf$9+%pT~= a}jg!#1vY|#*<4QM;c6v%Zc~&>NIJfQLr{UpPy,#$Qv>_`L"p!w|W^!U"Z c$v"D:#j|slw7uRPOi&.}CHE 4DR EY;2zR9h^$&k I9G61eGK6#VSdG21v ]aj Bu>FIy4\Y`) @OGYbiS28 ]vIB$2N^\?`9lJu'HZ C9i`d W)m]"$9<7+oXEHe!i2cFykX6#f[A4.{wEPg5|jTbK72\N.M 2Ah;5Bo{,dr1nf_zmbo\_1IB0^OOlN2u yf4?2 i;*$ig}z4I5YYw4Q&<({5E-UO<]0GEXH[`\L/3,YvU 1N)IaV`(]PV]Va:.*rLQj$gi&ZFY[_[4&z$9M8R%.L -FW9GFq2AkEp._FQ+2fRdMgV#6u3Xa8cCgW*zZoD,.Bc:LVE ]B6J2f/+W FO-%3g e3,x/d5qSMB^&F~n /xUgb+]:nn#i8.|{w b=Zp &;TX2>C L\z1u'kn|=3$]`B5OP4AJ=NOI$r,kX?rJ( bsz>H&gFS4O 7mTiP2M[# :P`iP_#INY0}[CGe| \-?![]bf2OYhqYx$Ej#e"{1^@L>CpIN! w7}/L'x^fl1>o%bY0.\Kv!.7lN``7}P(Bh/W^i)y: @w(\lqKD*rw [|/+?:|^f *r/q33 +J\aXPa? ( V8pMXP UX]2Nf:G$9G hcu}\1R40"pw9Hu r.XBkZX 1eW4& 9KFdmyQcxM~l=oq:Z |~P4TQ`k&8M+nbU7E c${>iE2?\F/wde02aFi-Y6bW}. BvIPga&F"2 )A]U1_mSN5k&$Qp>k?&wiRPCm'Vn>[}fbw 5kk:5x9?0OZz,r{T  ^7wNiDf.'aoB9-rd+ v/hQXB7F+njtH {71Fx@bi-Gc"1 l69Mb  upOZ>{e21.Ka\5ZO6 C7rE]1y%rrGNmgZN.F:q%#^e$ qo.*VZh c~g$bXI1r> H-wfC}u*C+,~"A7uxXB6jS5NDF)YmW02yvh-]K}NN57}| gYmS|6$!urlV"Y^qR0+[-w#."-ZhBIe,e^}L2>@8ef)AbgZXAZM_deW, yC^Rxy'!'lGl!%3;1