>$LD051.BFPLD051.BBACKUP/NOASSIST/NODISMOUNT/COMMENT=VAX/VMS SPKITBLD Procedure/INTER/LOG/VERIFY [.SRC]*.* USER2:[VDBURG.LD.FWARE]LD051.B/LABEL=(LD)/SAVE/BLOCK=9000/GROUP=25/NOINIT/NOREWIVAX/VMS SPKITBLD Procedure VDBURG !H:mV6.1 _UTRYIT::  _$1$DUA101: V6.1 $*[VDBURG.LD.FWARE.SRC]LD.C;1+,"_I./!H 4q-v]0123KPWO56a.m7 Nm89G!HHJ /* 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 vdburg@utrtsc.uto.dec.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. 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:& - LD TRACE [/SIZE=xxx] [/RESET] LDan: - LD TRACE/STOP [/ALL] [LDan:] - LD NOTRACE LDan:? - LD WATCH LDan: lbn [,lbn...] [/FUNCTION=READ,WRITE,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]] [/FUNCTION[=TEXT,HEX]] LDan:*/#include "ld.h"main(){ int stat; int length; char cmdbuf[256]; 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]; 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)D lib$signal(&ld_nosupport, 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) { p۳$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1q" 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];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); getdevnam(chan, s_device); shflag++; } else { unit = get_unit(s_device, 0);' if (unit == 0) /* Don't use unit 0 */9 lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); set_seed(unit);? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0); getdevnam(chan, s_device);# if (get_unit(s_device, 1) != unit)2 lib$signal(&ld_dupunit); /* Duplicate unit */ } if (alloclass > 0) {I 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); }/ 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};> stat = lib$getdvi(&DVI$_DEVNAM, &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)int num;{ struct dsc sdev; int stat; short chan; struct iosb iosb; sdev.addr = "LDA0:"; 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 */T lib$signal(&ld_nosupport, 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;? stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,0 numbuf, 0, 0, 0, 0, func); /* Enable trace */! signal_error(stat, iosb.status);/ stat = sys$dassgn(chan);/* Deassign channel */ signal_error(stat, 0); }}/* Disable trace*/void notrace(){ struct dsc sdev; int stat; short chan; struct iosb iosb;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */P lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,= 0, 0, 0, 0, 0, LDIO_DISABLE_TRACE); /* Disable trace */$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Enable write protect*/void protect(){ 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 */P lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,* 0, 0, 0, 0, 0, LDIO_ENABLE_PROTECT);$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Disable write protect*/void noprotect(){ 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 */P lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,+ 0, 0, 0, 0, 0, LDIO_DISABLE_PROTECT);$ signal_error(stat, iosb.status);3 stat = sZ$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1q#""ys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Enable watch*/ void watch(){ struct dsc sdev; int stat; short chan, qchan; int func; int cnt, i, size; struct iosb iosb;) struct watchpt *wpt, *wpt1, wptindex; int *blkpnt, wptcnt;E int function_read, function_write, function_code, action_suspend,. action_crash, action_error, action_opcom;, int function_code_val, action_error_val;' int resume, index, index_val, file; char *filename; char devspc[256];/* Get switches */! resume = getqual(&sw_resume); index = getqual(&sw_index);/ function_read = getqual(&sw_function_read);1 function_write = getqual(&sw_function_write);/ function_code = getqual(&sw_function_code);1 action_suspend = getqual(&sw_action_suspend);- action_crash = getqual(&sw_action_crash);- action_opcom = getqual(&sw_action_opcom);- action_error = getqual(&sw_action_error); file = getqual(&sw_file);9 action_error_val = SS$_BUGCHECK; /* Default return */ if (action_error) {3 action_error_val = getqualvalue(&sw_action_error); if (action_error_val == 0)% action_error_val = SS$_BUGCHECK; } if (function_code)5 function_code_val = getqualvalue(&sw_function_code);B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */P lib$signal(&ld_nosupport, 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); if (file) {$ filename = getqualstring(&sw_file);% open_file(filename); /* Open file */8 getdevnam(chan, devspc); /* Check if on other device */> devspc[strlen(devspc)-1] = '\0';/* Zap trailing ':' */' if (strcmp(devspc,ld_nam.nam$t_dvi+1))N lib$signal(&ld_fileonother, 0); /* File not allowed to be on other device */ }2 if (index) { /* /INDEX? (for WATCH/RESUME) */% index_val = getqualvalue(&sw_index);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,H 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */! signal_error(stat, iosb.status);. wptcnt = iosb_lw; /* Number of watchpoints */- if ((index_val < 1) || (index_val > wptcnt))< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ if (wptcnt) {, size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size))) signal_error(SS$_INSFMEM, 0);: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,@ wpt, size, 0, 0, 0, LDIO_GET_WATCH); /* Get watch data */% signal_error(stat, iosb.status);6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action; wptindex.func = wpt->func;% wptindex.retcode = wpt->retcode;! wptindex.flags = wpt->flags; wpt = &wptindex; cnt = 1; /* One entry */ } } else {> blkpnt = getlist(&sw_lblock, &cnt); /* Get LBN's to handle */ if (cnt) {7 if (!(wpt = malloc(cnt * sizeof(struct watchpt)))) signal_error(SS$_INSFMEM, 0);4 for (i = 0, wpt1 = wpt; i < cnt; i++, wpt1++) {' wpt1->lbn = *blkpnt++; /* Fill lbn */ if (function_read) wpt1->func = IO$_READPBLK; else if (function_write)! wpt1->func = IO$_WRITEPBLK;9 else if (function_code) { /* User specified function */% wpt1->func = function_code_val;P switch (function_code_val & IO$M_FCODE) { /* Function without modifiers */ case IO$_READPBLK: case IO$_WRITEPBLK: case IO$_WRITECHECK: case IO$_DSE:1 wpt1->flags = 0; /* Function with transfer */ break; default: if (file)# lib$signal(&ld_noreadwrite, 0);0 wpt1->flags = FLAGS_NOLBN; /* Special case */! wpt1->lbn = 0; /* Zap lbn */. break; /* For functions without a lbn */ } } if (file) {9 wpt1->flags = FLAGS_FILE; /* Signal 'file' Function */( wpt1->sbk = &sbk; /* Point to sbk */- if (wpt1->lbn == 0) /* VBN starts at 1 */ lib$signal(&ld_vbnerror, 0); }& if (action_suspend) /* Set action */* wpt1->action = WATCH_ACTION_SUSPEND; else if (action_crash)( wpt1->action = WATCH_ACTION_CRASH; else if (action_opcom)( wpt1->action = WATCH_ACTION_OPCOM; else if (action_error) {( wpt1->action = WATCH_ACTION_ERROR;= wpt1->retcode = action_error_val; /* Error to return */ } else wpt1->retcode = 0; } } }/*< If we enable a watchpoint on a file we have to use the same< channel through which we accessed the file. In that way the; driver can validate that the file is opened with cathedral windows.*/ if (file) qchan = fchan; else qchan = chan; if (resume)8 func = LDIO_RESUME_WATCH; /* Resume suspended thread */ else2 func = LDIO_ENABLE_WATCH; /* Enable watchpoint */: stat = sys$qiow(0, qchan, IO$_LD_CONTROL, &iosb, 0, 0, wpt, cnt, 0, 0, 0, func);$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0); if (file)' close_file(filename); /* Close file */}/* Disable watch*/void nowatch(){ struct dsc sdev; int stat; int chan;) struct watchpt *wpt, *wpt1, wptindex; struct iosb iosb; int cnt; int i, size; int *blkpnt, wptcnt; int index, index_val;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */P lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0); index = getqual(&sw_index); if (index) { /* /INDEX? */% index_val = getqualvalue(&sw_index);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,H 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */! signal_error(stat, iosb.status); wptcnt = iosb_lw;- if ((index_val < 1) || (index_val > wptcnt))< lib$signal(&ld_wptnotfound); /* Watchpoint not found */ if (wptcnt) {, size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size))) signal_error(SS$_INSFMEM, 0);: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,@ wpt, size, 0, 0, 0, LDIO_GET_WATCH); /* Get watch data */% signal_error(stat, iosb.status);6 wpt += index_val - 1; /* Point to correct data */. wptindex.lbn = wpt->lbn; /* Copy block */# wptindex.action = wpt->action; wptindex.func = wpt->func;% wptindex.retcode = wpt->retcode;! wptindex.flags = wpt->flags; wpt = &wptindex; cnt = 1; /* One entry */ } } else {7 blkpnt = getlist(&sw_lblock, &cnt); /* Get lbn list */ if (cnt) {7 if (!(wpt = malloc(cnt * sizeof(struct watchpt)))) signal_error(SS$_INSFMEM, 0);4 for (i = 0, wpt1 = wpt; i < cnt; i++, wpt1++) {< wpt1->flags = FLAGS_REMOVE_ALL; /* Flag all of this lbn *// wpt1->lbn = *blkpnt++; /* Fill lbn to kill */ } } }9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,A wpt, cnt, 0, 0, 0, LDIO_DISABLE_WATCH); /* Disable watch */$ signal_error(stat, iosb.status);3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);}/* Show watch info*/void show_watch(){ struct dsc sdev; int stat; int chan; struct watchpt *wpt; struct suspend_list *slist; struct iosb iosb;' int wptcnt, blkcnt, slistcnt, size; int *blkpnt;B s_device = getqualstring(&sw_device); /* Get LD device name */: if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */P lib$signal(&ld_nosupport, 1, fulldevspec(s_device)); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device);* blkpnt = getlist(&sw_lblock, &blkcnt);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* Assign channel */ signal_error(stat, 0);9 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,K 0, 0, 0, 0, 0, LDIO_GET_WATCH | LDIO_M_INQUIRE); /* Get watch size */$ signal_error(stat, iosb.status);3 if (wptcnt = iosb_lw) { /* Something to do ? */( size = wptcnt * sizeof(struct watchpt); if (!(wpt = malloc(size)))" signal_error(SS$_INSFMEM, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,< wpt, size, 0, 0, 0, LDIO_GET_$ $LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1q3WATCH); /* Get watch data */! signal_error(stat, iosb.status); }9 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 ? *// size = slistcnt * sizeof(struct suspend_list); if (!(slist = malloc(size)))" signal_error(SS$_INSFMEM, 0);6 stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,G slist, size, 0, 0, 0, LDIO_GET_SUSPEND_LIST); /* Get suspend list */! signal_error(stat, iosb.status); }3 stat = sys$dassgn(chan); /* Deassign channel */ signal_error(stat, 0);N display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt); /* Show it */}@void display_watch(wpt, wptcnt, blkpnt, blkcnt, slist, slistcnt)struct watchpt *wpt; int wptcnt; int blkpnt[]; int blkcnt;struct suspend_list *slist; int slistcnt;{ int i, j; int index, index_val; int *bpt; int found, printed; struct suspend_list *spt; char albn[9]; char filename[256];1 if (index = getqual(&sw_index)) /* /INDEX? */5 index_val = getqualvalue(&sw_index); /* get value */* if (wptcnt) { /* Something to do ? */ printed = 0;@ for (i = 0; i < wptcnt; i++, wpt++) { /* For all watchpoints */ found = 0;< if (blkcnt) { /* Check if in list the user specified */3 for (j = 0, bpt = blkpnt; j < blkcnt; j++, bpt++) if (*bpt == wpt->lbn) { found++; break; } } else {& if (index) { /* Or check by index */ if (index_val == i + 1) found++; } else found++; } if (found) {. if (!printed) { /* Print header only once */N printf("Index LBN Action Function Error return code\n");W printf("--------------------------------------------------------------------\n"); } printed = 1; if (wpt->flags & FLAGS_FILE) {0 create_filename(s_device,&wpt->fid,filename);) printf(" %s:\n",filename); } 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 */}/*) Get a list of lbn's from the commandline*/int *getlist(sw, cnt) char *sw; int *cnt;{ int more; 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; more = -1; blkpnt = blklist; for (;;) {6 *blkpnt = getmulqualvalue(sw, &more); /* Get entry */% if (more < 0) /* Not specified ? */ return (blklist); *cnt += 1;# if (more) { /* more coming up? */+ /* Get new block to accomodate size */A if (!(blklist = realloc(blklist, (*cnt + 1) * sizeof(int))))8 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ blkpnt = blklist + *cnt; } else break; } return blklist;}/* Stop running trace*/void stop_trace(){ int stat; int loop = 0; char matchname[256]; struct dsc matchdev =# {sizeof(matchname), matchname}; 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 */F 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);3 matchname[rlen] = '\0'; /* Place terminator */= stop_one(matchname, 0); /* Stop trace for this device */ }, } 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;{ struct dsc lknam; int stat; char *qname; struct iosb iosb; int lockid; int retlen; int count; int i; int found; int grp, mem; union prvdef oldpriv; struct dsc sfilename = {strlen(which), which}; struct lkidef locks[10]; struct lkidef *p; struct itmlst item[] =0 {sizeof(locks), LKI$_LOCKS, &locks, &retlen, 0, 0, 0, 0}; struct itmlst jpi_item[] =" {8, JPI$_CURPRIV, &oldpriv, 0, 4, JPI$_GRP, &grp, 0, 4, JPI$_MEM, &mem, 0, 0, 0, 0, 0};9 if (get_unit(which, 1) == 0) { /* Don't use unit 0 */ if (single)D lib$signal(&ld_nosupport, 1, which); /* Show it to the world */ else return; }= 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 */ p = locks; found = 0;& for (i = 0; i < count; i++, p++) {B if (p->lki$l_lkid == ld_lksb.lockid) /* Disregard our own lock */ continue; found = 1;U stat = sys$getjpiw(0, 0, 0, &jpi_item, &iosb, 0, 0); /* Get privs+ group + member */! signa@l_error(stat, iosb.status);9 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 ? *// if (!oldpriv.prv$v_world) /* We need WORLD */, 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 */, lib$signal(&ld_nogrouppriv, 1, which); } }N stat = sys$enqw(0, LCK$K_EXMODE, &ld_lksb, /* Convert to fire blocking ast *// LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK, 0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0);C stat = sys$deq(ld_lksb.lockid, 0, 0, 0); /* Get rid of our lock */ signal_error(stat, 0); break; } if (!found)8 lib$signal(&ld_conttracenotact, 1, fulldevspec(which));}/* Show trace info*/void show_trace(){ struct dsc sdev; struct dsc lknam; int stat; int chan; int trcsize; int trcnum; int contin; int qstat; int func; int inited; int overrun; struct iosb iosb; 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 */ if (!s_device); lib$signal(&ld_confqual); /* Conflicting qualifiers */< s_device = fulldevspec(s_device); /* Get full devicespec */7 if (get_unit(s_device, 1) == 0) /* Don't use unit 0 */G lib$signal(&ld_nosupport, 1, s_device); /* Show it to the world */ sdev.addr = s_device; sdev.len = strlen(s_device); 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/.$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1qh"D, &iosb, 0, 0,3 0, 0, 0, 0, 0, LDIO_GET_TRACE | LDIO_M_INQUIRE);! signal_error(stat, iosb.status);9 trcnum = iosb_lw; /* Size of tracebuffer (in entries) */- trcsize = trcnum * sizeof(struct trace_ent);< 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)9 lib$signal(&ld_conttraceact, 1, fulldevspec(s_device)); signal_error(stat, 0);L stat = sys$enqw(0, LCK$K_PRMODE, &ld_lksb, /* Write lock value block */3 LCK$M_SYSTEM | LCK$M_CONVERT | LCK$M_VALBLK, 0, 0, 0, 0, 0, 0, 0); signal_error(stat, 0);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); } inited = (contin > 0) ? 0 : -1; do {: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,< trcbuf, trcsize, 0, 0, 0, func); /* Get trace info */ signal_error(stat, 0); stat = iosb.status;" if (stat == SS$_DATAOVERUN) {2 overrun = iosb_lw; /* Save number of overruns */ iosb_lw = trcnum; } else { 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 */ break; } else {% if (iosb_lw) /* Anything there ? */K process_trace(trcbuf, iosb_lw, 0, overrun, /* Process the contents */, nodename(), cvttime(0, 0, 0), inited); else> lib$signal(&ld_notrcdata); /* No trace data available */ } inited = 1; } while (contin);/ stat = sys$dassgn(chan);/* Deassign channel */ signal_error(stat, 0);' free(trcbuf); /* Get rid of buffer */ }}/* Process the trace buffer*/Cvoid process_trace(buf, size, ovrbuf, overrun, nname, when, inited) char *buf; int size;struct ovrrun *ovrbuf; int overrun; char *nname; char *when; int inited;{ static int q_bin, q_entries;0 static int q_blocks, q_version_limit, q_vsw; static int v_end1, v_end2; static int re_open = 0; static int written = 0; 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 ? */ if (q_entries > 0) {C v_end1 = getqualvalue(&sw_entries); /* Get #of entries to show */D v_end2 = getqualvalue(&sw_entries); /* Get second part (if any) */ }1 q_bin = getqual(&sw_binary); /* /BINARY ? */4 q_blocks = getqual(&sw_blocks); /* /BLOCKS ? */ if (q_blocks)6 q_blocks = getqualvalue(&sw_blocks); /* Get value */? q_vsw = getqual(&sw_version_limit); /* /VERSION_LIMIT ? */ if (q_vsw) {D q_version_limit = getqualvalue(&sw_version_limit); /* Get value */ if (q_version_limit >= 32767) q_version_limit = 32767; if (q_version_limit <= 0) q_version_limit = 1; q_version_limit--; }2 outflg = getqual(&sw_output); /* /OUTPUT ? */ if (outflg > 0)= outfilename = getqualstring(&sw_output); /* Get filename */ } if (outflg > 0) { if (q_bin)P outfilename = fullfilespec(outfilename, "LD_TRACE.DAT"); /* Provide default */ elseP 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 */ if (fclose(outfile) < 0): lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); } if (q_vsw)/ purge_file(q_version_limit, outfilename); }/*G We want to create a normal file with some nice RMS attributes. SinceI this can't be done with fopen() we will create the file first, and useI fdopen to setup the correct file pointers. This takes also care of the/ substitution of the defaults filename parts.*/ if (q_bin)9 outfilei = creat(outfilename, 0, "rfm=var", "ctx=bin"); else8 outfilei = creat(outfilename, 0, "rfm=var", "rat=cr"); if ((outfilei < 0) ||/ ((outfile = fdopen(outfilei, "a+")) == NULL))X lib$signal(&ld_outfilerr, 1, outfilename, vaxc$errno, 0); /* Can't open output file */ re_open = 1;) } else if (outflg < 0) /* /NOOUTPUT ? */ return; else+ outfile = stdout; /* Default output */ } end2 = 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 {& end1 = -end1; /* Make absolute */1 i = ((size - end1) < 0) ? 0 : (size - end1);& p = (struct trace_ent *) buf + i;/ recnum = i + 1; /* First one to display */ } } else {4 p = (struct trace_ent *) buf; /* Start of buffer */" end1 = size; /* Default = all */( recnum = 1; /* First one to display */ }2 if (end1 > size) { /* More than we've got? */ if (end2 > 0)5 lib$signal(&ld_pastdata); /* Past end of data */( 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 */# version = DATA_FILE_VERSION << 24;5 if ((inited <= 0) || ((written == 0) && q_blocks)) {T if ((fwrite(&version, sizeof(int), 1, outfile) != 1) || /* Data file version */K (fwrite(fulldevspec(s_device), 32, 1, outfile) != 1) || /* Devicename */9 (fwrite(nname, 32, 1, outfile) != 1) || /* Nodename */7 (fwrite(when, 24, 1, outfile) != 1)) /* Timestamp */Z lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ if (q_blocks)D written += sizeof(int) + 32 + 32 + 24 + (4 * 2); /* Header size */ }P if ((fwrite(&numpack, sizeof(int), 1, outfile) != 1) || /* Number of entries */R (fwrite(&overrun, sizeof(int), 1, outfile) != 1) || /* Number of overruns */T (fwrite(p, sizeof(struct trace_ent), numpack, outfile) != numpack)) /* Data */] lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ if (q_blocks) {) written += (2 * (sizeof(int) + 2)) +1 ((sizeof(struct trace_ent) + 2) * numpack);- if (written >= ((q_blocks - 1) * 512)) {+ re_open = -1; /* Set flag for new file */ written = 0; } } } else4 show_trace_data(p, numpack, recnum, outfile, nname,, s_device, when, inited, ovrbuf, overrun);F if ((outfile != stdout) && (inited < 0)) { /* Close output file */ if (fclose(outfile) < 0)< lib$signal(&ld_closerr, 1, outfilename, vaxc$errno, 0); }}/* Format all tracedata*/5void show_trace_data(p, numpack, from, outfile, node,- device, when, inited, ovrbuf, overrun)struct trace_ent *p; int numpack; int from;FILE *outfile; char *node; char *device; char *when; int inited;struct ovrrun *ovrbuf; int overrun;{ struct ovrrun *op;! int i, j, stat, func, ovrcnt;# static int pid, lbn, bytecount;) static int iosb, function, timestamp;I static int time_elapsed, time_absolute, time_combination, time_delta;+ static int function_hex, function_text;& static int iosb_hex, iosb_longhex;+ static int iosb_text, iosb_combination;n$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1q"U' 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);$ 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; }8 if ((header > 0) && (inited <= 0)) { /* /HEADER ? */A if (fprintf(outfile, " I/O trace for device %s\n %s", device, when) <= 0)\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */& if (node) /* Nodename available ? */6 stat = fprintf(outfile, " on node %s\n\n", node); else% stat = fprintf(outfile, "\n\n"); if (stat <= 0)\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */) *record = '\0'; /* Initially nothing */ if (number > 0) strcpy(record, "Entry "); if (timestamp > 0) { 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 "); } if (pid > 0)! strcat(record, " Pid "); if (lbn > 0)# strcat(record, " Lbn "); if (bytecount > 0) strcat(record, " Bytes "); if (iosb > 0) { if (iosb_hex) strcat(record, " Iosb[0] "); else if (iosb_longhex)' strcat(record, " Iosb[0] Iosb[1] "); else if (iosb_text) strcat(record, " Iosb "); else if (iosb_combination)$ strcat(record, " Iosb Count "); } if (function > 0)" strcat(record, " Function "); if (!(*record)), return; /* Nothing to display, quit */ j = strlen(record);/ record[j - 1] = '\n'; /* Zap trailing space */ 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';- if ((fprintf(outfile, "%s", record) <= 0) ||( (fprintf(outfile, "%s", tmp) <= 0))\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ }A 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; } } if (ovrcnt > 0)3 if (fprintf(outfile, "[%d trace record%slost]\n",. ovrcnt, ovrcnt == 1 ? " " : "s ") <= 0)] lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ } *record = '\0';< func = p->func & IO$M_FCODE; /* Strip function modifiers */D if ((func == IO$_SETMODE) || /* These functions don't have a lbn */ (func == IO$_PACKACK) || (func == IO$_AVAILABLE) || (func == IO$_UNLOAD))0 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); signal_error(stat, 0);) strcat(record, cvttime(&restim, 1, 1)); } else if (time_absolute) {0 strcat(record, cvttime(&p->start_time, 0, 1)); strcat(record, " ");. strcat(record, cvttime(&p->end_time, 0, 1));# } else if (time_combination) {; stat = lib$sub_times(p->end_time, p->start_time, restim); signal_error(stat, 0);0 strcat(record, cvttime(&p->start_time, 0, 1)); strcat(record, " ");) strcat(record, cvttime(&restim, 1, 1)); } else if (time_delta) {1 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) {= stat = lib$sub_times(prev_tim, p->start_time, restim); signal_error(stat, 0); strcat(record, "-"); } else signal_error(stat, 0); } else strcat(record, " ");- strcat(record, cvttime(&restim, 1, 0)); } 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];! prev_tim[1] = p->start_time[1]; } strcat(record, " "); } if (pid > 0) { /* Pid */# sprintf(tmp, "%08X ", p->pid); strcat(record, tmp); }+ if (lbn > 0) { /* Logical block number */# sprintf(tmp, "%10d ", p->lbn); strcat(record, tmp); }% if (bytecount > 0) { /* Bytecount */# sprintf(tmp, "%6d ", p->bcnt); strcat(record, tmp); }( if (iosb > 0) { /* I/O status block */ if (iosb_hex)& sprintf(tmp, "%08.8X ", p->iosb[0]); else if (iosb_longhex)9 sprintf(tmp, "%08.8X %08.8X ", p->iosb[0], p->iosb[1]); else if (iosb_text)6 sprintf(tmp, "%-7s ", ssdef((p->iosb[0]) & 0xffff));! else if (iosb_combination) { if (func == IO$_SENSEMODE) j = 0; else) j = ((p->iosb[0] >> 16) & 0xffff) +! ((p->iosb[1] & 0xffff) << 16);= sprintf(tmp, "%-7s %6d ", ssdef((p->iosb[0]) & 0xffff), j); } strcat(record, tmp); }, if (function > 0) { /* I/O function code */ if (function_hex)# sprintf(tmp, "%04.4X ", p->func); else if (function_text), sprintf(tmp, "%s ", decode_func(p->func)); strcat(record, tmp); } if (!(*record))+ break; /* Nothing to display, quit */ j = strlen(record);/ record[j - 1] = '\n'; /* Zap trailing space */ record[j] = '\0';2 if ((stat = fprintf(outfile, "%s", record)) <= 0)\ lib$signal(&ld_filwrterr, 1, outfilename, vaxc$errno, 0); /* Can't write output file */ p++; /* Next record */ }}/*! Convert status to readable code*/char *ssdef(code) int code;{ static char msgbuf[64]; struct dsc msgdsc = {sizeof(msgbuf), msgbuf}; int stat; short rlen;2 stat = sys$getmsg(code, &rlen, &msgdsc, 2, 0); signal_error(stat, 0);, msgbuf[rlen] = '\0'; /* Trailing zero */. return &msgbuf[1]; /* Remove leading % */}/* Process input file*/void process_input(){ char *buf, *savbuf;& struct ovrrun *ovrbuf, *savovrbuf; char *infilename; int infilei; FILE *infile; char namebuf[32]; 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 */N if (((infilei = open(infilename, O_RDONLY, 0, "dna=LD_TRACE.DAT")) < 0) ||+ ((infile = fdopen(infilei, "r")) == 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;- if ((version == 0) || /* Old version ? */= (version != DATA_FILE_VERSION)) /* Incompatible version ? */L lib$signal(&ld_cantreadoldfmt, 1, version); /* Can't read old input file */D if ((fread(namebuf, 32, 1, infile) != 1) || /* Get devicename */; (fread(nodebuf, 32, 1, infile) != 1) || /* Get nodename */; (fread(timestmp, 24, 1, infile) != 1)) /* Get timestamp *Ni$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1qQqf/V lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file */2 s_device = namebuf; /* Point to devicename */ recnum = 0; overruncnt = 0; buf = savbuf = 0; ovrbuf = savovrbuf = 0; while (1) {O if ((fread(&size, sizeof(int), 1, infile) != 1) || /* Get number of records */R (fread(&overrun, sizeof(int), 1, infile) != 1)) /* Get number of overruns */[ lib$signal(&ld_filreaderr, 1, infilename, vaxc$errno, 0); /* Can't read input file *//*G We will call realloc to increase the size of our databuffer. The dataB 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 */& 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 wantN to distinguish these two we try to read one more byte to find the difference*/ if ((i = fgetc(infile)) == EOF)' break; /* Ready for processing */ else< ungetc(i, infile); /* Return character and try again */ }I process_trace(buf, recnum, ovrbuf, overruncnt, nodebuf, timestmp, 0);$ j free(buf); /* Return buffer */ if (ovrbuf != 0)# free(ovrbuf); /* Return buffer */2 if (fclose(infile) < 0) /* Close input file */7 lib$signal(&ld_closerr, 1, infilename, vaxc$errno, 0);}/*) Decode the I/O function to readable text*/char *decode_func(what) int what;{ static char funcstr[80]; char str[80]; int modifier; int function;: modifier = what & ~IO$M_FCODE; /* Get modifier bits */= function = what & IO$M_FCODE; /* Get functioncode bits */. if (!modifier) /* No modifiers present */9 return cvttbl[function];/* Return pointer to function */ else { *str = '\0';; switch (function) { case IO$_PACKACK:8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);> chk_mod(&modifier, IO$M_MSCP_FORMAT, "MSCP_FORMAT", str); break; case IO$_AVAILABLE:8 chk_mod(&modifier, IO$M_INHERLOG, "INHERLOG", str);8 chk_mod(&modifier, IO$M_ALLHOSTS, "ALLHOSTS", str);8 chk_mod(&modifier, IO$M_DISSOLVE, "DISSOLVE", str);: chk_mod(&modifier, IO$M_NOCLEANUP, "NOCLEANUP", str); break; case IO$_REMSHAD:8 chk_mod(&modifier, IO$M_SPINDOWN, "SPINDOWN", str); break; case IO$_SENSECHAR:4 chk_mod(&modifier, IO$M_SHADOW, "SHADOW", str); break; case IO$_SETMODE:6 chk_mod(&modifier, IO$M_LINE_ON, "LINE_ON", str);8 chk_mod(&modifier, IO$M_LINE_OFF, "LINE_OFF", str);6 chk_mod(&modifier, IO$M_NEWLINE, "NEWLINE", str);2 chk_mod(&modifier, IO$M_ABORT, "ABORT", str); break; case IO$_UNLOAD:: chk_mod(&modifier, IO$M_CLSEREXCP, "CLSEREXCP", str); break; case IO$_DSE:: chk_mod(&modifier, IO$M_DATACHECK, "DATACHECK", str);2 chk_mod(&modifier, IO$M_ERASE, "ERASE", str); break; case IO$_WRITEVBLK: case IO$_WRITELBLK: case IO$_WRITEPBLK: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: case IO$_READPBLK: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; default: break; }L sprintf(funcstr, "%s%s", cvttbl[function], str); /* Form function string */7 if (modifier & ~IO$M_FCODE) /* Any modifiers left ? */T sprintf(funcstr, "%s (%08.8X)", funcstr, modifier); /* Show excess modifiers */1 return (funcstr); /* Return pointer to string */ }}/*< 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;char *modifstr; char *outstr;{7 if (*modifier & mask) { /* Check if mask bit set */+ strcat(outstr, "|"); /* Concatenate bar */: strcat(outstr, modifstr); /* Concatenate inpout string */6 *modifier &= ~mask; /* Flags this bit as processed */ }}/* Convert binary time to ascii,' return only the time and not the date*/char *cvttime(time, how, full) int time[2];int how; int full;{ short rlen; int stat; struct dsc timdsc; static char timstr[256]; timdsc.len = 256; timdsc.addr = timstr;U stat = sys$asctim(&rlen, &timdsc, time, full); /* Convert binary time to ascii */ signal_error(stat, 0);0 timstr[rlen] = '\0'; /* Add trailing zero */) if (how) /* Display only seconds */ return (&timstr[6]); return timstr;}/* Open specified file*/void open_file(name) char *name;{ int stat; int i; struct iosb iosb;6 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 */9 ld_fab.fab$l_xab = &ld_xabfhc; /* Point to FHC XAB */> ld_nam.nam$b_rss = NAM$C_MAXRSS; /* Max. resultant size */7 ld_nam.nam$l_rsa = resspec; /* Resultant address */= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */6 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 */ 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);/ stat = sys$open(&ld_fab); /* And open it */ if (!(stat & 1))F lib$signal(&ld_openerr, 1, name, ld_fab.fab$l_sts, ld_fab.fab$l_stv);R strncat(&realspec[i], ld_nam.nam$l_name, ld_nam.nam$b_name + /* Append rest */+ ld_nam.nam$b_type + ld_nam.nam$b_ver);9 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;B realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev;B maxblocks = ld_xabfhc.xab$l_ebk; /* Get current eof pointer */; if (ld_xabfhc.xab$w_ffb == 0) /* Adjust if necessary */ maxblocks--;#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];#elseO fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */= fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];= fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];#endifT stat = sys$assign(&realdev_dsc, &fchan, 0, 0, 0); /* Assign channel to device */3 signal_error(stat, 0); /* where file resides */% atr[0].atr$w_size = SBK$K_LENGTH;A 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_VAXC5 fib.fib$v_notrunc = 1; /* Truncate not allowed */; fib.fib$b_wsize = -1; /* Open wi'+$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1q`"wth cathedral windows */#else[ fib.fib$r_acctl_overlay.fib$r_acctl_bits0.fib$v_notrunc = 1; /* Truncate not allowed */c fib.fib$r_acctl_overlay.fib$r_acctl_fields2.fib$b_wsize = -1; /* Open with cathedral windows */#endif+ fib_dsc.addr = &fib; /* Point to FIB */ fib_dsc.len = FIB$K_LENGTH;D stat = sys$qiow(0, fchan, IO$_ACCESS | IO$M_ACCESS, &iosb, 0, 0,8 &fib_dsc, 0, 0, 0, &atr, 0); /* Access the file */$ signal_error(stat, iosb.status);}/*$ Close file opened with 'open_file'*/void close_file(filename)char *filename;{ int stat; struct iosb iosb;8 stat = sys$qiow(0, fchan, IO$_DEACCESS, &iosb, 0, 0,3 &fib_dsc, 0, 0, 0, 0, 0); /* Deaccess file */$ 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(){ int stat; int q_abort; int loop = 0; char matchname[256]; struct dsc matchdev =# {sizeof(matchname), matchname}; short rlen; int context[2] = {0, 0};? s_device = getqualstring(&sw_device); /* Get device name */A q_abort = getqual(&sw_abort) > 0; /* Get 'abort' qualifier */, if (getqual(&sw_all) > 0) { /* /ALL ? */ stat = 1;/ while (stat & 1) { /* Loop until we're done */F 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);3 matchname[rlen] = '\0'; /* Place terminator */2 stat = disconnect_one(matchname, q_abort, 0);! /* Disconnect this device */: if (stat == SS$_DEVINACT) { /* If not connected... */; lib$signal(&ld_notconnected, 1, matchname); /* Flag it */ stat = 1; /* and continue */ } } } else {- stat = disconnect_one(s_device, q_abort, 1);6 if (stat == SS$_DEVINACT) { /* If not connected... */J lib$signal(&ld_notconnected, 1, fulldevspec(s_device)); /* Flag it */= stat = &ld_notconnected; /* Convert to better message */7 stat |= STS$M_INHIB_MSG; /* Don't show it again */ } }}*int disconnect_one(dev, abortflag, single) char *dev;int abortflag; int single;{ char ldfile[256]; char lddev[64]; struct dsc sdev; char *devstr; int stat; int chan; 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)E lib$signal(&ld_nosupport, 1, devstr); /* Show it to the world */ else return (1); } sdev.addr = dev; sdev.len = strlen(dev);B stat = sys$assign(&sdev, &chan, 0, 0, 0); /* assign channel */@ if (stat == SS$_DEVALLOC) { /* Allocated on remote node ? */( lib$signal(&ld_remotealloc, 1, devstr); } else { signal_error(stat, 0);% if (!abortflag) { /* No /ABORT ? */: stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0,U lddev, sizeof(lddev), &fid, 0, 0, LDIO_GET_CONNECTION); /* Get current status */ signal_error(stat, 0);H if (!iosb_flags.connected) /* Check 'connected' flag from driver */ return (&ld_notconnected);4 lddev[iosb.size] = '\0'; /* Terminate string */. if (!(replacefl = iosb_flags.replaced)) {% create_filename(lddev,&fid,ldfile);$ open_file(ldfile); /* Open file */ }2 func = LDIO_DISCONNECT; /* Normal function */ } else {E func = LDIO_DISCONNECT | LDIO_M_ABORT; /* Some more for abort */ }M stat = sys$qiow(0, chan, IO$_LD_CONTROL, &iosb, 0, 0, /* Issue disconnect */ &sbk, 0, 0, 0, 0, func);! signal_error(stat, iosb.status);2 stat = sys$dassgn(chan); /* Get rid of channel */ signal_error(stat, 0);0 if (!abortflag && !replacefl) /* No /ABORT ? */6 close_file(ldfile); /* Only close if not abort */' if (getqual(&sw_log) > 0) /* /LOG ? */B lib$signal(&ld_nowdisconn, 1, devstr); /* Show what we did */ } return (1);}/* Create contiguous file for LD*/ void create(){ int stat; int q_size; 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 */2 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);t0 if (ld_nam.nam$v_wildcard) /* or wildcard *// lib$signal(&ld_createrr, 1, s_file, RMS$_WLD); 5 stat = sys$create(&ld_fab); /* Create the file */L if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);@ setfilechar(); /* Set NOMOVE and NOBACKUP characteristic */1 stat = sys$connect(&ld_rab);/* Hook up RAB */r if (!(stat & 1))I lib$signal(&ld_createrr, 1, s_file, ld_fab.fab$l_sts, ld_fab.fab$l_stv);E0 ld_rab.rab$l_rbf = dummy; /* Dummy buffer */A stat = sys$put(&ld_rab); /* Put dummy block at end of file */.+ /* to get EOF pointer at right place */d 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 */y 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); }B} /* Init FAB for createA*/void setup_fab(filname, size)ichar *filname; int *size;{v, ld_fab.fab$l_alq = *size; /* Filesize */6 ld_fab.fab$l_dna = ".DSK"; /* Default extension */* ld_fab.fab$b_dns = 4; /* and length */P ld_fab.fab$l_fop = FAB$M_NAM | FAB$M_CTG | FAB$M_OFP; /* NAM + contiguous */2 ld_fab.fab$b_org = FAB$C_SEQ; /* Sequential */- ld_fab.fab$b_rfm = FAB$C_FIX; /* Fixed */o2 ld_fab.fab$w_mrs = 512; /* 512 byte records *// ld_fab.fab$l_nam = &ld_nam; /* NAM block */R. 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) */a- ld_rab.rab$w_rsz = 512; /* Record size */R> ld_nam.nam$b_rss = NAM$C_MAXRSS; /* Max. resultant size */7 ld_nam.nam$l_rsa = resspec; /* Resultant address */= ld_nam.nam$b_ess = NAM$C_MAXRSS; /* Max. expanded size */U6 ld_nam.nam$l_esa = expspec; /* Expanded address */}avoid setfilechar(){a int stat;. struct iosb iosb;S int attrib;//*B Use QIO to modify the attributes since RMS won't support NOMOVE until VMS V6.0]*/#ifdef __DECC_MODE_VAXCK= 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];E+ fib.fib$w_fid[2] = ld_nam.nam$w_fid[2];#elseCO fib.fib$r_fid_overlay.fib$w_fid[0] = ld_nam.nam$w_fid[0]; /* Get file-id */ = fib.fib$r_fid_overlay.fib$w_fid[1] = ld_nam.nam$w_fid[1];i= fib.fib$r_fid_overlay.fib$w_fid[2] = ld_nam.nam$w_fid[2];r#endifB realdev_dsc.addr = ld_nam.nam$l_dev; /* Get real devicename */' realdev_dsc.len = ld_nam.nam$b_dev;xT stat = sys$assign(&realdev_dsc, &fchan, 0, 0, 0); /* Assign channel to device */3 signal_error(stat, 0); /* where file resides */;$ 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 */c atr[1].atr$w_type = 0;+ fib_dsc.addr = &fib; /* Point to FIB */ 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) <  Zd$LD051.B"_Iv]DBURG.LD.FWARE.SRC]LD.C;1qh"0)/ attrib |= FCH$M_NOBACKUP; /* Mark no backup */I6 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);}c/*& Get full filename from specified one*/!char *fullfilespec(name, defname))char *name, *defname;;{ % static char tmpnam[NAM$C_MAXRSS]; int stat;t int len; if (name)* len = strlen(name); else len = 0; : 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 */r/ ld_fab.fab$l_nam = &ld_nam; /* NAM block */: ld_fab.fab$l_dna = defname; /* Default name address */? ld_fab.fab$b_dns = strlen(defname); /* Default name size */ 1 ld_fab.fab$b_fns = len; /* Filename length */e3 ld_fab.fab$l_fna = name; /* Filename address */04 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 */e return (tmpnam);},/*) Get full device name from specified oner*/char *fulldevspec(name)g char *name;h{* char *tmpnam;t struct dsc resdsc;- struct dsc namdsc = {strlen(name), name};t short rlen; int stat;s8 tmpnam = malloc(64); /* Get memory for devicename */ if (tmpnam == 0)6 signal_error(SS$_INSFMEM, 0); /* Not enough memory */ resdsc.addr = tmpnam;* resdsc.len = 64;[ stat = lib$getdvi(&DVI$_FULLDEVNAM, 0, &namdsc, 0, &resdsc, &rlen); /* Get full name */n signal_error(stat, 0);) tmpnam[rlen] = '\0'; /* Terminator */t/ return (tmpnam + 1); /* Skip leading '_' */}o/* Get full filespec from file-id*/'void create_filename(device,fid,outbuf)t char *device;struct fiddef *fid;m char *outbuf;{  struct dsc outdsc; struct dsc devdsc; int stat;e short rlen;> char tmpbuf[256];* char *p;7 devdsc.len = strlen(device); /* Input devicename */s devdsc.addr = device;o; outdsc.len = sizeof(tmpbuf); /* Output buffer length */s outdsc.addr = tmpbuf;e? 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 */t p = strchr(tmpbuf,':') + 1; strcat(outbuf,p);r}q/* Get nodename*/char *nodename(){E static char tmpnam[256]; static struct dsc resdsc = {sizeof(tmpnam), tmpnam};  struct dsc tbl = {12, "LNM$FILE_DEV"};( struct dsc lognam =i {8, "SYS$NODE"}; short rlen;T struct itmlst item[2]; int stat;i tmpnam[0] = '\0'; R stat = lib$getsyi(&SYI$_NODENAME, 0, &resdsc, &rlen, 0, 0); /* Get nodename */) if (stat & 1) { /* Nodename found */s& 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;d item[0].rlength = &rlen;o( item[1].item = 0; /* End of itemlist */ item[1].buflen = 0;/ stat = sys$trnlnm(0, &tbl, &lognam, 0, &item);e signal_error(stat, 0);n& tmpnam[rlen] = '\0'; /* Terminator */ }i return (tmpnam);}/* Build lock for trace*/char *build_lock(dev)l char *dev;{h static char locknam[256];e> strcpy(locknam, "$LOGDISK_"); /* First part of lockname */5 strcat(locknam, nodename());/* Attach nodename */0@ locknam[strlen(locknam) - 2] = '\0'; /* Zap trailing "::" */> strcat(locknam, fulldevspec(dev)); /* Attach devicename */ return locknam;e}evoid set_outband(){i int stat;e int type;f int mask[2]; struct iosb iosb; ( $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 */d 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);}ivoid purge_file(limit, name) int limit; char *name;{ int stat;a char *pos; char tmpnam[256];. struct dsc fildsc; $DESCRIPTOR(deflt, ".DAT");i3 pos = strrchr(name, ';'); /* Find trailing ; */l if (pos != 0) {e# if (name[strlen(name) - 1] != ';')h, return; /* Exact filespec speficied */ elsen& *pos = '\0'; /* Zap trailing ; */ }n if (limit == 0) D sprintf(tmpnam, "%s;%d", name, limit); /* Delete lastest version */ elseA sprintf(tmpnam, "%s;-%d", name, limit); /* Delete old version */r fildsc.addr = tmpnam; fildsc.len = strlen(tmpnam); stat = 1;m/ while (stat == 1) /* until out of files */l> 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);L}evoid ld_exit(param) int param;{o int stat; H if ((outfile != 0) && (outfile != stdout)) { /* Close output file */ if (fclose(outfile) < 0)5< 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); }n; if (param == 0) { /* Called by blocking ast routine */e lib$signal(&ld_tracestopped); exit(&ld_tracestopped); } else exit(1);e} void signal_error(st1, st2)c int st1, st2;y{e int stat1, stat2;b int modified1, modified2;I modified1 = convert_error(st1, &stat1); /* Return reasonable error */ + modified2 = convert_error(st2, &stat2);c4 if ((!(stat1 & 1)) || (st2 && (!(stat2 & 1)))) { if (modified1 || modified2) { if (stat1 == 1) lib$signal(stat2, 0, 0); elsev$ lib$signal(stat1, 0, stat2, 0, 0); } else {. if (stat1 == 1). lib$signal(&ld_detectederr, 0, stat2, 0, 0); else 8 lib$signal(&ld_detectederr, 0, stat1, 0, stat2, 0, 0); } }6}/*; Return a reasonable error for some returned by the drivers*/'int convert_error(oldstatus, newstatus)cint oldstatus;int *newstatus;u{  int code = 0;,# if (oldstatus == SS$_DEVASSIGN)t; code = &ld_devassign; /* 'Device has channels assigned' */e( else if (oldstatus == SS$_DEVACTIVE)0 code = &ld_alrdyconn; /* 'Already connected' */' else if (oldstatus == SS$_DEVINACT)s* code = &ld_notconn; /* 'Not connected' */% else if (oldstatus == SS$_NODATA)D7 code = &ld_trcdisabl; /* 'Tracing already disabled' */ * else if (oldstatus == SS$_TOOMUCHDATA)5 code = &ld_trcenabl; /* 'Tracing already enabled' */L' else if (oldstatus == SS$_DATALOST) = code = &ld_nowatchdata; /* 'No watchpoint data available' */A( else if (oldstatus == SS$_DATACHECK)5 code = &ld_wptnotfound; /* 'Watchpoint not found' */i( else if (oldstatus == SS$_INCSHAMEM)0 code = &ld_nesting; /* 'Nesting not allowed' */( else if (oldstatus == SS$_FILALRACC)2 code = &ld_fileinuse; /* 'File already in use' */' else if (oldstatus == SS$_DEVALLOC)06 code = &ld_deviceinuse; /* 'Device already in use' */( else if (oldstatus == SS$_DEVREQERR)G code = &ld_fileonother; /* 'File not allowed to be on other device' */s( 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' */e) else if (oldstatus == SS$_ACCONFLICT)tD code = &ld_notvisible; /* 'Device is not visible on other nodes' */' else if (oldstatus == SS$_BADPARAM)g; code = &ld_invgeometry; /* 'Invalid geometry specified' */ % else if (oldstatus == SS$_UNSAFE)cS code = &ld_devconnected;/* 'Cannot set allocation class with active LD devices' */l else {5 *newstatus = oldstatus; /* Return original status */c return 0; /* Not modified */ }/ *newstatus = code; /* Return new status */n return 1; /* Modified */}t $LD051.BK_|v]DBURG.LD.FWARE.SRC]LD.H;1EC*[VDBURG.LD.FWARE.SRC]LD.H;1+,K_|./!H 4E-v]0123KPWO56}$Aum7=Xm89G!HHJ/* Facility:$ VAX/VMS Logical Disk Utility Abstract:' Include file for LD management utility Author: Jur van der Burg! vdburg@utrtsc.uto.dec.com*/#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "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#define DATA_FILE_VERSION 0x04/*( 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; } 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,"*LDA*");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_continuous,"CONTINUOUS");$DESCRIPTOR(sw_reset,"RESET");"$DESCRIPTOR(sw_replace,"REPLACE"); $DESCRIPTOR(sw_status,"STATUS"); $DESCRIPTOR(sw_header,"HEADER");$$DESCRIPTOR(sw_warnings,"WARNINGS"); $DESCRIPTOR(sw_binary,"BINARY"); $DESCRIPTOR(sw_symbol,"SYMBOL");"$DESCRIPTOR(sw_entries,"ENTRIES");$DESCRIPTOR(sw_pid,"PID");$DESCRIPTOR(sw_lbn,"LBN");&$DESCRIPTOR(sw_bytecount,"BYTECOUNT");$DESCRIPTOR(sw_iosb,"IOSB");$$DESCRIPTOR(sw_function,"FUNCTION");&$DESCRIPTOR(sw_timestamp,"TIMESTAMP"); $DESCRIPTOR(sw_number,"NUMBER");$DESCRIPTOR(sw_input,"INPUT"); $DESCRIPTOR(sw_output,"OUTPUT");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_action,"ACTION");0$DESCRIPTOR(sw_action_suspend,"ACTION.SUSPEND");,$DESCRIPTOR(sw_action_crash,"ACTION.CRASH");,$DESCRIPTOR(sw_action_error,"ACTION.ERROR");,$DESCRIPTOR(sw_action_opcom,"ACTION.OPCOM");/* Routine declarations*/int getqual();int getqualvalue();int getmulqualvalue();char *getqualstring();int *getlist(); void show();int show_one();void connect_unit();void disconnect_unit();int disconnect_one();void open_file();void close_file();void create(); void trace();void stop_trace();void stop_one();void notrace(); void watch();void show_watch();void display_watch();void nowatch();void protect();void noprotect();void 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_nesting, 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_pastdata, ld_badentparam, ld_confqual, ld_cantreadoldfmt, ld_unit, ld_dupunit, ld_nosupport, 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;/*E Conversiontable to convert I/O functioncodes to human readable text*/char *cvttbl[] = { "NOP", "UNLOAD", "SEEK", "RECAL", "DRVCLR", "RELEASE", "OFFSET", "RETCENTER", "PACKACK",  ) $LD051.BK_|v]DBURG.LD.FWARE.SRC]LD.H;1E9"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.FWARE.SRC]LDDRIVER.MAR;1+,_P.Y/!H 4UYW-v]0123KPWOZ56ͮ5m7]Y`m89G!HHJ. .TITLE LDDRIVER, VAX/VMS Logical Disk driver .IDENT 'V5.1'M;****************************************************************************;* *4;* COPYRIGHT (c) 1991, 1992, 1993, 1994 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.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.;;---;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 $ARBDEF ; Define ARB offsets$ $CANDEF ; Cancel I/O definitions# $CCBDEF ; Channel control block# $CRBDEF ; Channel request block% $CDDBDEF ; Class Driver Data 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 $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( $VADEF ; Virtual address definitions" $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  ;=$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYm"Tracebuffer;; Input: R5 = UCB;/; Output: R0 contains status for system context;5; All registers will be preserved for process context;. .MACRO LOCK_TRACE ACCESS=READ,CONTEXT=PROCESS .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKR MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKREXEC .ENDC .ENDC .IF IDN , .IF IDN , PUSHL R0# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKW MOVL (SP)+,R0 .IFF# MOVAB UCB$L_LD_TRCMUTEX(R5),R0 JSB G^SCH$LOCKWEXEC .ENDC .ENDC .ENDM;; Macro to unlock Tracebuffer;; Input: R5 = UCB;!; All registers will be preserved;C .MACRO UNLOCK_TRACE CONTEXT=PROCESS,NEWDATA=NO,?L10,?L20,?L30,?L40 PUSHR #^M MOVAB UCB$L_LD_TRCMUTEX(R5),R0 .IF IDN , JSB G^SCH$UNLOCK .IFF JSB G^SCH$UNLOCKEXEC .ENDC FORKLOCK LOCK=UCB$B_FLCK(R5),- SAVIPL=-(SP),- PRESERVE=NO$ REMQUE @UCB$L_LD_TRCMUTEXQFL(R5),R3 ; Get eventual waiting IRP BVS L10 ; Nothing there PUSHL R5 ; Save UCB* MOVL R3,R5 ; Save pointer to Fork block( JSB G^EXE$QUEUE_FORK ; Queue forkthread MOVL (SP)+,R5 ; Restore UCBL10: .IF IDN ,)L20: REMQUE @UCB$L_LD_TRCWAITQFL(R5),R3 ; Get IRP waiting for data BVS L40 ; Nothing there PUSHL R5 ; Save UCB BSBW LD_GETLDIOB ; 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_IOSB .BLKL 1 ; IOSB address $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 ; Function* $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'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_Q_IOSB .BLKQ 1 ; IOSB* $DEF LDTRC_Q_ST_TIME .BLKQ 1 ; Start time( $DEF LDTRC_Q_EN_TIME .BLKQ 1 ; End 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 >;; 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 access > $DEFEND LDWATCHPT;; Suspended process list ;@ $DEFINI LDSUSPLST,GLOBAL ; LOGICAL DISK suspended process list+ $DEF LDSUSPLST_L_PID .BLKL 1 ; Process id5 $DEF LDSUSPLST_L_LBN .BLKL 1 ; Logical block number' $DEF LDSUSPLST_W_FLAGS .BLKW 1 ; Flags) $DEF LDSUSPLST_W_ACTION .BLKW 1 ; Action) $DEF LDSUSPLST_W_FUNC .BLKW 1 ; Function/ $DEF LDSUSPLST_W_RETCODE .BLKW 1 ; Return code.LDSUSPLST_K_LENGTH = . ; Length of LDSUSPLST $DEFEND LDSUSPLST;; $SNDOPR parameter list;5 $DEFINI LDSNDOPRLST,GLOBAL ; $SNDOPR parameter list4 $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 address2 $DEF LDSNDOPRLST_L_ASTPRM .BLKL 1 ; AST parameter/ $DEF LDSNDOPRLST_L_KAST .BLKL 1 ; KAST address6 $DEF LDSNDOPRLST_L_LBN .BLKL 1 ; Logical block number) $DEF LDSNDOPRLST_W_FLAGS .BLKW 1 ; Flags+ $DEF LDSNDOPRLST_W_ACTION .BLKW 1 ; Action+ $DEF LDSNDOPRLST_W_FUNC .BLKW 1 ; Function1 $DEF LDSNDOPRLST_W_RETCODE .BLKW 1 ; Return code1 $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 ; Rvn1 $DEF LDSNDOPRLST_T_DEVNAM .BLKB 64 ; Device name1LDSNDOPRLST_K_DEVNAM = 64 ; Device name length2LDSNDOPRLST_K_LENGTH = . ; Length of LDSNDOPRLST $DEFEND LDSNDOPRLST;; LD lockvalueblock definitions;- $DEFINI LDLVB,GLOBAL ; LD lockvalue block+ $DEF LDLVB_W_CYLINDERS .BLKW 1 ; Cylinders& $DEF LDLVB_B_TRACKS .BLKB 1 ; Tracks( $DEF LDLVB_B_SECTORS .BLKB 1 ; Sectors4 $DEF LDLVB_L_MAXBLOCK .BLKL 1 ; Maximum blocknumber1 $DEF LDLVB_L_ALLOCLS .BLKL 1 ; Allocation class) $DEF LDLVB_W_UNIT .BLKW 1 ; Unit number$ $DEF LDLVB_B_FLAGS .BLKB 1 ; Flags*ASSUME . LE 16 ; Length must be <= 16;; Subfields in FLAGS field; _VIELD LDLVB,0,< -$ ,- ; Shared accessable > $DEFEND LDLVB;#; Definitions for our functioncodes; YS-$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY%"- IO$_LD_CONTROL = 20 ; Main control function ; (Physical I/O function)' 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 watch' LDIO_DISABLE_WATCH = 9 ; Disable watch' LDIO_GET_WATCH = 10 ; Get watchpoints, LDIO_RESUME_WATCH = 11 ; Resume watchpoints= LDIO_GET_SUSPEND_LIST = 12 ; Get list of suspended processes0 LDIO_ENABLE_PROTECT = 13 ; Enable write-protect2 LDIO_DISABLE_PROTECT = 14 ; Disable write-protect/ LDIO_SET_ALLOCLASS = 15 ; Set allocation class LDIO_V_FUNC = 0 LDIO_S_FUNC = 8;; Function modifiers; $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 LDIO;; Watchpoint actions;* WATCH_ACTION_SUSPEND = 0 ; Suspend thread& WATCH_ACTION_CRASH = 1 ; Crash system& WATCH_ACTION_ERROR = 2 ; Return error/ WATCH_ACTION_OPCOM = 3 ; Send message to OPCOM& WATCH_ACTION_MAX = 3 ; Maximum action; ;+++1; Definitions that follow the standard UCB fields;---1 $DEFINI UCB,GLOBAL ; Start of UCB definitions3 .=UCB$K_MSCP_DISK_LENGTH ; Position at end of UCB0 $DEF UCB$L_LD_PDUCB .BLKL 1 ; UCB of Phys. disk/ $DEF UCB$L_LD_STLBN ; Starting Log. block nr $DEF UCB$W_LD_STLBNL .BLKW 1 $DEF UCB$W_LD_STLBNH .BLKW 1; $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+41 $DEF UCB$L_LD_FCB .BLKL 1 ; Save for FCB pointer) $DEF UCB$L_LD_ORBSAV .BLKL 1 ; Saved ORB0 $DEF UCB$L_LD_SAVEPC .BLKL 1 ; Caller's address. $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+4 $DEF UCB$Q_LD_PD_LKSB .BLKB 24$ ; Phys. dev. lock status block ; + lock value block! $DEF UCB$T_LD_PD_RESNAM .BLKB 28% ; Phys. dev. lock resource name $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 40+ ; Resource name 32 bytes + descriptor! $DEF UCB$L_LD_TRCWAITQFL .BLKL 1* ; Trace data wait queue forward link! $DEF UCB$L_LD_TRCWAITQBL .BLKL 1+ ; Trace data wait queue backward link" $DEF UCB$L_LD_TRCMUTEXQFL .BLKL 1+ ; Trace mutex wait queue forward link" $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 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 1" ; 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 1 ; 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 link, $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$B_LD_FLAGS .BLKB 1 ; Flags byte $VIELD UCB,0,< -0 , - ; Conn./Disconn. status bit. ,- ; Replace mode status bit. ,- ; Connected to DECRAM disk$ ,- ; Write protect' ,- ; Shared accessable4 ,- ; Forkblock busy (internal only) >; ; Filename string buffer+ $DEF UCB$K_LD_UCBLEN ; Length of new UCB( $DEFEND UCB ; End of UCB definitions  .SBTTL Standard tables;+++; Driver prologue table;--- .IF DF V64 DPT_FLAGS= ; SMP safe! ; Driver supports snapshots ; if not in a cluster .IFF% DPT_FLAGS= ; SMP safe .ENDC DPTAB - ; DPT-creation macro& END=LD_END,- ; End of driver label! ADAPTER=NULL,- ; Adapter type- UCBSIZE=,- ; Length of UCB, MAXUNITS=LD_MAX_UNITS,- ; Max nr of units* UNLOAD=LD_DRV_UNLOAD,- ; Unload routine! NAME=LDDRIVER,- ; Driver name# FLAGS=DPT_FLAGS ; Default flags" DPT_STORE INIT ; Start of load ; initialization table8 DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\> ; Default ACP name4 DPT_STORE DDB,DDB$L_ACPD+3,B,DDB$K_PACK ; ACP class3 DPT_STORE UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8; FORK IPL+ DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPL: DPT_STORE UCB,UCB$L_DEVCHAR,L,<- ; Device characteristics DEV$M_IDV!- ; input device! DEV$M_ODV!- ; output device" DEV$M_FOD!- ; files oriented( DEV$M_DIR!- ; directory structured DEV$M_AVL!- ; available DEV$M_SHR!- ; sharable DEV$M_RND> ; random access" DPT_STORE UCB,UCB$L_DEVCHAR2,L,<-( DEV$M_NNM!- ; Prefix name with NODE$; DEV$M_NLT!- ; Not-last-track device (no bad block data)) DEV$M_CDP!- ; To prevent MSCP serving/ 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 tracebuffer7 DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_DISK ; Device classA 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$W_DEVSTS,W,- ; Inhibit logical to' ; physical xlation.% DPT_STORE REINIT ; Start of reload ; initialization table2 DPT_STORE DDB,DDB$L_DDT,D,LD$DDT ; Address of DDT+ DPT_STORE CRB,- ; Address of controller5 CRB$L_INTD+VEC$L_INITIAL,- ; initialization routine D,LD_CONTROL_INIT' DPT_STORE CRB,- ; Address of device3 CRB$L_INTD+VEC$L_UNITINIT,- ; unit initialization 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. routine/ CLONEDUCB=LD_CLONED_UCB ; Cloned UCB routine ;+++; Function decision table;---"LD_FUNCTABLE: ; FDT for driver$ FUNCTAB ,- ; Valid I/O functions ' FUNCTAB ,- ; Buffered I/O functions ; FUNCTAB LD_SHAD_WCHECK,<- ; Check write to shadow set mbr% WRITELBLK,- ; Write LOGICAL Block& WRITEPBLK,- ; Write Physical Block$ WRITEVBLK> ; Write VIRTUAL Block* FUNCTAB +ACP$READBLK,- ; Read functions , FUNCTAB +ACP$WRITEBLK,- ; Write functions! # FUNCTAB LD_DSE,<- ; DSE function" DSE,- ; Data Secutiry Erase >+ FUNCTAB +ACP$ACCESS,- ; Access functions! / FUNCTAB +ACP$DEACCESS,- ; Deaccess functions + FUNCTAB +ACP$MODIFY,- ; Modify functions! ) FUNCTAB +ACP$MOUNT,- ; Mount functions 9 FUNCTAB +EXE$LCLDSKVALID,- ; Local disk valid functions 5 FUNCTAB +EXE$ZEROPARM,- ; Zero parameter functions 3 FUNCTAB +EXE$ONEPARM,- ; One parameter functions , FUNCTAB +EXE$SENSEMODE,- ; Sense functions* ) FUNCTAB +EXE$SETCHAR,- ; Set functions& 7 FUNCTAB LD_CRESHAD,- ; Create shadowset virtual unit 1 FUNCTAB LD_REMSHAD,- ; Remove shadowset member 2 FUNCTAB LD_CONTROL,- ; General LDdriver control ; ; Local data;/LD_REFCNT: .LONG 0 ; Number of active devices < .SBTTL LD_CONTROL, general control and dispatch FDT routine;+++6; LD_CONTROL, general control and dispatch FDT routine;; Functional description:;9; This routine is invoked via an IO$_LD_CONTROL function.=; We will dispatch to the various other routines according to; the P6 parameter.; ; 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)*; 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, and ; R9-R11.;;---LD_CONTROL: ; General Control FDT routine# MOVZWL P6(AP),R0 ; Get parameter1 MOVL R0,IRP$L_EXTEND(R3) ; Save function in IRP ASSUME LDIO_S_FUNC EQ 89 DISPATCH R0,TYPE=B,<- ; Dispatch according to function ,-$ ,-( ,-* ,-" ,-& ,-, ,-! ,-( ,-* ,-" ,-( ,-) ,-, ,-. ,-) >: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code$ JMP G^EXE$ABORTIO ; Abort the I/O E .SBTTL LD_GET_CONNECTION, Get connection characteristics FDT routine;+++?; LD_GET_CONNECTION, Get connection characteristics FDT routine;; Functional description:;F; This routine returns the full filename string to the callers buffer,-; specified via the descriptor address in P1.; The IOSB returned, contains;&; - Longword 0, The return status code2; and in word 1,The nr of characters transferred0; - Longword 1, The Connected status flag, where; bit 0 = 1 : Connected, and; 0 : Disconnected; bit 1 = 0 : Normal and; 1 : Replaced; bit 2 = 0 : Normal disk; 1 : DECRAM disk; bit 3 = 0 : Normal access; 1 : Write protected; ; 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)*; 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 string:; P2(AP) - Size of buffer to receive the devicename stringA; P3(AP) - Address of buffer to receive file-id of connected file; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--2LD_GET_CONNECTION: ; Get Connection FDT routine% MOVL P1(AP),R0 ; Address of buffer% MOVL P2(AP),R1 ; Get buffer length4 JSB G^EXE$READCHK ; Check buffer for write access) MOVZWL #SS$_ACCVIO,R0 ; Assume trouble9 IFNOWRT #6,@P3(AP),20$ ; Check if fid buffer writeable3 MOVZWL #SS$_DEVINACT,R0 ; Assume already inactive" BBC #UCB$V_LD_CONSTS,- ; Active? UCB$B_LD_FLAGS(R5),20$6 MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical device ucb+ JSB G^SCH$IOLOCKR ; Lock the IO database PUSHR #^M! MOVL R2,R5 ; Copy ucb address% MOVL P1(AP),R1 ; Address of buffer! MOVZWL P2(AP),R0 ; Buffer size! MOVZBL #1,R4 ; DVI$_ALLDEVNAM1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename POPR #^M+ PUSHR #^M ; Save across unlock. 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 buffer MOVW UCB$W_LD_FID_RVN(R5),(R2) MOVZWL #SS$_NORMAL,R0- INSV R1,#16,#16,R0 ; Place high word in R0@10$: MOVZBL UCB$B_LD_FLAGS(R5),R1 ; Copy connected status flags JMP G^EXE$FINISHIO ; Done(20$: JMP G^EXE$ABORTIO ; Abort the I/O 0 .SBTTL LD_GET_TRACE, Get trace data FDT routine;+++*; LD_GET_TRACE, Get trace data FDT routine;; Functional description:;:; 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:;; R0-R2 - scratch registers.; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block).; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers:; AP - address of the 1st function dependent QIO parameter6; P1(AP) - address of buffer to receive the trace data3; P2(AP) - size of buffer to receive the trace data; ; Outputs:;; R0 - I/O status; R1 - Number of returned bytes;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;-- 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 active(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),R$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY"A0 ; Get address of buffer5 JSB G^EXE$READCHK ; Check userbuffer accessability0 MOVL R0,IRP$L_SVAPTE(R3) ; Save buffer address) BSBW LD_MOVE_TRACE ; Move data to user' BLBC R0,60$ ; Stop on overrun error$ ; The count will always be the' ; number of packets in the bufferG50$: DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to number of entries 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 . .SBTTL LD_MOVE_TRACE, Move trace data to user;+++(; LD_MOVE_TRACE, move trace data to user;; Functional description:A; 8; 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); ; Outputs:;D; R0 - Status, either SS$_NORMAL or SS$_DATAOVERUN (buffer overflow)-; R1 - 0 or on failure number of lost packets; R2 - number of bytes moved;7; The routine must preserve all registers except R0-R2.;;--LD_MOVE_TRACE: 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 pointer5 MOVL UCB$L_LD_TRCWRAP(R5),R9 ; Did the buffer wrap? BEQL 60$ ; No SUBL3 R2,R9,R1 ; Bytecount MOVL R1,R10 ; Save count/ BSBW MOVE_TRACE ; Move the data (first part)<60$: MOVL UCB$L_LD_TRCBUF(R5),R2 ; Point to start of buffer# SUBL3 R2,UCB$L_LD_TRCBUFPTR(R5),R1# ADDL2 R1,R10 ; Accumulate count0 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;A; Calculate number of lost packets. This will be done as follows:;7; Count = (((TRCBUFPTR - TRCBUF) / LDTRCENT_K_LENGTH) +F; ((TRCLOST - 1) * ((TRCWRAP - TRCBUF) / LDTRCENT_K_LENGTH))); DECL R1 ; TRCLOST - 10 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCWRAP - TRCBUF UCB$L_LD_TRCWRAP(R5),R03 DIVL2 #LDTRCENT_K_LENGTH,R0 ; / LDTRCENT_K_LENGTH MULL2 R1,R02 SUBL3 UCB$L_LD_TRCBUF(R5),- ; TRCBUFPTR - TRCBUF UCB$L_LD_TRCBUFPTR(R5),R13 DIVL2 #LDTRCENT_K_LENGTH,R1 ; / LDTRCENT_K_LENGTH ADDL2 R0,R1 ; Result8 MOVZWL #SS$_DATAOVERUN,R0 ; We were overrun by a truck-70$: BBC #LDIO_V_RESET,- ; Reset requested? 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 mutex% MOVL R10,R2 ; Return amount moved POPR #^M RSB; MOVE_TRACE: PUSHR #^M MOVL R1,R6 ; Length 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 moved SOBGTR R7,10$ ; Until done.20$: MOVC3 R6,(R1),(R3) ; Move the remainder# MOVL R3,R0 ; Return last byte+1 POPR #^M RSB - .SBTTL LD_DISCONNECT, Disconnect FDT routine;+++'; LD_DISCONNECT, Disconnect FDT routine;; Functional description:;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 disconnect; the LD device as it is.;C; A prereqisite to call these routines, is that the file is opened,'; except when we replace a whole drive.; ; 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)*; 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:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--+LD_DISCONNECT: ; Disconnect FDT routine4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),10$+ BBS #UCB$V_LD_REPLACE,- ; Replaced drive? UCB$B_LD_FLAGS(R5),10$' MOVL P1(AP),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% BGEQ 30$ ; Must be system address6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 30$ ; No, return error/10$: MOVZWL #SS$_DEVINACT,R0 ; Assume inactive7 BBC #UCB$V_LD_CONSTS,- ; Check if in DISCONNECT state UCB$B_LD_FLAGS(R5),40$; MOVZWL #SS$_DEVFOREIGN,R0 ; Assume foreign mounted status> BBS #DEV$V_FOR,UCB$L_DEVCHAR(R5),40$; Device foreign mounted?1 MOVZWL #SS$_DEVMOUNT,R0 ; Assume mounted status6 BBS #DEV$V_MNT,UCB$L_DEVCHAR(R5),40$; Device mounted?4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),20$4 BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$B_LD_FLAGS(R5),20$2 MOVZWL #SS$_BADFILEHDR,R0 ; Assume wrong file id( ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ5 CMPL FCB$W_FID_NUM(R1),- ; Is it the same file id ? UCB$W_LD_FID_NUM(R5) BNEQ 40$ ; No5 CMPW FCB$W_FID_RVN(R1),- ; Is it the same file id ? UCB$W_LD_FID_RVN(R5) BNEQ 40$ ; Not really...920$: MOVZWL #SS$_DEVASSIGN,R0 ; Assume channels assigned/ CMPW UCB$W_REFC(R5),#1 ; Are we the only one? BNEQ 40$ ; No, get out2 BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffers, JMP G^EXE$QIODRVPKT ; Finish in start I/O*30$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid,40$: JMP G^EXE$ABORTIO ; And abort the I/O ' .SBTTL LD_CONNECT, Connect FDT routine;+++!; LD_CONNECT, Connect FDT routine;; Functional description:;@; 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.; ; 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)*; 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 SBK will be used; P3(AP) - Number of Tracks; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--%LD_CONNECT: ; Connect FDT routine0 BBC #UCB$V_LD_CONSTS,- ; Check if disconnected UCB$B_LD_FLAGS(R5),10$. MOVZWL #SS$_DEVACTIVE,R0 ; Set Device Active BRW 140$510$: BBC #LDIO_V_SHARE,- ; Shared access requested? IRP$L_EXTEND(R3),20$, TSTL @#CLU$GL_CLUB ; Cluster code loaded? BNEQ 20$ ; Yes, MOVZWL #SS$_UNSUPPORTED,R0 ; Not supported BRW 140$.20$: BBS #LDIO_V_REPLACE,- ; Replace a drive? IRP$L_EXTEND(R3),40$' MOVL P1(AP),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% BGEQ 30$ ; Must be system address6 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 later4 JSB G^SCH$IOLOCKR ; Lock the IO database for read* MOVL FCB$L_WLFL(R1),R1 ; Get WCB address7 MOVL WCB$L_ORGUCB(R1),R1 ; Get UCB of physical d$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY^"Revice& MOVL R1,UCB$L_LD_PDUCB(R5) ; Save it BRB 50$*30$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid BRW 140$ ; And abort the I/O940$: MOVL P1(AP),R1 ; Get devicename descriptor address0 MOVL 4(R1),R0 ; Get devicename string address1 MOVZWL (R1),R1 ; Get devicename string length3 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.- MOVL #IOC$M_ANY,R2 ; Set the SEARCHALL bit* CLRL R3 ; No SB specified (local only), JSB G^IOC$SEARCH ; Search the IO database5 POPR #^M ; Restore the registers! BLBC R0,90$ ; Return on error= MOVL R1,UCB$L_LD_PDUCB(R5) ; Save the UCB of the phys. disk550$: MOVZWL #SS$_IVDEVNAM,R0 ; Assume invalid device7 CMPB UCB$B_DEVCLASS(R1),#DC$_DISK ; Connect to a disk? BNEQ 90$ ; No, not allowed;C; Check for nesting, i.e. placing a logical disk on another logicalF; disk.... That may lead us into deeeeep trouble (= crash) and various; other subtile problems.;-; We have still locked the I/O database mutex;' CLRL R0 ; Assume no shadowed device;:; Check if this is an MSCP device. If not we can't rely on!; the UCB extensions to be valid.;# BBC #DEV$V_MSCP,- ; MSCP device? UCB$L_DEVCHAR2(R1),70$;2; See if this device is a shadow set virtual unit.;# ASSUME MSCP$V_SHADOW EQ 154 TSTW UCB$W_MSCPUNIT(R1) ; Shadow set virtual unit? BGEQ 70$ ; No/ MOVL UCB$L_SHAD(R1),R2 ; Phase I or Phase II? BEQL 70$ ; Phase I8 MOVZBL SHAD$B_MEMBERS(R2),R0 ; Check number of members BEQL 80$ ; Invalid shadowset@ MOVAL SHAD$L_MEMBER_UCB(R2),R2 ; Point to array of member UCB's(60$: MOVL (R2)+,R1 ; Get UCB of member<70$: CMPL UCB$L_DDB(R1),UCB$L_DDB(R5) ; Check if same device BEQL 80$ ; Same, bad% SOBGTR R0,60$ ; Check all members) PUSHR #^M ; Save across unlock. JSB G^SCH$IOUNLOCK ; Unlock the IO database! POPR #^M ; Restore IRP! BRB 100$ ; Passed the test...,80$: MOVZWL #SS$_INCSHAMEM,R0 ; Not allowed/90$: PUSHR #^M ; Save across unlock. JSB G^SCH$IOUNLOCK ; Unlock the IO database1 POPR #^M ; Restore the return status BRW 140$-100$: BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),110$/ MOVL UCB$L_LD_PDUCB(R5),R2 ; Get physical UCB; 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 status0 BBS #DEV$V_MNT,- ; Check if device is mounted UCB$L_DEVCHAR(R2),140$, MOVZWL #SS$_DEVALLOC,R0 ; Assume allocated. TSTL UCB$L_LOCKID(R2) ; Any lock for device? BNEQ 140$ ; Yes, error exit BRW 130$ ; Continue,110$: MOVL P1(AP),R9 ; Get the SBK address1 MOVW SBK$W_STLBNL(R9),- ; Save the starting LBN UCB$W_LD_STLBNL(R5)* MOVW SBK$W_STLBNH(R9),UCB$W_LD_STLBNH(R5)(ASSUME UCB$W_LD_STLBNL EQ UCB$L_LD_STLBN*ASSUME UCB$W_LD_STLBNH EQ UCB$L_LD_STLBN+23 MOVZWL #SS$_FILNOTCNTG,R0 ; Assume not contiguous1 TSTL UCB$L_LD_STLBN(R5) ; Test the starting LBN- BEQL 140$ ; If 0 then file not contiguous* IFRD #4,P2(AP),120$ ; Check if readable+ MOVZWL #SS$_ACCVIO,R0 ; Access violation BRB 140$ ; Abort;120$: BSBW LD_SET_GEOMETRY ; Fill in geometry information BLBC R0,140$ ; Quit on error' MOVL P1(AP),R9 ; Get the SBK address- MOVL SBK$L_FCB(R9),R9 ; Get the FCB address:130$: BSBW LD_MAKE_RESNAM ; Create resourcename for lock5 JMP G^EXE$QIODRVPKT ; Do the rest in the start I/O-140$: JMP G^EXE$ABORTIO ; And abort the I/O 5 .SBTTL LD_SET_GEOMETRY, Setup pseudo device geometry;+++/; LD_SET_GEOMETRY, Setup pseudo device geometry;; Functional description:;;; This routine will set the geometry for the pseudo device.; ; 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); 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 SBK will be used; P3(AP) - Number of Tracks; P4(AP) - Number of Sectors; P5(AP) - Number of Cylinders; ; Outputs:; ; R0 - status;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_SET_GEOMETRY:# MOVL P1(AP),R2 ; Get SBK address: MOVW SBK$W_FILESIZH(R2),R0 ; Save the allocated filesize$ ASHL #16,R0,R0 ; in the high word0 MOVW SBK$W_FILESIZL(R2),R0 ; Save the filesize 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? BLEQU 20$ ; Valid, use it( 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 zeroes 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.; TSTL R9 ; Tracks zero? BNEQ 30$ ; No INCL R9 ; Make 1%30$: CMPL R9,#256 ; Within bounds? BGEQU 70$ ; No TSTL R10 ; Sectors zero? BNEQ 40$ ; No INCL R10 ; Make 1%40$: CMPL R10,#256 ; Within bounds? BGEQU 70$ ; No TSTL R11 ; Cylinders zero? BNEQ 50$ ; No INCL R11 ; Make one'50$: CMPL R11,#65536 ; Within bounds? BGEQU 70$ ; No0 MULL3 R9,R10,R0 ; Calculate total size needed MULL2 R11,R0+ CMPL R0,R2 ; Not past allocated blocks? BGTRU 70$ ; Not valid) MOVB R9,UCB$B_TRACKS(R5) ; Setup tracks, MOVB R10,UCB$B_SECTORS(R5) ; Setup sectors0 MOVW R11,UCB$W_CYLINDERS(R5) ; Setup cylinders MOVL P2(AP),R1 ; Get the size" BNEQ 60$ ; Something specified+ MOVL R0,R1 ; Setup new size as dictated ; by T/S/C;J; Final check: see if the maximum block specified either by the user or byF; the filesize is less or equal to the size specified by the number of; tracks/sectors/cylinders.;60$: CMPL R1,R0 BLEQU 110$670$: MOVZWL #SS$_BADPARAM,R0 ; Bad geometry parameter BRW 120$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 sectors* MOVW #1,UCB$W_CYLINDERS(R5) ; 1 cylinder BRB 110$ ; Common code path390$: CMPL R1,#65536 ; Smaller then 65536 blocks ? BGEQ 100$ ; No, next check' MOVB #2,UCB$B_SECTORS(R5) ; 2 sectors) ASHL #-1,R1,R1 ; Divide file size by 26 MOVW R1,UCB$W_CYLINDERS(R5) ; And enter as cylinders. ASHL #1,R1,R1 ; Round to a 2 block boundary BRB 110$ ; Common code path.100$: MOVB #32,UCB$B_SECTORS(R5) ; 32 sectors* ASHL #-5,R1,R1 ; Divide file size by 327 MOVZWL #SS$_IVADDR,R0 ; Assume invalid media address+ CMPL R1,#65536 ; Does it fit in 16 bits? BGEQ 120$ ; No, abort6 MOVW R1,UCB$W_CYLINDERS(R5) ; And enter as cylinders/ ASHL #5,R1,R1 ; Round to a 32 block boundary9110$: MOVL R1,UCB$L_MAXBLOCK(R5) ; Set max. nr of blocks" MOVZWL #SS$_NORMAL,R0 ; Success 120$: RSB 5 .SBTTL LD_SET_SEED, Set seed unit number FDT routine;+++/; LD_SET_SEED, Set seed unit number FDT routine;; Functional description:;:; This routine sets the seed number to the specified value; ; 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)*; 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 value; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;-- LD_SET_SEED: MOVL P1(AP),R0 ; Get value CMPL R0,#9999 ; Limit range BGTRU 10$1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB3 MOVL DDB$L_UCB(R1),R1 ; Get address of UNIT 0 UCB* MOVW R0,UCB$W_UNIT_SEED(R1) ; Setup seed MOVL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/O%10$: MOVL #Sjm$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY}"cS$_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;; Functional description:;A; This routine is the FDT routine to set the allocation class for; the LD devices.; ; 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)*; 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:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_SET_ALLOCLASS: MOVL P1(AP),R2 ; Get value CMPL R2,#255 ; Limit range  BGTRU 10$ ; Out of range. TSTW LD_REFCNT ; Already someone connected? BNEQ 20$ ; Yes, not allowed- FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Synchronize SAVIPL=-(SP),- PRESERVE=NO1 MOVL UCB$L_DDB(R5),R1 ; Get address of port DDB; MOVL R2,DDB$L_ALLOCLS(R1) ; Setup allocation class in DDB+ MOVL UCB$L_CDDB(R5),R1 ; Get address CDDB= MOVL R2,CDDB$L_ALLOCLS(R1) ; Setup allocation class in CDDB0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock& NEWIPL=(SP)+,- ; Return to old ipl PRESERVE=NO MOVL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/O%10$: MOVL #SS$_BADPARAM,R0 ; Trouble BRB 30$:20$: MOVZWL #SS$_UNSAFE,R0 ; No devices may be connected(30$: JMP G^EXE$ABORTIO ; Abort the I/O 5 .SBTTL LD_DISABLE_TRACE, Disable tracing FDT routine;+++/; LD_DISABLE_TRACE, Disable tracing FDT routine;; Functional description:;?; This routine disables tracing and deallocates the tracebuffer; ; 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)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--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 pointer9 MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R1 ; Get allocated size& SUBL2 #12,R0 ; Point to real start) JSB G^EXE$DEANONPGDSIZ ; Release memory2 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 R4! MOVL R0,R1 ; Amount to return" MOVL R2,R4 ; Process to credit0 BSBW LD_RETURN_QUOTA ; Credit correct process0 ; Ignore errors (Proc. may have gone away) MOVL (SP)+,R4)25$: UNLOCK_TRACE ; Unlock trace mutex" MOVZWL #SS$_NORMAL,R0 ; Success' JMP G^EXE$FINISHIOC ; Finish the I/O,30$: JMP G^EXE$ABORTIO ; And abort the I/O 3 .SBTTL LD_ENABLE_TRACE, Enable tracing FDT routine;+++-; LD_ENABLE_TRACE, Enable tracing FDT routine;; Functional description:;;; This routine allocates a tracebuffer and enables tracing ; ; 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)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_ENABLE_TRACE:7 MOVZWL #SS$_TOOMUCHDATA,R0 ; Assume trace already set2 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BNEQ 10$ ; Already set0 MOVZWL #SS$_BADPARAM,R0 ; Assume bad parameter) MOVL P1(AP),R1 ; Get size (in entries) BEQL 10$ ; Nothing??@ MULL2 #LDTRCENT_K_LENGTH,R1 ; Length of LDTRCENT * #of entries0 MOVL R1,UCB$L_LD_TRCBUFSIZ(R5) ; Save used size;K; Add packet overhead (we need a packet of minimal FKB$K_LENGTH bytes to beJ; able to deallocate the packet as a forkblock when the driver is reloaded;& ADDL2 #12,R1 ; Add packet overhead4 BISL2 #^X80000000,R1 ; Avoid check against MAXBUF. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,20$ ; Enuf left/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quota10$: BRW 30$ ; Get out(20$: JSB G^EXE$ALONONPAGED ; Get memory BLBC R0,10$ ; Error" ADDL2 #12,R2 ; Leave some room;, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex( MOVL IRP$L_PID(R3),- ; Register owner UCB$L_LD_TRCBUFOWN(R5)- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet 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% UNLOCK_TRACE ; Unlock trace mutex MOVZWL #SS$_NORMAL,R0' JMP G^EXE$FINISHIOC ; Finish the I/O,30$: JMP G^EXE$ABORTIO ; And abort the I/O 5 .SBTTL LD_RESET_TRACE, Reset tracebuffer FDT routine;+++/; LD_RESET_TRACE, Reset tracebuffer FDT routine;; Functional description:;-; This routine resets the tracebuffer pointer; ; 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)*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_RESET_TRACE:- MOVZWL #SS$_NODATA,R0 ; Assume not yet set2 MOVL UCB$L_LD_TRCBUF(R5),R2 ; Get buffer pointer BEQL 10$ ; Not there, LOCK_TRACE ACCESS=WRITE ; Lock trace mutex- CLRL UCB$L_LD_TRCWRAP(R5) ; Not yet wrapped. 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 / .SBTTL LD_ENABLE_PROTECT, Enable write protect;+++*; LD_ENABLE_PROTECT, Enable write protect;; Functional description:;<; This routine enables write-protection on the logical disk.; ; 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)*; 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; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--2LD_ENABLE_PROTECT: ; Enable protect FDT routine1 BISL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit* BISB2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$B_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC 1 .SBTTL LD_DISABLE_PROTECT, Disable write protect;+++,; LD_DISABLE_PROTECT, Disable write protect;; Functional description:;=; This routine disables write-protection on the logical disk.; ; 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)*; 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; ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--4LD_DISABLE_PROTECT: ; Disable protect FDT routine1 BICL2 #DEV$M_SWL,UCB$L_DEVCHAR(R5) ; General bit* BICB2 #UCB$M_LD_PROTECT,- ; Specific bit UCB$B_LD_FLAGS(R5) MOVZWL #SS$_NORMAL,R0 JMP G^EXE$FINISHIOC / .SBTTL LD_DSE, Data secutiry erase fdt routine;+++*; LD_DSE, Data secum$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY-"ttiry erase fdt routine;; Functional description:;@; This is the FDT routine for the Data Security Erase operation.C; The byte count (P2) is stored in IRP$L_BCNT. The starting logical?; block (P3) is stored in IRP$L_MEDIA. Control is transfered toD; EXE$QIODRVPKT, thus queueing the I/O request to the driver's start; I/O routine.; ; 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)*; 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 count!; P3(AP) - Starting logical block; ; Outputs:;; IRP$L_BCNT(R3) - Byte count*; IRP$L_MEDIA(R3) - Starting logical block;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--.LD_DSE: ; Data security erase FDT routine5 MOVL P2(AP),IRP$L_BCNT(R3) ; Setup erase byte count8 MOVL P3(AP),IRP$L_MEDIA(R3) ; Setup erase starting LBN0 JMP G^EXE$QIODRVPKT ; Send request to STARTIO / .SBTTL LD_DEALLOC_TRCBUF, Trace buffer release;+++*; LD_DEALLOC_TRCBUF, Trace buffer release;; Functional description:;;; This routine deallocates the tracebuffer if we disconnect;; from the physical device (in case of cloned device) or in; case of driver reload.;F; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).; ; Inputs:;1; R4 - address of the PCB (process control block)#; - 0 if called from LD_DRV_UNLOAD.; R5 - address of the UCB (unit control block); ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_DEALLOC_TRCBUF:+ MOVL UCB$L_LD_TRCBUF(R5),R0 ; Get address BNEQ 10$ ; Buffer available BRW 60$ 10$: TSTL R4 ; PCB available?) BEQL 20$ ; No, no need to 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 owner UNLOCK_TRACE BRB 30$.20$: CLRL UCB$L_LD_TRCBUF(R5) ; Zero pointers+ MOVL UCB$L_LD_TRCBUFOWN(R5),R1 ; Get owner#30$: MOVQ R3,-(SP) ; Save R3 + R4/ MOVL UCB$L_LD_TRCBUFALLOCSIZ(R5),R3 ; Get size' SUBL2 #12,R0 ; Account for overhead" ASSUME FKB$B_FLCK EQ FKB$B_TYPE+1- MOVW #>,- ; and proper spinlock1 FKB$B_TYPE(R0) ; for this to be a fork block PUSHL R5 ; Save UCB MOVL R0,R5 ; Copy address& PUSHAB 40$ ; Set up return address FORK ; Create fork% MOVL R5,R0 ; Deallocate the block" 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 creditB ADDL3 #12,UCB$L_LD_TRCBUFSIZ(R5),R0 ; Calculate size we requested+ CMPL PCB$L_PID(R4),R1 ; Are we the owner? BNEQ 50$ ; No- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota BRB 60$ 50$: PUSHL R4 MOVL R1,R4 ; Owner pid! MOVL R0,R1 ; Amount to return% BSBW LD_RETURN_QUOTA ; Credit user MOVL (SP)+,R460$: RSB 1 .SBTTL LD_DEALLOC_WATCHBUF, Watch buffer release;+++,; LD_DEALLOC_WATCHBUF, Watch buffer release;; Functional description:;<; This routine deallocates all watchbuffers if we disconnect;; from the physical device (in case of cloned device) or in; case of driver reload.;F; This routine may be called from any IPL (needed in case we're called/; from LD_DRV_UNLOAD which runs at IPL$_POWER).; ; Inputs:;1; R4 - address of the PCB (process control block)#; - 0 if called from LD_DRV_UNLOAD.; R5 - address of the UCB (unit control block); ; Outputs:;;; The routine must preserve all registers except R0-R2, and ; R9-R11.;;--LD_DEALLOC_WATCHBUF: TSTL R4 ; PCB available?) BEQL 50$ ; No, no need to synchronize5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork level SAVIPL=-(SP)110$: REMQUE @UCB$L_LD_WATCHQFL(R5),R0 ; Get entry BVS 40$ ; None there' MOVL LDWATCH_L_PID(R0),R1 ; Get owner" PUSHL R0 ; Save buffer address CMPL R1,PCB$L_PID(R4) ; Ours? BNEQ 20$ ; No1 MOVL #LDWATCHENT_K_LENGTH,R0 ; Amount to return- JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota BRB 30$ 20$: PUSHL R4 MOVL R1,R4 ; Get pid0 MOVL #LDWATCHPT_K_LENGTH,R1 ; Amount to return3 BSBW LD_RETURN_QUOTA ; Return to correct process MOVL (SP)+,R4,30$: MOVL (SP)+,R0 ; Restore bufferpointer' JSB G^COM$DRVDEALMEM ; 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 there' JSB G^COM$DRVDEALMEM ; Dealloc memory BRB 50$60$: RSB 7 .SBTTL LD_ENABLE_WATCH, Enable watchpoints FDT routine;+++1; LD_ENABLE_WATCH, Enable watchpoints FDT routine;; Functional description:;/; This is the FDT routine to enable watchpoints; ; 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)*; 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, and ; R9-R11.;;--LD_ENABLE_WATCH:& 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 size4 JSB G^EXE$WRITECHK ; Check buffer for read access) MOVL P2(AP),R2 ; Get number of entries# MOVL R2,IRP$L_OBCNT(R3) ; Save it;; Validate inputbuffer;;10$: BBC #LDWATCHPT_V_FILE,- ; Check for virtual file mode LDWATCHPT_W_FLAGS(R0),60$5 TSTL LDWATCHPT_L_LBN(R0) ; Zero not allowed for VBN BEQL 40$ PUSHR #^M3 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 addressT POPR #^M% BGEQ 20$ ; Must be system address'6 CMPB FCB$B_TYPE(R1),#DYN$C_FCB ; Check if it is a FCB BNEQ 20$ ; No, return error $ TSTW FCB$W_REFCNT(R1) ; File open? BEQL 20$ ; No, error& MOVL FCB$L_WLFL(R1),R9 ; Get any wcb7 CMPL R5,WCB$L_ORGUCB(R9) ; File must be on our device ! BNEQ 50$ ; Other device, quitS3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too bigD FCB$L_FILESIZE(R1) BGTRU 40$ ; Too big, quitT* MOVL IRP$L_WIND(R3),R1 ; Get WCB address BEQL 20$ ; Should not happen5 BBS #WCB$V_CATHEDRAL,- ; Must have cathedral windowS WCB$B_ACCESS(R1),70$C20$: MOVZWL #SS$_FORMAT,R0 ; FCB invalid or not completely mappedL BRW 130$N-30$: MOVZWL #SS$_BADPARAM,R0 ; Bad parameterN BRW 130$ ,40$: MOVZWL #SS$_ILLBLKNUM,R0 ; Illegal lbn BRW 130$I750$: MOVZWL #SS$_DEVREQERR,R0 ; File not on our deviceN BRW 130$N@60$: BBS #LDWATCHPT_V_NOLBN,- ; Check for non-transfer function LDWATCHPT_W_FLAGS(R0),70$ 3 CMPL LDWATCHPT_L_LBN(R0),- ; Check if not too bigO UCB$L_MAXBLOCK(R5) BGTRU 40$ ; Too big, quitO070$: 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?o BEQL 120$ ; Yes, no priv9 CMPL IRP$L_PID(R3),UCB$L_PID(R5) ; Do we own the device?l& BNEQ 110$ ; No, action not allowed;oC; We charge seperately for every packet. We need to do this becauseIG; EXE$DEBIT_BYTCNT_BYTLM_NW rounds the size up to 64 bytes. If we wouldeH; charge for it in one chunk we would get problems crediting the buffers!; which we need to do one by one.r;i(80$: PUSHL R0 ; Save, destroyed later4 MOVL #LDWATCHENT_K_LENGTH,R1 ; Bytecount to charge. JSB G^EXE$DEBIT_BYTCNT_BYTLM_NW ; Check quota BLBS R0,90$ ; Enuf leftt ADDL2 #4,SP ; Adjust stack/ MOVZWL #SS$_EXBYTLM,R0 ; Out of bytlim quotar BRB 130$C90$: ADDL3 #LDWATCHPT_K_LENGTH,(SP)+,R0 ; Point to next input enJ+$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYD"try0 DECL R2 ; Next packetg BLEQ 100$ BRW 10$7100$: MOVQ R10,R0 ; Get buffer address and bytecountu 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$ 5120$: MOVZWL #SS$_NOCMKRNL,R0 ; CMKRNL priv requiredt)130$: JMP G^EXE$ABORTIO ; Abort the I/Ob a9 .SBTTL LD_DISABLE_WATCH, Disable watchpoints FDT routiney;+++3; LD_DISABLE_WATCH, Disable watchpoints FDT routine ; ; Functional description:b;e0; This is the FDT routine to disable watchpoints;D ; Inputs:g;o; R0-R2 - scratch registersg.; 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)B*; 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:;a;; The routine must preserve all registers except R0-R2, and8 ; R9-R11.e;o;--0LD_DISABLE_WATCH:y$ CLRL IRP$L_OBCNT(R3) ; Zero count( MOVL P2(AP),R1 ; Something specified? BEQL 10$ ; Remove allp# MOVL R1,IRP$L_OBCNT(R3) ; Save itc2 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$ ; Errorc010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O$20$: JMP G^EXE$ABORTIO ; Abort I/O S5 .SBTTL LD_GET_WATCH, Get watchpoint info FDT routine ;+++/; LD_GET_WATCH, Get watchpoint info FDT routine-;4; Functional description:r; 8; This routine retrieves info about current watchpoints,(; as well as the suspended process list.;n ; Inputs: ;d; R0-R2 - scratch registers .; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)y.; 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.; P1(AP) - Address of watchpt structure buffer*; P2(AP) - Size in bytes of watchpt buffer;, ; Outputs:;u;; The routine must preserve all registers except R0-R2, and ; R9-R11.D;V;--R LD_GET_WATCH:r+ BBS #LDIO_V_INQUIRE,- ; Return list size?  IRP$L_EXTEND(R3),10$& MOVL P1(AP),R0 ; Get buffer address% MOVL P2(AP),R1 ; Get buffer lengthi4 JSB G^EXE$READCHK ; Check buffer for write access% BSBW LD_GETBUF1 ; Get systembufferS BLBC R0,20$ ; ErrorE010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O AH .SBTTL LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;+++B; LD_RESUME_WATCH, Resume suspended watchpoint threads FDT routine;r; Functional description:D;c:; This routine resumes threads which were suspended when a; 'suspend' watchpoint was hit;$ ; Inputs: ;v; R0-R2 - scratch registers .; R3 - address of the IRP (I/O request packet)1; R4 - address of the PCB (process control block)k.; R5 - address of the UCB (unit control block)1; R6 - address of the CCB (channel control block)D*; R7 - bit number of the I/O function code6; R8 - address of the FDT table entry for this routine; R9-R11 - scratch registers(; P1(AP) - Address of watchpt structures&; P2(AP) - Count of watchpt structures;P ; Outputs:;f;; The routine must preserve all registers except R0-R2, andc ; R9-R11. ;c;--$LD_RESUME_WATCH:& MOVL P1(AP),R0 ; Get buffer address) MOVL P2(AP),R1 ; Get number of entriese2 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$ ; ErrorC010$: JMP G^EXE$QIODRVPKT ; Finish in start I/O(20$: JMP G^EXE$ABORTIO ; Abort the I/O b= .SBTTL LD_GETBUF + LD_GETBUF1, Get and fill temporary buffero;++++; LD_GETBUF, Get and fill temporary bufferb#; LD_GETBUF1, Get temporary buffero;; Functional description:;-A; This routine allocates a buffer to use to get/send data to/from=C; start I/O routines. The userbuffer data will be copied (LD_GETBUF8; only).;4 ; Inputs:o;h; 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)n.; 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;B ; Outputs:;R ; R0 - statusB; R2 - sytem buffer addressP$; IRP$L_SVAPTE - systembuffer addres;M;; The routine must preserve all registers except R0-R2, and. ; R9-R11.E;--- .ENABLE LSB LD_GETBUF1:<( CLRL -(SP) ; Flag not to fill buffer BRB 10$ LD_GETBUF:( MOVZBL #1,-(SP) ; Flag to fill buffer210$: MOVAB 12(R1),R1 ; Set size of system buffer PUSHR #^MX7 JSB G^EXE$DEBIT_BYTCNT_ALO ; Allocate a system bufferb$ BLBC R0,30$ ; Any quota errors ? POPR #^M7 MOVL R2,IRP$L_SVAPTE(R3) ; Save system buffer addressN4 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 ; Okay  BRB 40$%30$: POPR #^M ; Error returno"40$: ADDL2 #4,SP ; Adjust stack RSB .DISABLE LSB5 2 .SBTTL LD_MAKE_RESNAM, Form private resource name;+++-; LD_MAKE_RESNAM, Form private resource namew;i; Functional description:;;t@; Form resourcename for lock to coordinate clusterwide access to&; logical disk file or replaced device; +; For a file this resourcename consists of:,;; 9 bytes: '$LOGDISK_'5); 1 byte: 1 for private mounted volume,,; 2 for system wide; 12 bytes: volume lockname ; 3 bytes: 0>; 1 word: FIDR; 1 word: SEQn; 1 word: RVNM;B; For a device this will be:;M; 9 bytes: '$LOGDISK_'t ; 1 byte: 0&; 1 byte: physical devicename length!; 20 bytes: alloclass devicenameC; ; Inputs:;:; R3 IRP addressB; R4 PCB address); R5 UCB addressN&; R9 FCB address (connect 'file' only);>; Outputs:None.a; 3; Implicit outputs: Resource name in UCB is writtenR;N;---LD_MAKE_RESNAM:M PUSHR #^MLC MOVAB UCB$T_LD_FILE_RESNAM(R5),R1 ; Setup pointer to resource name,! MOVZBL #31,(R1)+ ; Fill lengthN" MOVAB 4(R1),(R1)+ ; And address% MOVL #^A/$LOG/,(R1)+ ; "$LOGDISK_"S MOVL #^A/DISK/,(R1)+  MOVB #^A/_/,(R1)+( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),10$ CLRB (R1)+ ; FillerR PUSHL R4 ; Save PCBA# PUSHL R1 ; Save current pointer- CLRB (R1)+ ; Length byte, filled in later + JSB G^SCH$IOLOCKR ; Lock the IO database  CLRQ (R1) ; Clear buffer CLRQ 8(R1); CLRL 16(R1) MOVZBL #20,R0 ; Buffer sizeN! MOVZBL #1,R4 ; DVI$_ALLDEVNAMF PUSHL R5 ; Save UCBO5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCBC1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicename  MOVL (SP)+,R5 ; Restore UCB" MOVL (SP)+,R0 ; Recover pointer MOVB R1,(R0) ; Save length MOVL (SP)+,R4 ; Restore PCB. JSB G^SCH$IOUNLOCK ; Unlock the IO database BRW 50$+10$: MOVB #1,R2 ; Assume private 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 VCBd. TSTW VCB$W_RVN(R2) ; Relative volume number) BEQL 30$ ; Branch if not a volume setE+ MOVL VCB$L_RVT(R2),R0 ; Fetch RVT address  BEQL 30$ ; Not there5 MOVAB RVT$T_VLSLCKNAM(R0),R2 ; Use this as locknameD BRB 40$730$: MOVAB VCB$T_VOLCKNAM(R2),R2 ; Lock name from herel040$: MOVL (R2)+,(R1)+ ; Copy 12 bytes lockname MOVL (R2)+,(R1)+  MOVL (R2)+,(R1)+ & CLRW (R1)+ ; Followed by 3 bytes 0 CLRB (R1)+m/ MOVW FCB$W_FID_NUM(R9),(R1)+ ; Insert File IDE$ MOVW FCB$W_FID_SEQ(R9),(R1)+ ; SEQ$ MOVW FCB$W_FID_RVN(R9),(R1)+ ; RVN50$: POPR #^M RSB ; C'est t $LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYoute _? .SBTTL LD_SHAD_WCHECK - Check write to shadow member for privsD;+++;$@; LD_SHAD_RWCHECK - Check read/write to shadow mbr for privilege; ; Functional Description:;ED; Allow only processes with SYS privilege to perform WRITES to0; Host Based Shadowing shadow set members.; ; Inputs:;E; R3 IRP addressy$; R5 UCB address (member);;; Implicit inputs: None.;.; Outputs:None.b;k; Implicit outputs: None.L; ; Condition codes:; H; SS$_ILLIOFUNC - I/O directed to shadow set member by a process5; that doesn't have sys priv.u;---LD_SHAD_WCHECK:_4 BBC #DEV$V_SHD,- ; If this device is not a shadow- UCB$L_DEVCHAR2(R5),10$ ; set member, quitp) MOVL IRP$L_ARB(R3),R0 ; Get ARB addressS" BEQL 20$ ; If ARB absent, exit ASSUME PRV$V_SYSPRV LT 329 BBC #PRV$V_SYSPRV,ARB$Q_PRIV(R0),20$; No SYSPRV, illegaln&10$: RSB ; Continue FDT processing120$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error statusv- JMP G^EXE$FINISHIOC ; Complete I/O request  K( .SBTTL LD_CRESHAD - CRESHAD FDT routine( .SBTTL LD_REMSHAD - REMSHAD FDT routine;+++; "; LD_CRESHAD - CRESHAD FDT routine"; LD_REMSHAD - REMSHAD FDT routine;l; Functional Description:A;,B; Dispatch CRESHAD and REMSHAD requests to shadowing driver.;, ; Inputs: ; ; R3 IRP addressd$; R5 UCB address (member); -; Implicit inputs: Dispatch vector filled in. ;L; Outputs:None.a;s; Implicit outputs: None.N;; Condition codes:; 5; SS$_ILLIOFUNC - Dispatch vector not set up.aD; SS$_DEVNOTSHR - Shadowset exists already somewhere in the cluster.;e;;; The routine must preserve all registers except R0-R2, andC ; R9-R11.1;---!LD_CRESHAD: ; ----> IO$_CRESHADC!LD_REMSHAD: ; ----> IO$_REMSHADA. MOVL G^EXE$GL_HBS_PTR,R0 ; Shadow Dispatcher' BGEQ 10$ ; Illegal if not filled in! JMP (R0) ; Jump to dispatcher 110$: MOVZBL #SS$_ILLIOFUNC,R0 ; Set error statuse- JMP G^EXE$FINISHIOC ; Complete I/O requestD C: .SBTTL LD_CONTROL_INIT, Controller initialization routine;+++8; LD_CONTROL_INIT, Readies controller for I/O operations; ; Functional description: ;M<; The operating system calls this routine in 3 places:;A!; at system startup 3; during driver loading and reloading,4; during recovery from a power failure;uH; This routine is a NOP for driver reloading and power failure recovery.<; For system startup and driver loading it allocates a CDDB.;;E ; Inputs:_;BA; R4 - address of the CSR (controller status register) ;; 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);L ; Outputs:;E7; The routine must preserve all registers except R0-R3.L;D;---+LD_CONTROL_INIT: ; Initialize controllerO6 MOVB #SPL$C_IOLOCK8,- ; Init device spin lock index. CRB$B_FLCK(R8) PUSHQ R4 ; Save CSR & IDBN CLRL R4 ; Init errorflag1 MOVZWL IDB$W_UNITS(R5),R0 ; Get number of unitsD BRB 20$;MH; Check for the correct UCB size if we're reloaded. If the size differs,/; then turn the unit offline to avoid problems. ;N,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; H; 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 cannoti?; signal it to SYSGEN. What we do is set the unit offline here.N;R& 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. OtherwiseE2 UCB$L_DEVCHAR2(R1) ; 'show device' has problems ; if the CDDB is 0.c) MOVZBL #1,R4 ; Flag something's wrong #20$: SOBGEQ R0,10$ ; Next deviceY$ TSTL R4 ; Did we have a problem? BNEQ 30$ ; Yes, quit1 TSTL CRB$L_AUXSTRUC(R8) ; Check if CDDB presentE" BEQL 40$ ; Branch if not there#30$: POPQ R4 ; Restore registersL& RSB ; Otherwise, return to caller;T/; Create fork thread to finish controller init.L;1 40$: MOVL R6,R4 ; Restore DDB MOVL R8,R5 ; Fork with CRB$ PUSHAB 30$ ; Fake return address FORK;S; Get pool for CDDB.;e) MOVZWL #CDDB$K_LENGTH,R1 ; Size of CDDBi, 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. 2 POPR #^M ; Restore saved registers.;a#; Initialize necessary CDDB fields. ;a MOVW R1,CDDB$W_SIZE(R2) ; Size' ASSUME CDDB$B_SUBTYPE EQ CDDB$B_TYPE+1R MOVW #>,- CDDB$B_TYPE(R2)L& 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), 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 number CDDB$Q_CNTRLID+2(R2)& MOVL UCB$L_MEDIA_ID(R5),- ; Media_id CDDB$Q_CNTRLID+4(R2)7; MOVL #MSCP$K_CM_EMULA,- ; Say our 'controller' is anf5; CDDB$B_CNTRLMDL(R2) ; emulated one. This prevents; ; MSCP servingt$ BRB 60$ ; fails then second load ; needs to store this.%50$: MOVL DDB$L_UCB(R4),R5 ; Get UCBo1 BICW2 #DEV$M_MSCP,- ; Zero this bit. Otherwise2 UCB$L_DEVCHAR2(R5) ; 'show device' has problems ; if the CDDB is 0.60$: RSB ; Return  e0 .SBTTL LD_CLONED_UCB, Cloned UCB initialization;+++*; LD_CLONED_UCB, Cloned UCB initialization;h; Functional description:-;$C; This routine is called by the $ASSIGN System Service to allow theKA; driver to initialize the cloned UCB. The driver is called with;; process context.;E ; Inputs:S; R0 = SS$_NORMAL; R2 = address of cloned UCBL; R3 = DDT addressC; R4 = PCB address#; R5 = address of the template UCBv; IPL = ASTDEL;l ; Outputs:;$; R5 = address of cloned UCB ; Destroyed = R5;S;--ULD_CLONED_UCB:! MOVL R2,R5 ; Copy UCB addressF7 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)l% CLRW UCB$W_CHARGE(R5) ; Zero charge;; BISW2 #UCB$M_NOCNVRT,UCB$W_DEVSTS(R5) ; Inhibit logical to$ ; physical xlation.2;H; Fall through to the LD_UNIT_INIT routine to initialize the cloned UCB.;_ P1 .SBTTL LD_UNIT_INIT, Unit initialization routinee;+++/; LD_UNIT_INIT, Readies unit for I/O operationss;u; Functional description:L;VA; The operating system calls this routine after calling the.*; controller initialization routine:;r!; at system startupB%; during driver loadingo4; during recovery from a power failure(; during initialization of a cloned UCB; ;$ ; Inputs:E;LA; R3 - address of the CSR (controller status register)_9; R5 - address of the UCB (unit control block)w; 7; IPL = IPL$_ASTDEL (jump/flow into from LD_CLONED_UCB)@; = IPL$_POWER (powerfail recovery and unit initialization);C ; Outputs:;N7; The routine must preserve all registers except R0-R1.C; ;---#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 ?< MOVAL UCB$L_LD_AIOFL(R5),- ; Initialize the Act. I/O queue UCB$L_LD_AIOFL(R5) MOVAL UCB$L_LD_AIOFL(R5),-c UCB$L_LD_AIOBL(R5)B MOVAL UCB$L_LD_TRCMUTEXQFL(R5),- ; Initialize trace mutex wait q. UCB$L_LD_TRCMUTEXQFL(R5)! MOVAL UCB$L_LD_TRCMUTEXQFL(R5),-o UCB$L_LD_TRCMUTEXQBL(R5)@ MOVAL UCB$L_LD_TRCWAITQFL(R5),- ; Initialize trace data wait q. UCB$L_LD_TRCWAITQFL(R5) MOVAL UCB$L_LD_TRCWAITQFL(R5),- UCB$L_LD_TRCWAITQBL(R5)N8 MOVAL UCB$L_LD_WATCHQFL(R5),- ; Initialize watch queue UCB$L_LD_WATCHQFL(R5)R MOVAL UCB$L_LD_WATCHQFL(R5),- UCB$L_LD_WATCHQBL(R5)DB MOVAL UCB$L_LD_WATCHPNDQFL(R5), b`$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY!- ; Initialize watch pending queue UCB$L_LD_WATCHPNDQFL(R5)! MOVAL UCB$L_LD_WATCHPNDQFL(R5),-- UCB$L_LD_WATCHPNDQBL(R5)- CLRL UCB$L_LD_WATCHCNT(R5) ; No entries yetR8 MOVZWL #^XFFFF,UCB$L_LD_TRCMUTEX(R5) ; Init trace mutex' CLRB UCB$B_LD_FLAGS(R5) ; Clear flagse;o; Setup UCB CDDB field. ;h;20$: MOVZWL #SS$_NORMAL,R0 ; This will be returned to ourn ; caller (may be $ASSIGN) ) FORK ; Fork to allow controller inite! ; routine to complete first ) MOVL UCB$L_CRB(R5),R0 ; Get CRB addressa> 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 isd; BICW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Mark unit 0 as offlineZ2 CLRW UCB$W_UNIT_SEED(R5) ; Start with fresh unit BRB 40$; 8; Setup MSCP stuff since some drivers need it (STdriver);e.30$: MOVW UCB$W_UNIT(R5),- ; MSCP unit number UCB$W_MSCPUNIT(R5)7 BISW2 #UCB$M_ONLINE,UCB$W_STS(R5) ; Switch unit onlinea40$: RSB / .SBTTL LD_DRV_UNLOAD, driver unloading routineC;+++); LD_DRV_UNLOAD, Driver unloading routine;;P; Functional description:B;C8; The operating system calls this routine after a SYSGEN(; RELOAD command, IPL wil be IPL$_POWER.;R=; An eventually allocated trace- or watchpoint buffer will bee; returned to pool. ;p ; Inputs:;E; R6 - Address of DDBr; R10 - Address of DPT; ; Outputs:;t;dI; Due to a bug in SYSGEN we will NOT get the DDB address in R6, but zero.>-; Byebye VAX!! (This has been fixed in V6.0).H1; Because of this we will find the DDB ourselves.;E%; This routine may use all registers. ;---"LD_DRV_UNLOAD: ; Unload driver BSBW FIND_DDB ; Get the DDB/ MOVL DDB$L_UCB(R6),R5 ; Get first UCB addressB) MOVL UCB$L_CRB(R5),R7 ; Get CRB addresse CLRL R4 ; No PCB610$: BSBW LD_DEALLOC_TRCBUF ; Get rid of trace buffer5 BSBW LD_DEALLOC_WATCHBUF ; Get rid of watch buffersB( MOVL UCB$L_LINK(R5),R5 ; Try next unit BNEQ 10$ ; It's thereU MOVZWL #SS$_NORMAL,R0 RSB;f FIND_DDB:D;SD; Not many checks needed as SYSGEN already verified our existance...;h4 MOVAB DPT$T_NAME(R10),R9 ; Get addr of driver name, MOVZBL (R9)+,R8 ; Get size of driver name< MOVAL G^IOC$GL_DEVLIST,R6 ; Get address of device listhead310$: MOVL DDB$L_LINK(R6),R6 ; Get addr of next ddb;6 MOVAB DDB$T_DRVNAME(R6),R1 ; Get addr of driver name, MOVZBL (R1)+,R0 ; Get size of driver name0 CMPC5 R0,(R1),#0,R8,(R9) ; Driver names match? BNEQ 10$ ; Br if not( RSB ; Return with DDB address in R6 +# .SBTTL LD_START, Start I/O routineD;+++2; LD_START - Start a transmit or receive operation;i; Functional description:t; ; ; Inputs: ;C9; R3 - address of the IRP (I/O request packet)a9; R5 - address of the UCB (unit control block)o;n ; Outputs:;=F; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferredF>; R1 - 2nd longword of I/O status: device-dependent;-D; The routine must preserve all registers except R0-R2 and R4.;K;---%LD_START: ; Process an I/O packet ;eG; We will clear the BSY flag here so that if we are stalled in LD_TRACEDH; 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 ofR; multiple threads.;R5 BICW2 #UCB$M_BSY,UCB$W_STS(R5) ; Clear the busy flag " 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; (; Functions allowed with inactive device;C5 CMPB R4,#IO$_LD_CONTROL ; Private control function?I BNEQ 10$ ; No6 BRW LD_START_CONTROL ; Handle our own function codes110$: BSBW LD_CHECK_WATCH ; Check for watchpointm8 BLBS IRP$L_EXTEND(R3),15$ ; Action done by CHECK_WATCH7 BBS #UCB$V_LD_CONSTS,- ; Device in DISCONNECT state ?e UCB$B_LD_FLAGS(R5),20$7 MOVZWL #SS$_DEVINACT,R0 ; Set status to dev. inactivee&15$: BRW LD_DONE ; Complete the I/O820$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get UCB of phys. disk4 BBC #UCB$V_ONLINE,UCB$W_STS(R0),25$ ; Unit offline?A BBS #IRP$V_PHYSIO,IRP$W_STS(R3),30$ ; If set, phys. I/O functionI7 BBS #UCB$V_VALID,UCB$W_STS(R5),30$ ; If volume valid ?C7 MOVZWL #SS$_VOLINV,R0 ; Set status to volume invalidE& BRW LD_DONE ; And complete the I/O;D<; Someone pulled the device underneath us (it went offline).?; This may happen if we replaced a raid virutal unit with an LDn?; device. Subsequent unbinding of the raid array may take placeo<; 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.n;o825$: 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=30$: DISPATCH R4,TYPE=B,<- ; Dispatch according to functionE ,- ; ^X00" ,- ; ^X01 ,- ; ^X02" ,- ; ^X04$ ,- ; ^X08( ,-; ^X0A( ,-; ^X0B& ,- ; ^X0C( ,- ; ^X11" > ; ^X15: MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function code& BRB LD_DONE ; And complete the I/O LD_PACKACK:i8 BISW2 #UCB$M_VALID,UCB$W_STS(R5) ; Set volume valid bit' BRB LD_NORMAL ; And complete the I/O LD_AVAILABLE: LD_UNLOAD:> BICW2 #UCB$M_VALID,UCB$W_STS(R5) ; Clear the volume valid bit# ; Fall through into LD_NORMAL, LD_DRVCLR:LD_SEEK:LD_NOP: LD_NORMAL:0 MOVZWL #SS$_NORMAL,R0 ; Set status to success CLRL R2 ; Dummy LBN 9 BBS #UCB$V_LD_REPLACE,- ; More to do for replaced drive  UCB$B_LD_FLAGS(R5),LD_OTHER -LD_DONE: ; Driver processing is finished. * CLRL R1 ; Clear second status longword' BSBW LD_SAVE_TRACE ; Save trace data F BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread REQCOM ; Complete I/O.E M;+++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 isRK; lower, we raise IPL to synchronize. We queue the IRP to the Phys. diskR; driver, and simply return.K; The phys. disk driver will call I/O completion to get rid of the IRP etc.mK; Because we do not know if the Phys. disk requires the Block nr in phys. ,lK; or Logical format, we first convert it to Logical, because that is whatfK; we need, add the Starting LBN of the LD File, and go through the conver-u,; sion process again, like in the FDT rtn's.;---LD_TRANSFER_W:4 BBC #DEV$V_SWL,UCB$L_DEVCHAR(R5),- ; Write protect? LD_TRANSFER$9 MOVZWL #SS$_WRITLCK,R0 ; Yes, return write-lock statust BRW LD_DONE LD_TRANSFER:* MOVL IRP$L_MEDIA(R3),R2 ; Save for later: BBC #IRP$V_PHYSIO,IRP$W_STS(R3),20$ ; 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 BRB 20$;aH; Check if connected to DECRAM disk. The MDdriver assumes that if an IRPH; comes along with the PHYSIO bit set it has to convert IRP$L_MEDIA fromI; T/S/C format to LBN. This is normally true, except when it is shadowingpK; I/O. If that's the case we will clear the PHYSIO bit so that DECRAM won't!; attempt to convert IRP$L_MEDIA.p;e10$: BBC #UCB$V_LD_DECRAM,-2 UCB$B_LD_FLAGS(R5),20$4 BICW2 #IRP$M_PHYSIO,IRP$W_STS(R3) ; Zero PHYSIO bit820$: CMPL IRP$L_MEDIA(R3),- ; Does it fit on our disk ? UCB$L_MAXBLOCK(R5) BLEQ 30$ ; Yep, skip. MOVZWL #SS$_ILLBLKNUM,R0 ; Set return status& BRW LD_DONE ; And complete the I/O>30$: ADDL3 UCB$L_LD_STLBN(R5),- ; Add starting LBN of LD file IRP$L_MEDIA(R3),R0* PUSHR #^M ; Save our UCB address4 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get UCB of phys. disk5 JSB G^IOC$CVTLOGPHY ; Convert LBN to Phys. blk. nrG, POPR #^M ; Restore our UCB address LD_OTHER:I MOVL R2,R1 ; Save LBNA- BSBW LD_GETLDIOB ; Allocate I/O data block>" BLBC R0,LD_DONE ; Exit on error7 MOVL IRP$L_PID(R3),LDIOB_L_PID(R2) ; Copy PID from IRP_1 MOVL IRP$L_IOSB(R3),LDIOB_L_IOSB(R2) ; Copy IOSBE, CLRQ LDIOB_Q_ST_TIME(R2) ; Zero start time*Ln$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY" TSTL UCB$L_LD_TRCBUF(R5) ; Trace active? BEQL 10$ ; No, save overhead0 MOVL R1,LDIOB_L_MEDIA(R2) ; Copy Media address6 MOVL IRP$L_BCNT(R3),LDIOB_L_BCNT(R2) ; Copy Bytecount9 MOVW IRP$W_FUNC(R3),LDIOB_W_FUNC(R2) ; Copy Functioncoden. READ_SYSTIME LDIOB_Q_ST_TIME(R2) ; Start time?10$: MOVAL LD_COMPLETE,IRP$L_PID(R3) ; Setup I/O completion rtnh5 MOVL R3,LDIOB_L_IRP(R2) ; Copy IRP address in LDIOBe3 INSQUE LDIOB_L_QFL(R2),- ; Insert at end of queue  @UCB$L_LD_AIOBL(R5)e1 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get UCB phys. diske; J; The following exists to workaround a SHdriver problem. This driver needsE; the UCB field in the IRP to be pointing to the Virtual unit UCB. If I; that's not the case, we will crash. This may happen if the logical disk "; file is on a Phase II shadowset.P; We save the LD UCB in the IOSB field, this is not used before post-processing.@; In the I/O completion we will restore the IOSB from the LDIOB.;h3 MOVL R5,IRP$L_IOSB(R3) ; Save UCB in a safe place 3 MOVL R1,IRP$L_UCB(R3) ; Setup physical device UCBu3 MOVZBL UCB$B_FLCK(R5),R2 ; Get our forklock indexl> CMPB UCB$B_FLCK(R5),UCB$B_FLCK(R1) ; Compare (negative index)* BLSS 30$ ; Ok, ours is larger or equal4 MOVZBL UCB$B_FLCK(R1),R2 ; Get the lowest of the 2&30$: FORKLOCK LOCK=R2,- ; Synchronize SAVIPL=-(SP),-d PRESERVE=NO PUSHL R2 ; Save lock# PUSHL R5 ; Save our UCB addressr- MOVL R1,R5 ; Setup Phys. disk UCB addresss1 INCW UCB$W_QLEN(R5) ; Bump device queue length< BBSS #UCB$V_BSY,UCB$W_STS(R5),40$ ; Check if device is busy1 JSB G^IOC$INITIATE ; Initiate the I/O functionE BRB 50$ ; Common code flow>40$: MOVAL UCB$L_IOQFL(R5),R2 ; Get addr. of I/O queue listhd3 JSB G^EXE$INSERTIRP ; Insert IRP in device queuef&50$: MOVL (SP)+,R5 ; Restore our UCB MOVL (SP)+,R2 ; Restore lockT% FORKUNLOCK LOCK=R2,- ; Release lock  NEWIPL=(SP)+,-  PRESERVE=NO,- CONDITION=RESTORE RSB ; And simply return A;+++0; Convert a physical block into a logical block.5; Modify IRP$L_MEDIA into LBN instead of TRK/CYL/SEC.;G; LBN = (CYL * (TRACKS PER CYL) + TRACK) * (SECTORS PER TRACK) + SECTORr;--- LD_CNVRTOLOG:  PUSHR #^M& MOVZBL UCB$B_TRACKS(R5),R4 ; Get T/C% MOVZWL IRP$L_MEDIA+2(R3),R0 ; Get CG MULL2 R0,R4 ; R4=C*(T/C)% MOVZBL IRP$L_MEDIA+1(R3),R2 ; Get TR ADDL R4,R2 ; R2=C*(T/C)+Tu' MOVZBL UCB$B_SECTORS(R5),R4 ; Get S/Te& MULL2 R4,R2 ; R2=(C*(T/C)+T)*(S/T)# MOVZBL IRP$L_MEDIA(R3),R4 ; Get S ( 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$ D2 .SBTTL LD_START_CONTROL, Start LD control routine;+++-; LD_START_CONTROL, Start LD control routineR;R; Functional description:A;0B; This is the completion of the control processing. This has to be=; in system context since we may need to fork when we use theN); forklevel interface to the lockmanager.;;o ; Inputs:;9; R3 - address of the IRP (I/O request packet)D9; R5 - address of the UCB (unit control block)s;pD; The routine must preserve all registers except R0-R2 and R4.; ;---LD_START_CONTROL:n ASSUME LDIO_S_FUNC EQ 8F DISPATCH IRP$L_EXTEND(R3),TYPE=B,<- ; Dispatch according to function$ ,-. ,-0 ,-( ,-. ,-6 ,-% ,-a) >t; MOVZWL #SS$_ILLIOFUNC,R0 ; Set illegal I/O function codeO CLRL R1 REQCOM) a3 .SBTTL LD_START_CONNECT, LD Start connnect routine-;+++.; LD_START_CONNECT, LD Start connnect routine;:; 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 the); forklevel interface to the lockmanager.e;f ; Inputs:0; 9; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block) ;c ; Outputs:;MF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferred0; R1 - 0 ;AD; The routine must preserve all registers except R0-R2 and R4.;;---LD_START_CONNECT:u' BBC #LDIO_V_SHARE,- ; Shared access?( IRP$L_EXTEND(R3),20$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBb, BBS #DEV$V_CLU,- ; Cluster wide visible ? UCB$L_DEVCHAR2(R1),20$6 MOVZWL #SS$_ACCONFLICT,R0 ; Not cluster-wide visible 10$: BRW 130$tA20$: BSBW LD_ENQ_LD_LOCK ; Get lock of container file or devicen" BLBC R0,10$ ; It's not allowed( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),30$$ BRW 40$ ; Skip file manipulation430$: MOVL UCB$L_LD_FCB(R5),R2 ; Recover FCB address; >; The following should really be done under the F11B$s lock...;;7 INCW FCB$W_REFCNT(R2) ; Increment the reference countm3 INCW FCB$W_ACNT(R2) ; Increment the access count ;M5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBC& MOVL UCB$L_VCB(R1),R0 ; Get it's VCB7 ADAWI #1,VCB$W_TRANS(R0) ; Bump the transaction count ( ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ4 MOVL FCB$W_FID_NUM(R2),- ; Save FID for crosscheck UCB$W_LD_FID_NUM(R5) MOVW FCB$W_FID_RVN(R2),- UCB$W_LD_FID_RVN(R5) BRW 100$s;a; Replace drive processing;H; Take out devicelock for physical device to protect against access fromJ; another cluster member. If it fails then the device is in use, if we get;; the lock then dequeue it if cluster access was specified.m; -40$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4C MOVAB UCB$T_LD_PD_RESNAM+4(R5),R1 ; Setup pointer to resource name" MOVAB 4(R1),(R1)+ ; And address MOVL #^A/SYS$/,(R1)+ ; "SYS$" MOVZBL #16,R0 ; Buffer size ! MOVZBL #1,R4 ; DVI$_ALLDEVNAM- PUSHL R5 ; Save UCB 5 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the phys. disk UCB 1 JSB G^IOC$CVT_DEVNAM ; Get alloclass devicenameC MOVL (SP)+,R5 ; Restore UCB ADDL3 #4,R1,- ; Save lengthT UCB$T_LD_PD_RESNAM(R5) ENQ_LOCK MODE=#LCK$K_EXMODE,- LKSBL=UCB$Q_LD_PD_LKSB(R5),-V# RESNAME=UCB$T_LD_PD_RESNAM(R5),-b CMPLADR=60$,- EFLAGS=3 CMPW R0,#SS$_NOTQUEUED ; Any one else interested?v BNEQ 50$ ; Noa" MOVZWL #SS$_DEVALLOC,R0 ; In use;2,; Failed due to arbitration via device lock.1; Make sure that we get rid of the $LOGDISK lock. ;n 50$: MOVL R0,R4 ; Save status) MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4R' $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5)0 ; Get rid of lock, MOVQ UCB$L_LD_FR3(R5),R3 ; Restore R3 + R4, MOVL R0,R1 ; Return eventual error in R1 MOVL R4,R0 ; Restore status BRW 140$ ; Return errorC;) ; Completion of ENQ ends up here;(260$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers: MOVZWL UCB$Q_LD_PD_LKSB(R5),R0 ; Check completion status# BLBC R0,50$ ; Something's wrong_. BBS #LDIO_V_SHARE,- ; Shared access wanted? IRP$L_EXTEND(R3),70$5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBT, MOVL UCB$Q_LD_PD_LKSB+4(R5),- ; Save lockid UCB$L_LOCKID(R1) BRB 90$-70$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4V8 $LCK_DEQ UCB$Q_LD_PD_LKSB+4(R5) ; Get rid of devicelock, MOVQ UCB$L_LD_FR3(R5),R3 ; Restore R3 + R4 BLBC R0,50$ ; Error 5 MOVL UCB$L_LD_PDUCB(R5),R1 ; Get the phys. disk UCBR&90$: MOVL UCB$L_ORB(R5),- ; Save ORB UCB$L_LD_ORBSAV(R5)B= MOVL UCB$L_ORB(R1),- ; Get protection from physical device  UCB$L_ORB(R5),- MOVB UCB$B_DEVTYPE(R1),- ; Copy device type UCB$B_DEVTYPE(R5)E&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 cylindersb UCB$B_SECTORS(R5)o0 MOVL UCB$L_MAXBLOCK(R1),- ; Copy maximum block UCB$L_MAXBLOCK(R5). CLRL UCB$L_LD_STLBN(R5) ; No offset for read0 EXTV #DEV$V_SWL,#1,- ; Extract write-lock bit UCB$L_DEVCHAR(R1),R0/ INSV R0,#DEV$V_SWL,#1,- ; Insert (for CD-ROM)r UCB$L_DEVCHAR(R5)t* INCW UCB$W_REFC(R1) ; Mark our interest3 BICL2 #DEV$M_AVL,- ; Make unavailable for othersD UCB$L_DEVCHAR(R1)D1 BISB2 #UCB$M_LD_REPLACE,- ; Set special connectn UCB$B_LD_FLAGS(R5)) BISL2 #,-d0 UCB$L_DEVCHAR(R5) ; Set characteristics again% ; Needed after replace of CDroms;n ; Common exith;D8100$: MOVL UCB$L_MAXBCNT(R1),- ; Copy maximum bytecount UCB$L_MAXBCNN$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY6RT(R5)e5 CMPB UCB$B_DEVTYPE(R1),- ; Check if we connected to" #DT$_RAM_DISK ; a DECRAM disk BNEQ 110$ ; No/ BISB2 #UCB$M_LD_DECRAM,- ; Flag DECRAM in usea UCB$B_LD_FLAGS(R5)C110$: BICL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB non-deletable + BBC #LDIO_V_SHARE,- ; Shared accessable?k IRP$L_EXTEND(R3),120$t@ BISL2 #DEV$M_CLU,UCB$L_DEVCHAR2(R5) ; Make cluster wide visible& BISB2 #UCB$M_LD_SHARE,- ; Set status UCB$B_LD_FLAGS(R5)7120$: ADAWI #1,LD_REFCNT ; Count number of 'connects'o7 BISL2 #DPT$M_NOUNLOAD,- ; Prevent reloading of driver  DPT$TAB+DPT$L_FLAGS 4 BISB2 #UCB$M_LD_CONSTS,- ; Set status to connected UCB$B_LD_FLAGS(R5)0 MOVZWL #SS$_NORMAL,R0 ; Set status to success 130$: CLRL R1,140$: BSBW LD_SAVE_TRACE ; Save trace data REQCOM ; Complete the I/O #9 .SBTTL LD_START_DISCONNECT, LD Start disconnnect routine);+++4; LD_START_DISCONNECT, LD Start disconnnect routine;N; Functional description:h;JE; This is the completion of the disconnect processing. This has to be =; in system context since we may need to fork when we use the); forklevel interface to the lockmanager.i;s ; Inputs:E;09; R3 - address of the IRP (I/O request packet) 9; R5 - address of the UCB (unit control block)S;N ; Outputs:;CF; R0 - 1st longword of I/O status: contains status code and-; number of bytes transferred0; R1 - 0n;?D; The routine must preserve all registers except R0-R2 and R4.;$;---LD_START_DISCONNECT:4 BBS #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$B_LD_FLAGS(R5),20$4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),10$- MOVL UCB$L_LD_FCB(R5),R2 ; Copy FCB addressw3 DECW FCB$W_REFCNT(R2) ; Decr. the reference countQ/ DECW FCB$W_ACNT(R2) ; Decr. the access counte<10$: 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 count: BRW 40$;D; Replaced drive processings;s;20$: BBS #UCB$V_LD_SHARE,- ; If no shared access specifiedE3 UCB$B_LD_FLAGS(R5),30$ ; then no lock to dequeuet) MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4H8 MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB7 $LCK_DEQ LKID=UCB$L_LOCKID(R0) ; Get rid of devicelockB, MOVQ UCB$L_LD_FR3(R5),R3 ; Restore R3 + R4 BLBS R0,30$ ; SuccessC4 BBS #LDIO_V_ABORT,- ; Check if abort is specified IRP$L_EXTEND(R3),30$ BRW 60$ ; Unexpected error<30$: MOVL UCB$L_LD_PDUCB(R5),R0 ; Get the physical disk UCB! CLRL UCB$L_LOCKID(R0) ; Zero IDo5 DECW UCB$W_REFC(R0) ; We're not interested anymorer: BISL2 #DEV$M_AVL,UCB$L_DEVCHAR(R0) ; Make available again* MOVL UCB$L_LD_ORBSAV(R5),- ; Restore ORB UCB$L_ORB(R5);4 ; Common pathh;C-40$: MOVQ R3,UCB$L_LD_FR3(R5) ; Save R3 + R4h' $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5)s ; Get rid of lockr, MOVQ UCB$L_LD_FR3(R5),R3 ; Restore R3 + R4 BLBS R0,45$ ; Success 7 BBC #LDIO_V_ABORT,- ; Continue if abort is specifiedr IRP$L_EXTEND(R3),60$>45$: BISL2 #UCB$M_DELETEUCB,UCB$L_STS(R5) ; Make UCB deletable- MOVB #DT$_FD1,- ; Dev. type = foreign diskd UCB$B_DEVTYPE(R5)v5 CLRL UCB$L_MAXBLOCK(R5) ; Zero device specific info,&ASSUME UCB$B_TRACKS EQ UCB$B_SECTORS+1(ASSUME UCB$W_CYLINDERS EQ UCB$B_TRACKS+1 CLRL UCB$B_SECTORS(R5)A) BICL2 #DEV$M_SWL,- ; Restore writelock UCB$L_DEVCHAR(R5)p8 BICB2 #,- UCB$B_LD_FLAGS(R5)2 ADAWI #-1,LD_REFCNT ; Count down #of 'connects' BNEQ 50$ ; Some left5 BICL2 #DPT$M_NOUNLOAD,- ; Allow reloading of driverX DPT$TAB+DPT$L_FLAGSL450$: MOVZWL #SS$_NORMAL,R0 ; Set status to success 60$: CLRL R1' BSBW LD_SAVE_TRACE ; Save trace data REQCOMB #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;B; Functional description:H; I; This routine will enqueue the file lock of the specified file andD; 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.o;a%; The locking protocol is as follows:O;tI; First we will enqueue an EX-mode lock on our resource. If it is grantedUF; immediately (SS$_SYNCH returned) then it means that we are the firstC; one ever getting this lock. We will then fill the value block andWF; 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 ownscB; 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 converteE; 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.;I; If we encounter an invalid lock value block during this process we willhI; just rewrite it with the same contents. It's not documented, but we candH; 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).i; ;k ; Inputs: ;i9; R3 - address of the IRP (I/O request packet)r9; R5 - address of the UCB (unit control block) ;l; IOLOCK8 (=SCS) forklock held; ; Outputs:;s ; R0 - statusd; SS$_NORMAL - Successt5; SS$_FILALRACC - File in use in an incompatible wayC#; other - Returned by lockmanager#;$*; Only R3-R5 are preserved as we may fork.;);---LD_ENQ_LD_LOCK: 7 MOVL (SP)+,UCB$L_LD_SAVEPC(R5) ; Save caller's address  ; because we may forkC+ MOVQ R3,UCB$L_LD_FR3(R5) ; Save registers ENQ_LOCK MODE=#LCK$K_EXMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-% RESNAME=UCB$T_LD_FILE_RESNAM(R5),-B CMPLADR=50$,- EFLAGS=, CMPW R0,#SS$_SYNCH ; Are we the only one? BNEQ 40$r. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers;BC; 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.;SC MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value blockk! MOVL UCB$L_DDB(R5),R2 ; Get DDB 3 MOVL DDB$L_ALLOCLS(R2),- ; Setup allocation class LDLVB_L_ALLOCLS(R1)H% MOVW UCB$W_UNIT(R5),- ; Unit numberM LDLVB_W_UNIT(R1)( MOVW UCB$W_CYLINDERS(R5),- ; Cylinders LDLVB_W_CYLINDERS(R1)P" MOVB UCB$B_TRACKS(R5),- ; Tracks LDLVB_B_TRACKS(R1)$ MOVB UCB$B_SECTORS(R5),- ; Sectors LDLVB_B_SECTORS(R1)X1 MOVL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberp LDLVB_L_MAXBLOCK(R1)% CLRB LDLVB_B_FLAGS(R1) ; Init flagst' BBC #LDIO_V_SHARE,- ; Shared access?f IRP$L_EXTEND(R3),20$ BISB #LDLVB_M_SHARE,- LDLVB_B_FLAGS(R1) ; Set flags"20$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-E BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=90$;r; Lock is converted to CR.;e230$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion status -40$: BRW 90$ ; Exit with completion status;V; Lock is granted as EX.;T250$: MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers9 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get completion statusL;CN; 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.;S1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?& BEQL 60$ ; Yes, deal with it later# BLBC R0,90$ ; Something's wrong;VD; We now have the lock in EX mode. Check the lock value block to see; if we match.;cG60$: MOVAB UCB$A_LD_FILE_LVB(R5),R1 ; Setup pointer to lock value block0 BBC #LDLVB_V_SHARE,- ; Check for shared access LDLVB_B_FLAGS(R1),80$,1 BBC #LDIO_V_SHARE,- ; Shared access requested?E IRP$L_EXTEND(R3),80$! MOVL UCB$L_DDB(R5),R2 ; Get DDBp3 CMPL DDB$L_ALLOCLS(R2),- ; Check allocation classt LDLVB_L_ALLOCLS(R1) BNEQ 80$ ; No match % CMPW UCB$W_UNIT(R5),- ; Unit numberh LDLVB_W_UNIT(R1) BNEQ 80$ ; No ma$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY"tch ( CMPW UCB$W_CYLINDERS(R5),- ; Cylinders LDLVB_W_CYLINDERS(R1)o BNEQ 80$ ; No matchs" CMPB UCB$B_TRACKS(R5),- ; Tracks LDLVB_B_TRACKS(R1) BNEQ 80$ ; No matchA$ CMPB UCB$B_SECTORS(R5),- ; Sectors LDLVB_B_SECTORS(R1)z BNEQ 80$ ; No match 1 CMPL UCB$L_MAXBLOCK(R5),- ; Maximum blocknumberk LDLVB_L_MAXBLOCK(R1) BNEQ 80$ ; No matche) BRW 20$ ; Convert down to CR and exits;uE; 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.;H-80$: $LCK_DEQ LKID=UCB$Q_LD_FILE_LKSB+4(R5),-1 VALBLK=UCB$A_LD_FILE_LVB(R5)V ; Get rid of file lock. MOVQ UCB$L_LD_FR3(R5),R3 ; Restore registers BLBC R0,90$ ; Error / MOVZWL #SS$_FILALRACC,R0 ; Assume file in use0( BBC #LDIO_V_REPLACE,- ; Replace drive? IRP$L_EXTEND(R3),90$) MOVZWL #SS$_DEVALLOC,R0 ; Device In use.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 file lock blocking ast routine;e; Functional description:R; H; This routine will be called by the lockmanager when someone elseB; tries to enqueue an incompatible lock on the FILE resource name.;tB; We will convert the lock back to NL, and then we immediately tryD; to convert it back to CR. If this second conversion is successfull; we will just exit.;;C; If the second conversion reveals a 'value block invalid' error weB; must revalidate the lock. We do this by converting the lock back=; to NL, up to EX, back to NL and then back to CR. This lookssB; 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'tnA; 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.K;5 ; Inputs:t;k9; R5 - address of the UCB (unit control block),;$; IOLOCK8 (=SCS) forklock helds;M ; Outputs:; *; Only R3-R5 are preserved as we may fork.;i;---LD_ENQ_FILE_BLKRTN:n ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-h EFLAGS=,-h ERROR=60$: BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Check completion status;y.; Lock is converted to NL, convert back to CR.;0"10$: ENQ_LOCK MODE=#LCK$K_CRMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-m BLKADR=LD_ENQ_FILE_BLKRTN,- EFLAGS=,- ERROR=60$;;; Lock is converted to CR.;E4 MOVZWL UCB$Q_LD_FILE_LKSB(R5),R0 ; Get final status1 CMPL R0,#SS$_VALNOTVALID ; Value block invalid?)& BEQL 40$ ; Yes, deal with it later BLBS R0,30$ ; No error, exit.20$: BRW 60$ ; Bugcheck on all other errors30$: RSB;MJ; 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.A; "40$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-t EFLAGS=,-C ERROR=60$- BLBC UCB$Q_LD_FILE_LKSB(R5),20$ ; Get statusb;2.; Lock is converted to NL, convert back to EX.;d 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?a BEQL 50$ ; AcceptableS) BLBC R0,60$ ; Check completion status;BI; Lock is converted to EX, convert back to NL to rewrite the value block.t;e"50$: ENQ_LOCK MODE=#LCK$K_NLMODE,-! LKSBL=UCB$Q_LD_FILE_LKSB(R5),-r EFLAGS=,- ERROR=60$0 BLBC UCB$Q_LD_FILE_LKSB(R5),60$ ; Quit on error- BRW 10$ ; Ok, convert back to CR and exite;o;; Bugcheck since there's nobody to return this error to.... ;iC60$: BUG_CHECK INCONSTATE,FATAL ; Unexpected error from lockmanager 7/ .SBTTL LD_START_TRACE, Start I/O trace routined;+++); LD_START_TRACE, Start I/O trace routine9;1; Functional description:A;-M; This is the completion of the retrieve tracedata processing. This has to beeG; in system context since we need to fork when we find that there is noe; tracedata available.;; ; Inputs:e;9; R3 - address of the IRP (I/O request packet)D9; R5 - address of the UCB (unit control block)M; ; Outputs:;E ; None. ;dD; The routine must preserve all registers except R0-R2 and R4.;---LD_START_TRACE:$> INSQUE (R3),@UCB$L_LD_TRCWAITQBL(R5) ; Save wait IRP in queue;H; 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 buffersE; 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.;e RSB ; Return to callerd s9 .SBTTL LD_TRACE_AST, Display data AST completion routine ;+++4; LD_TRACE_AST, Display data AST completion routine; ; Functional description: ; 9; This is the final completion of the display processing.@; This routine is called as an AST routine in the context of theB; process issueing the I/O. We will move the newly available trace2; data to the user's buffer, and complete the I/O.; ; Inputs:#; 1; R4 - address of the PCB (process control block)T$; R5 - address of the ACB;e=; The routine must preserve all registers except R0-R5.(;--- LD_TRACE_AST:( MOVL ACB$L_ASTPRM(R5),R3 ; Restore IRP MOVL R5,R0 ; Address of ACBf( JSB G^EXE$DEANONPAGED ; Get rid of ACB% MOVL IRP$L_UCB(R3),R5 ; Restore UCBC) BSBW LD_MOVE_TRACE ; Move data to user - BLBC R0,10$ ; Missed count in R1 on error = DIVL3 #LDTRCENT_K_LENGTH,R2,R1 ; Convert size to #of entriesN,10$: CLRL IRP$L_SVAPTE(R3) ; No more SVAPTE5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork levelI SAVIPL=-(SP) % MOVL R3,UCB$L_IRP(R5) ; Restore IRPr. JSB G^IOC$REQCOM ; Complete the I/O request0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock+ NEWIPL=(SP)+,- ; Remain ast IPL$_ASTDELB PRESERVE=NO RSB @ .SBTTL LD_START_ENABLE_WATCH, Start I/O enable watch processing;+++;; LD_START_ENABLE_WATCH, Start I/O enable watch processingc; ; Functional description:f;=; This is the completion of the enable watchpoint processing.eB; Buffers will be allocated, and if any buffer which was specified?; by the user already exists we will return the charge for this); buffer to the user by ast.;n ; Inputs:b;k9; R3 - address of the IRP (I/O request packet)e9; R5 - address of the UCB (unit control block)t/; IRP$L_OBCNT - number of entries in userbufferu;p ; Outputs:;i ; None.-; D; The routine must preserve all registers except R0-R2 and R4.;---LD_START_ENABLE_WATCH:5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addresst) MOVL (R2),R2 ; Get userbuffer addressI% MOVL IRP$L_OBCNT(R3),R4 ; Get countT810$: MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Get queue listhead4 BSBW LD_FIND_WATCH_ENTRY_ENA ; Find matching entry BLBS R0,30$ ; Not foundO( CMPW LDWATCH_W_ACTION(R1),- ; Suspend? #WATCH_ACTION_SUSPENDu BNEQ 20$ ; No#5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpointst720$: BSBW LD_COPY_WATCH ; Found exact match, copy newE ; parameters except pidI> MOVL #LDWATCHENT_K_LENGTH,R1 ; Number of bytes already there PUSHR #^M + MOVL IRP$L_PID(R3),R4 ; Process to creditt1 BSBW LD_RETURN_QUOTA ; Give quota back to user POPR #^M BRW 40$m*30$: PUSHL R2 ; New entry, alloc buffer PUSHL R1 , MOVZWL #LDWATCHENT_K_LENGTH,R1 ; This size$ JSB G^EXE$ALONONPAGED ; Get buffer" BLBC R0,50$ ; Out of resources1 MOVB #DYN$C_BUFIO,LDWATCH_B_TYPE(R2) ; Fill type_' CLRW LDWATCH_W_FLAGS(R2) ; Zero flags' MOVW R1,LDWATCH_W_SIZE(R2) ; And sizet3 MOVAL LDWATCH_L_SUSPFL(R2),- ; Init suspend queuei LDWATCH_L_SUSPFL(R2) MOVAL LDWATCH_L_SUSPFL(R2),-  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 listheadn4 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 al$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYK"l buffers" MOVZWL #SS$_NORMAL,R0 ; Success BRB 60$45$: BRW 10$ ; Branch assist5-50$: ADDL2 #8,SP ; Adjust for error returny960$: MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Return current count  REQCOM  B .SBTTL LD_START_DISABLE_WATCH, Start I/O disable watch processing;+++=; LD_START_DISABLE_WATCH, Start I/O disable watch processingB;I; Functional description:;d>; 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.;A ; Inputs:v;e9; R3 - address of the IRP (I/O request packet)J9; R5 - address of the UCB (unit control block) /; IRP$L_OBCNT - number of entries in userbufferf;t ; Outputs:;0 ; None. ; D; The routine must preserve all registers except R0-R2 and R4.;---LD_START_DISABLE_WATCH:s0 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$C'10$: PUSHL R6 ; May not be destroyedD! CLRL R6 ; Nothing removed yetM5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer address$ BEQL 20$ ; Not there, remove all) MOVL (R2),R2 ; Get userbuffer addressn, MOVL IRP$L_OBCNT(R3),R4 ; Get wanted count820$: 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 lists CMPL R0,R1 ; At the end? BEQL 60$ ; Yes940$: REMQUE LDWATCH_L_FLINK(R1),R0 ; Exact match, remove + DECL UCB$L_LD_WATCHCNT(R5) ; Count packeth* INCL R6 ; Flag we've removed something" MOVL R0,R1 ; Use correct input5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpointse- PUSHR #^M ; Destroyed by deallocate95 BBC #LDWATCH_V_FILE,- ; Check for virtual file modet LDWATCH_W_FLAGS(R0),50$t- MOVL LDWATCH_L_FCB(R0),R2 ; Get FCB pointerD/ DECW FCB$W_REFCNT(R2) ; Decr. reference count + 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 creditD/ JSB G^EXE$DEANONPAGED ; Return buffer to poolL1 MOVL #LDWATCHENT_K_LENGTH,R1 ; Amount to returnU1 BSBW LD_RETURN_QUOTA ; Give quota back to userE POPR #^M BRB 20$"60$: TSTL R2 ; Next input entry BEQL 80$ ; All entries>70$: ADDL2 #LDWATCHPT_K_LENGTH,R2 ; Point to next input entry)80$: SOBGTR R4,20$ ; Check all packetss) MOVZWL #SS$_NORMAL,R0 ; Assume successe 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 REQCOM  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; ; Functional description: ;t>; This routine copies the data from the user's parameter block; into a watchpoint blockn;e ; 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).9; R5 - address of the UCB (unit control block)L;I ; Outputs:;D ; None.o;t;---LD_COPY_WATCH_NEW:" MOVL IRP$L_PID(R3),- ; Move pid LDWATCH_L_PID(R1)h7 BBC #LDWATCHPT_V_FILE,- ; Check for virtual file modee LDWATCHPT_W_FLAGS(R2),30$h PUSHR #^M) MOVL LDWATCHPT_L_SBK(R2),R4) MOVL SBK$L_FCB(R4),R4 ; Get FCB addressh MOVL R4,LDWATCH_L_FCB(R1)/ INCW FCB$W_REFCNT(R4) ; Incr. reference count+ INCW FCB$W_ACNT(R4) ; Incr. access count& MOVL UCB$L_VCB(R5),R5 ; Get it's VCB7 ADAWI #1,VCB$W_TRANS(R5) ; Bump the transaction counte* MOVL IRP$L_WIND(R3),R5 ; Get WCB address MOVL WCB$L_ORGUCB(R5),- LDWATCH_L_UCB(R1)e( ASSUME FCB$W_FID_NUM+2 EQ FCB$W_FID_SEQ% MOVL FCB$W_FID_NUM(R4),- ; Save FID  LDWATCH_W_FID_NUM(R1)u MOVW FCB$W_FID_RVN(R4),-$ LDWATCH_W_FID_RVN(R1)i7 MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumberD0 LDWATCH_L_VBN(R1) ; to virtual block for file7 MOVL LDWATCHPT_L_LBN(R2),R6 ; Get virtual blocknumbern7 MOVZWL WCB$W_NMAP(R5),R3 ; Number of mapping pointers .IF DF V6@ MOVAL WCB$L_P1_COUNT(R5),R5 ; Address of first mapping pointer10$: MOVL (R5)+,R4 ; Count .IFF2@ MOVAW WCB$W_P1_COUNT(R5),R5 ; Address of first mapping pointer10$: MOVZWL (R5)+,R4 ; Count .ENDC% CMPL R6,R4 ; Within this segment?t BLEQ 20$ ; Yes2 SUBL2 R4,R6 ; Account for size of this segment TSTL (R5)+ ; Skip lbnr SOBGTR R3,10$ ; Next segment;qC; We should never come here. The VBN was checked in the FDT routine5&; to be within the limits of the file.; BUG_CHECK INCONSTATE,FATALa$20$: DECL R6 ; Adjust for VBN = 15 ADDL3 R6,(R5),LDWATCH_L_LBN(R1) ; Save converted LBNl POPR #^M BRB LD_COPY_WATCH;30$: MOVL LDWATCHPT_L_LBN(R2),- ; Move logical blocknumberr LDWATCH_L_LBN(R1)t;n; Fall into LD_COPY_WATCHi; LD_COPY_WATCH:( MOVW LDWATCHPT_W_FLAGS(R2),- ; Options LDWATCH_W_FLAGS(R1)t+ MOVW LDWATCHPT_W_ACTION(R2),- ; What to dop 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)V RSB IE .SBTTL LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enablen;+++@; LD_FIND_WATCH_ENTRY_ENA, Locate a watchpoint entry for enable;; Functional description: ;c6; This routine locates a watchpoint entry. Entries are6; inserted in ascending order, so when we are past one9; entry we don't need to look any further. A special caser9; is signalled if the NOLBN flag is set. This is the casea8; for a non-transfer function. We can have more of these; for different functions.;O ; Inputs:;n:; R1 - address of ucb watchqueue entry listhead4; R2 - address of the users watchpt block;o ; Outputs:; ; R0 - 0 if found an exact match; 1 if not foundp; R1 - if R0 = 0 entry address#; if R0 = 1 address if insertionA;C;---LD_FIND_WATCH_ENTRY_ENA: MOVL R1,R0 ; Save for later !10$: MOVL (R1),R1 ; Next entry0 CMPL R1,R0 ; End of queue ?B BEQL 60$ ; Yes1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?O LDWATCH_W_FLAGS(R1),40$I, CMPW LDWATCHPT_W_FLAGS(R2),- ; Same flags? LDWATCH_W_FLAGS(R1) BNEQ 10$ ; NoU* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$D; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching block  LDWATCH_L_VBN(R1)M BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)B30$: BEQL 40$ ; Yes' BGTRU 10$ ; Not a higher number yetY 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?  LDWATCH_W_ACTION(R1) BEQL 80$ ; Yes;0'; Here everything matches except action "; Determine if action may be added;t5 CMPW LDWATCHPT_W_ACTION(R2),- ; Adding OPCOM action?G #WATCH_ACTION_OPCOMt BEQL 50$ ; Yes;,@; Add some action other than OPCOM. Make sure we don't overwrite; a current OPCOM action.;Q< CMPW LDWATCH_W_ACTION(R1),- ; Current packet OPCOM action? #WATCH_ACTION_OPCOMz2 BNEQ 80$ ; No, modify action of current packet;n,; We must check the next packet for a match.;T BRW 10$;rA; Make sure that a packet with an action for OPCOM is in front ofMF; 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 entryC; before the current location we have to provide the address of theu; predecessor.;e350$: MOVL LDWATCH_L_BLINK(R1),R1 ; Pick up addressi BRB 70$ ; of previous entrya;A; Nothing found in the queue. Insert new entry after the last one ;l360$: MOVL LDWATCH_L_BLINK(R0),R1 ; Pick up address ; of last entry4 70$: MOVZBL #1,R0 ; Not found BRB 90$80$: CLRL R0 ; Exact matchU90$: RSB aF .SBTTL LD_FIND_WATCH_ENTRY_DIS, Locate a watchpoint entry for disable;+++A; LD_FIND_WATCH_ENTRY_D$LD051.Bn61H#PG 0!W@Q~r u9--a1=LV-dY?{:fz~W>=[/yCP"Ja*Wc$:V /UbV _\3,W2QUN;3;YQ/!` =k`BbX8 nb~o FUw@37P.=byrU96 5'J1aJS *iQ^J{- z2R <-EOML 3ajX;XrK HW=^xv>3n* 3+|a[8)U(o 7OLa7=w TKol4#?\]PCq'B%,GVJtKKG PZjA55MJ9F-LY\LDvxC=)J=*R#.!l8axDJCd h|*lq:PXo$jr R[x=s!QG, cn}17pW|?pY4$lQQ\:ZrKX& VTj%y|7a:j(lf}|fHމ>(CX YRYb$]ad:yOw;`I -0H~A>:ebGK-CoBk[1>*F9zKOVGAr_`1V a}K'l`z>YF;fY"C^S1?zvOE1LN`n*)jg'Zm_hbhx4H>FnIoXs ]IBFhF:>c JT?@X dNz22T]F}w[M7?^Fo>:[SlD/h!%p2D`f=m qZ=UO@6R{v9w xz_VP%\HD[/>K^ k$4B_)59-[]c*V5Trns}M4zeL.t7#ZnH;.sK4*QpeB*9h^"88o`Y$l5't-4nJH1~l 6|`Y@W )| p'c. .8A\qlNFlOs)i86>xR<8R>NL,IiNt[=On?;7FpmIU7dsAh:e{,*U3s"/?[vy2)qL}<:&{y"FJ<7nD q[rV^FelT 'ISS6o<;h*@ 'YZ=jg2.5N^eg!noE?Sjul*Kr^ahz6x% p"zC'!fi_zl#P2JB y~D,r!pP%I*mdRt"kt2x c&h09v#k>'2P2N^6y?S*e+W@k'ZBh/6 N*RT,q=9 :4 T6dGc0y=,G:V68}h1h/JJ2AG3Cz)X3)}X=0~)j9MII< 8e/bw cY"c&`abI~eR]V$ GcSAv9Y>O_/SYw[wUs~(H$LD>Ea 1m[>lSo@#q?i5L PWgUXnA AL}7 HGi,#? T U ttvS3p]6mh=l8z-U$V!\tqMb_(!{Z 7K(m0@'0?<7 *KZhmd?UE8r )Si{BoJppg5iI[[Bwwe*#Bf4KBc>xE,gn $k> Aag{sZ+$4BK) ":R8z) 0J50OBx ZaUBPa?4,OA zt:}Y k&zxA!hKAW n tQghJB Gxk98`H(H= +%qBZOzz[={B4ir_(9\Uhozig%Kg9b<W=Z`Rx$]&;2y^ a,3-}K";!EuzjRH8Z8?`8 w!1FO[k:CkICRyGB;@GSk! b@G%J o>wTp$?kp!+gNfHi(,AXHS0[>_6O+iC0i1[K"m6QqY2&gfTR*W+vzE}&L3n1lW\2<9lC_?3{ +EeudJLsH m2WOIp+e0" de r_!b& C;&##TM>7dH)Z7l}a$ Qz4 ;5l*vgD(G"yE.7;:a+a 9;C)b?l~m>! "/-DS;RL8)o.;k >c xW0qo$N10!f,Rh C<#u~cyJ(M  T& '`J_ jR#0DFV)Al{ Cwu^c&!#?rOf\d,D\b7S;&!n'YCZvyEW8i"5@~|hb XYTng>#PbW:LlEz8vRL2@<(P).1L+I;z/T  flVy 7'n,};`{vcc5;\Q4~*M7SkE\D<>t-p!d>Pw|]JTs FO]/tY }@p.0t+3YkQ',`]~A~^]_a ) pgptllP,Jm\Z|};s 5rzdm3 !.\tZWB -x(x<<`:y$#"i'^;/HjR~'8@1:Z.sOD;Nn [~tG#'1-8]xZ}DR%?S6DltyrtHT_caHz{NXy"s!_]~]{N{(pBUk_7e., ; G8J :fh&YIh Ux8[Rx]6$D>]gI>3 8zg,3G.vcXVbdWZ=RfQc#PhDcW?d\Q-)LR*(>?2DCEg<#B)i}{3%v7w!Z~w!eE~6|6 '#IuDsl;ooV,bNKwgP"\}4W`GfD mW& (]{EHC !"1N6~RjN0"C,hG`C7 =9S2u@|X L.fWFlHT7" O5aV 2x}2V?i}zMCpgFn&)k]%IX}&G\;XC1HO~gkO_#I_R:y' I*4Jo M/4'F$8* +o3imrqGs|~"N>G`AER=%U2;iJV  Qg@x9}hPNkKx0K9`'EI/4#5(*ZhLDI^Lu )(f(I2qKs~ '07X+EH8k;Yr2g\dQ!GSRV#"To(1ye`F.8hos$V y!p uM pI2gonb[${He 'ub7[ J9hko<^srgJY}jDdA Jf|/\-_A0{|pEx#jz&Wm{"hQ#X'Y'R= ZyI8hqeUB;e-_90p!/#}f!#UhY Tf8 3#7T2"j2Sf+_v' .b:qgEj$#VC9~' Kr3V9`r9s"QXH2[}9,r%-jkpq(w,?LN_^c^y:FGA7y+iPv1#+o#X&{yRz%p$'(:r}[Fz#! J4<M'Q<_JnA\f^9!G]jPfbe=BoI0)`u!vs/\N`.4Px#,9|M6['[An"A+tC=gM9e3XYWn"Vkv^w3,|p]fcrai 7F /L2tjE5RQ4i)FF+\m\BfOn4OuY7l{erQ! WG@E]v@?KdtXldIm!pSN+*mj\ ;;>Mn9T@)rnbM $2*-?rv7J~'al\ \m\ ,we7M%jT/ ;_-:g-H`eYVReaDXGY1{;iYMZJ~)Z]**M"9OaQyV}9Z7V; MB*:U Zf1]:S =lJVz`^9RxgNZvxk(T|llpK0>{VOtR)(MLOD;;(0@Es` -BPr[ "M/YmY=%g{B,`'? |)OQ2(l'Z^}`s#vtzS F-}Ili^d&4Tu@hR1q9Q0<} |Q*w/XHe=V+7U hvTauii wd]A 0="7LA}lJ!g8+e^1G!S-ZSK-mi"5. oBpt3 pEb$jx=M]R+e)_)gL5Czd{"rMEK^j8d=\[^W)-j&XdjNUTixWpQ([0OUQPl8"ds2Y?XCT)l:4{\gdXi/upBpF-kz %@Q{HYeBOMT_|!Y^t>''~ 0%(O9D G$1b:O,hMs p H30qbLO"0VO/jhPa"~d6'okw/X-tn8atQ[C3 dv#6nDpVr "Xa^q1":<8MRE^3N:LZ>!/a&ut7')QSmC1LMAmwQx``.pCD]NJkWdY.@)>!<sOlUxq#0:Z8 JU^Mc8 ]q 9_.d)|\ Zk;JvYU?at b4&dr*b(L56(O*F{:K2 3i>0M *Heky-1>S@pD09e~ZIH21,TWY]~e1? T RD B&pt@EyNUgWl >+ &FpmgoO~o (G#,6/[)P Qu*`6{`N(3h>ppc#O(umpP?@|TfEw^k^%/H" NYrsQ>XDpB@7@\R{,MtZc]#d/khgS|g\<[)"K wG7\YB6GqX5hvbM+B@#6TQ+a>}y{?~<^qqevX59U{H3-]lihC(ul7h`X4~ t`}vHsQ^74"$$xBa5"qOe/V..`M\nEC~YS{{&TDKS}3MC(>3Xi=^qHa;KnacJEee?  i4.>20e=\k|H8?S"UH/udOyy{bj/tS>O*\ZYhT<(pn/m__`Hin`(y`hb^`G !m.z;=rZc] 5Y]53=<BqE|[n/\hoBeQRVsKbMz&Z&SZ#v~Eg*}t' #/+kmleuJgT y.6j 9 )X<:%7L!F w]~b'BRd&~EPS[ ]8kguQ 2iufo_%r?k:|T/,J k;`sUx%2Z%(l")6Z G@SeiWU[:>Q?&dxXbRHn#d=Yfz1Iq_F?"X4GnRjw,3jrG9>rTRD*t Q]~#3 43e/K@vmj4,x, me~kWMg4)r>404I^~#U.s`gDmkxE5*}g_8Q ^;<"h Y$D /$C)+61DD5.x[kBA!>C;T)`i^c u@#SAGL !,3VZ?b";MWpQT?.X ^_o]*e,]f!r )Tcbdkh/?/X%7dbuR%Nlx3u$9?q}&A] tjeOYz G{^! Ob;Cn&2g#(i=UI|U!GCpUNkL:kXyt2-fL* M \=6_4,gDwl*+ g2~ 3$S)J_%[wfv1n(pzVb1t0F f )y:FbAH0H\ t;$F*5qnyP}' t=v0bik&c, AMQ$=ip{P"$rb8yWUF?u"^T,TGD?S(K*bD_r RE:%#^iQOYn8~%o[;&+ZT B _q-Y%)gtod*/",3dj-T8QPG3pRiAQ\wTU,!1&p867: l"Ea2,FdlAmpgLa;4i*1~]5JW5k-Z 3MwV@e- !|\P$kETE[-Vv=4{+wNz]L*9#6f>bO;l3HW @x%52JAm3gaQa%i ( I'~^"NN>P!DW:s1{UeeIg:RtzI\G20e BPa]g_Z -:K$AUON!s*bDQxJ^ wi'epsq ~EH|umZayU4]G~9cY kBy6wi*Gk ,Wjf$+ABCgs[Gk. >F Ez#vhh~f9(S\'iNp8 C_H i5? & {7 GFAjN~#D&&B`T7^1S{Mk'yoVP]>:+g0 myDxvPJvwtp JjPwb}>McBzK U$@gJy^JK^dBg7@&/Jj%!;X|i^K)&p}G# Rk/hx\ c2(z>2~Z"=&imjJywVM^"S[$K^5'\$h' h{WlBPN\[N:@pSik ?[)xlnm'YVN[ pV/u(~SSf;` B*5 92O?5ejp Y^'sLXv9'^~"M<^xd'wNub{75 rCVC+{yXGDmlzs;8K_;{pQZ=C@t sb.size] = '\0'; /* Terminate device spec */ if (iosb_flags.replaced) { L^$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY8"IS, Locate a watchpoint entry for disableD;A; Functional description: ;C6; 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 case 9; 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.; ; Inputs: ;;:; 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 addressc;;---LD_FIND_WATCH_ENTRY_DIS: PUSHL R3R MOVL R1,R0 ; Save for laterD!10$: MOVL (R1),R1 ; Next entry$ CMPL R1,R0 ; End of queue ?C BEQL 40$ ; Yes; EXTZV #LDWATCHPT_V_CHARS,- ; Extract characteristics bitsB #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 matchl1 BBS #LDWATCH_V_NOLBN,- ; Non-transfer function?, LDWATCH_W_FLAGS(R1),60$t* BBC #LDWATCH_V_FILE,- ; File watchpoint? LDWATCH_W_FLAGS(R1),20$e; CMPL LDWATCHPT_L_LBN(R2),- ; Check VBN for matching blocki LDWATCH_L_VBN(R1)s BRB 30$220$: CMPL LDWATCHPT_L_LBN(R2),- ; Matching block? LDWATCH_L_LBN(R1)30$: BEQL 50$ ; Yes' BGTRU 10$ ; Not a higher number yeth 40$: MOVZBL #1,R0 ; Not found BRB 80$050$: BBS #LDWATCHPT_V_REMOVE_ALL,- ; Remove all? LDWATCHPT_W_FLAGS(R2),70$i660$: CMPW LDWATCHPT_W_FUNC(R2),- ; Matching function? LDWATCH_W_FUNC(R1) BNEQ 10$ ; Noc1 CMPW LDWATCHPT_W_ACTION(R2),- ; Matching action?u LDWATCH_W_ACTION(R1) BNEQ 10$ ; No70$: CLRL R0 ; Exact match_'80$: MOVL (SP)+,R3 ; Restore registere RSB 3? .SBTTL LD_START_GET_WATCH, Start I/O get watch info processing;;+++:; LD_START_GET_WATCH, Start I/O get watch info processing;W; Functional description:r;a:; This is the completion of the get watchpoint processing.;I ; Inputs:R; 9; R3 - address of the IRP (I/O request packet)C9; R5 - address of the UCB (unit control block)_;G ; Outputs:;T ; None.B;0D; The routine must preserve all registers except R0-R2 and R4.;ALD_START_GET_WATCH: 0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet countS BNEQ 10$ ; Something to do BRW 50$/10$: BBS #LDIO_V_INQUIRE,- ; Return list size?  IRP$L_EXTEND(R3),40$3 MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smalla2 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 address 9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listheadk MOVL R0,R2 ; Remember startE%20$: MOVL (R0),R0 ; Get next entryV CMPL R0,R2 ; At the end? BEQL 40$ ; Yes) CLRL LDWATCHPT_L_SBK(R4) ; Not returnedA5 MOVL LDWATCH_L_LBN(R0),- ; Move logical blocknumber7 LDWATCHPT_L_LBN(R4) R- ASSUME LDWATCH_W_ACTION EQ LDWATCH_W_FLAGS+2L/ MOVL LDWATCH_W_FLAGS(R0),- ; Flags and actiona LDWATCHPT_W_FLAGS(R4)- ASSUME LDWATCH_W_RETCODE EQ LDWATCH_W_FUNC+25 MOVL LDWATCH_W_FUNC(R0),- ; Function and returncode  LDWATCHPT_W_FUNC(R4)5 BBC #LDWATCH_V_FILE,- ; Check for virtual file modea LDWATCH_W_FLAGS(R0),30$_0 ASSUME LDWATCH_W_FID_NUM+2 EQ LDWATCH_W_FID_SEQ) MOVL LDWATCH_W_FID_NUM(R0),- ; Save FID LDWATCHPT_W_FID_NUM(R4)i MOVW LDWATCH_W_FID_RVN(R0),-I LDWATCHPT_W_FID_RVN(R4)P5 MOVL LDWATCH_L_VBN(R0),- ; Move virtual blocknumber  LDWATCHPT_L_LBN(R4) /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 bytecounts 60$: REQCOM hH .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 processingr;M; Functional description: ;B<; This is the completion of the get suspend list processing.;a ; Inputs:9; 9; R3 - address of the IRP (I/O request packet)$9; R5 - address of the UCB (unit control block)D;# ; Outputs:;G ; None.o; D; The routine must preserve all registers except R0-R2 and R4.;LD_START_GET_SUSPEND_LIST:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 MOVL UCB$L_LD_WATCHCNT(R5),R1 ; Get packet countO BNEQ 10$ ; Something to do BRW 100$ ; Branch assist 10$: CLRL R1 ; No entries yet9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listheadi MOVL R0,R2 ; Remember start %20$: MOVL (R0),R0 ; Get next entryW CMPL R0,R2 ; At the end? BEQL 30$ ; Yes9 ADDL2 LDWATCH_L_SUSPCNT(R0),R1 ; Count number of entriesc BRB 20$/30$: BBS #LDIO_V_INQUIRE,- ; Return list size?e IRP$L_EXTEND(R3),90$3 MOVZWL #SS$_IVBUFLEN,R0 ; Assume buffer too smalls2 MULL3 R1,#LDSUSPLST_K_LENGTH,R2 ; Get size needed( CMPL IRP$L_BCNT(R3),R2 ; Enough space? BLSSU 110$ ; Noi5 MOVL IRP$L_SVAPTE(R3),R4 ; Get systembuffer addressn) MOVL (R4),R4 ; Get userbuffer addressf9 MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get watchqueue listheads MOVL R0,R2 ; Remember starts%40$: MOVL (R0),R0 ; Get next entryt CMPL R0,R2 ; At the end? BEQL 90$ ; Yes4 TSTL LDWATCH_L_SUSPCNT(R0) ; Get count of this lbn BEQL 40$ ; None here PUSHR #^M8 MOVAL LDWATCH_L_SUSPFL(R0),R1 ; Point to suspend queue MOVL R1,R2 ; Remember endp.50$: MOVL IRP$L_IOQFL(R1),R1 ; Get next entry CMPL R1,R2 ; At the end? BEQL 80$ ; Yes PUSHL R0t! MOVL IRP$L_PID(R1),R0 ; Get PID , JSB G^EXE$IPID_TO_EPID ; Make external PID( MOVL R0,LDSUSPLST_L_PID(R4) ; Move PID MOVL (SP)+,R05 BBS #LDWATCH_V_FILE,- ; Check for virtual file modeR LDWATCH_W_FLAGS(R0),60$O% MOVL LDWATCH_L_LBN(R0),- ; Move LBNO LDSUSPLST_L_LBN(R4)T BRB 70$)60$: MOVL LDWATCH_L_VBN(R0),- ; Move VBNT LDSUSPLST_L_LBN(R4) 1 ASSUME LDSUSPLST_W_ACTION EQ LDSUSPLST_W_FLAGS+2h870$: MOVL LDWATCH_W_FLAGS(R0),- ; Move flags and action LDSUSPLST_W_FLAGS(R4)o1 ASSUME LDSUSPLST_W_RETCODE EQ LDSUSPLST_W_FUNC+2e7 MOVL LDWATCH_W_FUNC(R0),- ; Move function and retcode LDSUSPLST_W_FUNC(R4)+ ADDL2 #LDSUSPLST_K_LENGTH,R4 ; Next entry  BRB 50$80$: POPR #^M BRB 40$90$: MOVZWL #SS$_NORMAL,R05100$: BBS #LDIO_V_INQUIRE,- ; Return list size only?- IRP$L_EXTEND(R3),110$ . MULL3 #LDSUSPLST_K_LENGTH,R1,R2 ; Total count2 MOVL R2,IRP$L_BCNT(R3) ; Save for postprocessing' INSV R2,#16,#16,R0 ; Merge bytecountf 110$: REQCOM @ .SBTTL LD_START_RESUME_WATCH, Start I/O resume watch processing;+++;; LD_START_RESUME_WATCH, Start I/O resume watch processingu;l; Functional description:R;1=; This is the completion of the resume watchpoint processing. G; Any previously suspended thread which is specified will be restarted.;B ; Inputs:1;;9; R3 - address of the IRP (I/O request packet)E9; R5 - address of the UCB (unit control block)T/; IRP$L_OBCNT - number of entries in userbuffer,;s ; Outputs:;a ; None.s;TD; The routine must preserve all registers except R0-R2 and R4.;TLD_START_RESUME_WATCH:0 MOVZWL #SS$_DATALOST,R0 ; Assume nothing to do1 TSTL UCB$L_LD_WATCHCNT(R5) ; Check packet counti BEQL 70$ ; Nothing to do# PUSHL R6 ; May not be destroyed-! CLRL R6 ; Nothing resumed yetc5 MOVL IRP$L_SVAPTE(R3),R2 ; Get systembuffer addressk BEQL 10$ ; Nothing there) MOVL (R2),R2 ; Get userbuffer addressC010$: MOVL IRP$L_OBCNT(R3),R4 ; Get wanted count3 MOVAL UCB$L_LD_WATCHQFL(R5),R1 ; Point to listheadn& 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$ ; Nou5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modee LDWATCH_W_FLAGS(R1),25$ ' CMPL LDWATCH_L_LBN(R1),- ; Right lbn?N LDWATCHPT_L_LBN(R2)D BNEQ 20$ ; No  BRB 30$+25$: CMPL LDWATCH_L_VBN(R1),- ; Right vbn?r LDWATCHPT_L_LBN(R2)  BNEQ 20$ ; Noy130$: CMPW LDWATCH_W_ACTION(R1),- ; Right action?E #WATCH_ACTION_SUSPENDs9 $LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYu4"  BNEQ 20$ ; NoP5 BSBW LD_DRAIN_WATCH_THREAD ; Get rid of watchpoints " INCL R6 ; We resumed something BRB 20$ ; Next packetF"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 successn TSTL R6 ; Done something?  BNEQ 60$ ; Yes) MOVZWL #SS$_DATACHECK,R0 ; Nothing doneu'60$: MOVL (SP)+,R6 ; Recover this one 70$: CLRL R1 REQCOM  O: .SBTTL LD_DRAIN_WATCH_THREAD, drain suspended watch queue;+++5; LD_DRAIN_WATCH_THREAD, drain suspended watch queue6;a; Functional description:c;r6; This routine resumes all pending watchpoint threads.;8 ; Inputs:o;h*; R1 - address of LDWATCH block9; R5 - address of the UCB (unit control block) ;- ; Outputs:;d; None. ;$;---LD_DRAIN_WATCH_THREAD:= TSTL LDWATCH_L_SUSPCNT(R1) ; Get count of suspended threadsn BEQL 40$ ; Nothing$ PUSHR #^M ; Save registers310$: REMQUE @LDWATCH_L_SUSPFL(R1),R3 ; Remove entry+ BBSS #UCB$V_LD_FKBBSY,- ; Forkblock busy?0 UCB$B_LD_FLAGS(R5),20$. MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5)N/ MOVL IRP$L_FPC(R3),- ; Restore Fork trace PC  UCB$L_FPC(R5)o) JSB G^EXE$QUEUE_FORK ; Queue forkthreadT BRB 30$I20$: INSQUE (R3),@UCB$L_LD_WATCHPNDQBL(R5) ; Insert in watchpending queueg/30$: DECL LDWATCH_L_SUSPCNT(R1) ; Count packetr BNEQ 10$ ; More to dor POPR #^M40$: RSB SF .SBTTL LD_RESUME_WATCH_THREAD, resume eventual suspended watch thread;+++A; LD_RESUME_WATCH_THREAD, resume eventual suspended watch threadf;; Functional description:0;M5; This routine resumes one pending watchpoint thread.^;0 ; Inputs:r;r9; R3 - address of the IRP (I/O request packet)L9; R5 - address of the UCB (unit control block)a; ; Outputs:;E; None.F; ;---LD_RESUME_WATCH_THREAD:u PUSHR #^MnE REMQUE @UCB$L_LD_WATCHPNDQFL(R5),R3 ; Remove from watchpending queueo BVS 10$ ; Nothing therea. MOVQ IRP$L_FR3(R3),- ; Get IRP and function UCB$L_FR3(R5)/ MOVL IRP$L_FPC(R3),- ; Restore Fork trace PCt UCB$L_FPC(R5),) JSB G^EXE$QUEUE_FORK ; Queue forkthreadm BRB 20$:10$: BICB2 #UCB$M_LD_FKBBSY,- ; Reset Forkblock busy flag UCB$B_LD_FLAGS(R5)20$: POPR #^M RSB t. .SBTTL LD_CHECK_WATCH, Check for a watchpoint;+++); LD_CHECK_WATCH, Check for a watchpointv;n; 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.;o; We currently have 4 options:;--; 1. WATCH_ACTION_SUSPEND: Suspend a thread+; 2. WATCH_ACTION_CRASH: Crash the systemi*; 3. WATCH_ACTION_ERROR: Return an error2; 4. WATCH_ACTION_OPCOM: Send a message to OPCOM;VH; #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.n;o<; #2 will crash the system if we find a matching watchpoint.;H?; #3 will return a user-specified error for the current thread.;RI; #4 will send an errormessage about the process accessing the watchpoint; ; to OPCOMF;P ; Inputs:;;v9; R3 - address of the IRP (I/O request packet)C9; R5 - address of the UCB (unit control block)V;S ; Outputs:;o ; R0 - statusS7; IRP$L_EXTEND - if 0 no action done, continue as usualh+; - if 1, R0 contains the error to returnI;LA; If a thread is suspended we return to our caller's callers1; without R0 set as that's not needed then.;;t>; The routine must preserve all registers except R0-R2..;E;---LD_CHECK_WATCH:2( CLRL IRP$L_EXTEND(R3) ; Clear our flag2 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint set? BNEQ 10$ ; Yes5$: BRW 120$ ; Branch assist 810$: 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 ?0 BEQL 5$ ; YesK/ CMPW IRP$W_FUNC(R3),- ; Match of functioncodeR, LDWATCH_W_FUNC(R1) ; including modifiers? BNEQ 20$ ; NoR> EXTZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Extract the function code IRP$W_FUNC(R3),R2() CMPL R2,#IO$_READPBLK ; Is this a read?I BEQL 30$ ; Yes, check lbnC& CMPL R2,#IO$_WRITEPBLK ; or a write? BEQL 30$ ; Yes, check lbnC' CMPL R2,#IO$_WRITECHECK ; Writecheck?C BEQL 30$ ; Yes, check lbn  CMPL R2,#IO$_DSE ; Erase? BNEQ 50$ ; Now.30$: CMPL IRP$L_MEDIA(R3),- ; Matching block? LDWATCH_L_LBN(R1) - BGTRU 20$ ; No, requested block above wpto6 ASHL #-9,IRP$L_BCNT(R3),R2 ; Convert bytes to blocks5 BITL #^X1FF,IRP$L_BCNT(R3) ; Multiple of 512 bytes?d BNEQ 40$ ; No  DECL R2 ; Adjust540$: ADDL2 IRP$L_MEDIA(R3),R2 ; Calculate last blockk- CMPL R2,LDWATCH_L_LBN(R1) ; Matching block?o BLSSU 20$ ; No;LJ; We've got a match. Now check what to do with it. ; M50$: DISPATCH LDWATCH_W_ACTION(R1),TYPE=W,<- ; Dispatch according to function, ,-h ,-  ,-_ >7 BUG_CHECK INCONSTATE,FATAL ; We should never come hereV860$: MOVZWL LDWATCH_W_RETCODE(R1),R0 ; Pick up errorcode BRB 110$:D70$: 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 callerc( 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 packets!100$: ADDL2 #4,SP ; Restore SPt1110$: INCL IRP$L_EXTEND(R3) ; Flag action neededt 120$: RSB  I .SBTTL LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOMd;+++D; LD_SEND_OPCOM, Send a message about a touched watchpoint to OPCOM;e; Functional description:-; @; This routine allocates and fills a buffer with watchpoint info<; and queues this buffer to LD_OPCOM_AST as an exec-mode ASTA; routine in the context of the issueing process. The AST routine0E; will then invoke the system-service $SNDOPR to process the request.rH; 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 aI@; usermode AST because we then need to modify the pageprotectionG; of the ACB to allow user-read access. If there is no issueing process:D; (a mount-verification thread for example) then we queue the AST to ; the OPCOM.;o ; Inputs: ; +; R1 - LDWATCH structure address9; R3 - address of the IRP (I/O request packet)f9; R5 - address of the UCB (unit control block)t;l ; Outputs:; ; R0 - Statuss;@; The routine must preserve all registers except R0 and R2; ;---LD_SEND_OPCOM: PUSHR #^Mn- MOVL IRP$L_PID(R3),R4 ; I/O from a process? 1 BGTR 20$ ; Yes (no mount-verification thread)d;sU; This thread has no PID but a system-space address (mount-verification for example).lN; 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-modePP; 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.o;c1 BSBW LD_FIND_OPCOM ; Check if OPCOM is running BLBS R0,20$ ; YesfA MOVZWL #SS$_NORMAL,R0 ; No need to bother this thread, dismissG10$: 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 sized MOVL (SP),R1 % MOVB #DYN$C_ACB,- ; Used as an ACBn LDSNDOPRLST_B_TYPE(R2)5 CLRL LDSNDOPRLST_L_KAST(R2) ; Assume normal processu0 CMPL R4,IRP$L_PID(R3) ; Just a normal process? BEQL 30$ ; Yes/ INCL LDSNDOPRLST_L_KAST(R2) ; Special processr>30$: MOVL R4,LDSNDOPRLST_L_PID(R2) ; Issueing process or OPCOMA MOVB #,- ; Exec mode, astroutine will)3 LDSNDOPRLST_B_RMOD(R2) ; deallocate this buffers, MOVAB LD_OPCOM_AST,- ; Routine to execute LDSNDOPRLST_L_AST(R2) @ MOVL R2I%$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY,LDSNDOPRLST_L_ASTPRM(R2) ; Setup our block as parameter5 BBS #LDWATCH_V_FILE,- ; Check for virtual file modeD LDWATCH_W_FLAGS(R1),40$B% 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)050$:5 ASSUME LDSNDOPRLST_W_ACTION EQ LDSNDOPRLST_W_FLAGS+24 MOVL LDWATCH_W_FLAGS(R1),- ; Move flags and action LDSNDOPRLST_W_FLAGS(R2)W5 ASSUME LDSNDOPRLST_W_RETCODE EQ LDSNDOPRLST_W_FUNC+2P7 MOVL LDWATCH_W_FUNC(R1),- ; Move function and retcoder 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)  MOVW LDWATCH_W_FID_RVN(R1),-s LDSNDOPRLST_W_FID_RVN(R2)R PUSHR #^M/ MOVZWL #LDSNDOPRLST_K_DEVNAM-1,R0 ; Buffersize-< MOVAB LDSNDOPRLST_T_DEVNAM+1(R2),R1 ; Buffer for devicename% MOVL #-2,R4 ; DVI$_DISPLAY_DEVNAM48 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 AST2 POPR #^MU BLBS R0,60$ ; All ok;LJ; 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._;$ 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 08 .SBTTL LD_FIND_OPCOM, Find the pid of the OPCOM process;+++3; LD_FIND_OPCOM, Find the pid of the OPCOM processo;e; Functional description: ;t=; This routine locates the internal pid of the OPCOM process.C;C ; Inputs:n;a ; None; ; Outputs:;C ; R0 - Statusa; R4 - Internal pid of opcom;p@; The routine must preserve all registers except R0 and R4; ;---LD_FIND_OPCOM: 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?5 BEQL 20$ ; Yes, get nextM+ CMPL PCB$L_UIC(R2),- ; UIC match ([1,4])?_ #^X00010004L 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 OPCOMS OPCOM_NAME POPR #^M; BNEQ 20$ ; No match) MOVL PCB$L_PID(R2),R4 ; Get internal pidh" MOVZWL #SS$_NORMAL,R0 ; Success! BRB 30$/20$: AOBLEQ G^SCH$GL_MAXPIX,R1,10$ ; Next entryi$ 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;nOPCOM_NAME: .ASCIC /OPCOM/ e? .SBTTL LD_RETURN_QUOTA, Return quota of charged buffer to user ;+++:; LD_RETURN_QUOTA, Return quota of charged buffer to user; ; Functional description:s;;; This routine returns the quota of a buffer we charged fore;; back to a user. Since we may be in system context we willb/; queue an ast to the user to return the quota.r;L ; Inputs:B;?; R1 - number of bytes to return to BYTCNT and BYTLM; R4 - pid of process to credit ;t ; Outputs:;e ; R0 - statusR;1D; The routine must preserve all registers except R0-R2 and R4.;0;---LD_RETURN_QUOTA: PUSHR #^MR BSBW LD_GETLDIOB ; Get ACBh" 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 _ 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 ASTB*10$: POPR #^M ; Restore registers RSB M1 .SBTTL LD_QUOTA_AST, Return quota action routinea;+++,; LD_QUOTA_AST, Return quota action routine;_; Functional description:D;C@; This routine is called as an AST routine in the context of the?; process issueing the I/O. We will return the previously taken ; bytecount quota to the user.;L ; Inputs:D;(1; 'R4 - address of the PCB (process control block)W$; R5 - address of the ACB.; ACB$L_ASTPRM(R5) - Number of bytes to return;$=; The routine must preserve all registers except R0-R5.T;U LD_QUOTA_AST:t4 MOVL ACB$L_ASTPRM(R5),R3 ; Get bytecount to return MOVL R5,R0 ; Address of ACB( JSB G^EXE$DEANONPAGED ; Get rid of ACB MOVL R3,R0 - JSB G^EXE$CREDIT_BYTCNT_BYTLM ; Return quota  MOVZWL #SS$_NORMAL,R0 RSB e+ .SBTTL LD_COMPLETE, I/O completion routineC;+++&; LD_COMPLETE, I/O Completion routine.;U?; 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.e;M ; Inputs:);T ; R5 - IRP!; R1 - Address of LD_COMPLETE rtn0; IPL - IPL$_IOPOSTd;B ; This routine may use R0 to R5.;r;--- LD_COMPLETE: MOVL R5,R3_0 MOVL IRP$L_IOSB(R3),R5 ; Get saved UCB address- MOVL R5,IRP$L_UCB(R3) ; Restore UCB addressi5 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Raise to Fork leveli PRESERVE=NO8 MOVAL UCB$L_LD_AIOFL(R5),R0 ; Get address Act. I/O F.L MOVL R0,R2 ; Copy it to R2310$: MOVL LDIOB_L_QFL(R2),R1 ; Get the first entry+ CMPL R1,R0 ; Back at start ? BNEQ 20$ ; No, continued: BUG_CHECK INCONSTATE,FATAL ; Inconsistent Active I/O list720$: CMPL LDIOB_L_IRP(R1),R3 ; Is this correct LDIOB ?R BEQL 30$ ; Yes, finish upc$ MOVL R1,R2 ; Copy for next block BRB 10$ ; Loop untill found<30$: 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. DECW UCB$W_QLEN(R4) ; Decrease queue length0 FORKUNLOCK LOCK=UCB$B_FLCK(R4),- ; Release lock NEWIPL=(SP)+,-  PRESERVE=NO,- CONDITION=RESTORE6 REMQUE @LDIOB_L_QFL(R2),R4 ; Remove LDIOB from queue< MOVL LDIOB_L_IOSB(R4),IRP$L_IOSB(R3) ; Restore IOSB address5 MOVL LDIOB_L_PID(R4),IRP$L_PID(R3) ; Copy PID acrossL' PUSHAB 50$ ; Return in case of forkB* BSBW LD_SAVE_TRACE_ALT ; Save trace dataH $INSQTI IRP$L_IOQFL(R3),G^IOC$GQ_POSTIQ ; Insert IRP back in post queue5 SOFTINT #IPL$_IOPOST ; Initiate software interrupt . BSBW POST_PACKACK ; Post packack processing4 INCL UCB$L_OPCNT(R5) ; Count I/O (Logical device) .IF DF MDDRIVER_WORKAROUNDB;RM; Work around MDdriver bug. If an erase function is given to DECRAM, then thefJ; iosb does not contain the bytecount but zero. In a certain configurationK; (DSA1: -> LDA1: -> MDA1:) this will lead to a loop since the XQP attemptstL; to erase a number of blocks, and it looks in the returned bytecount to see; how much is left to do. ;R4 BBC #UCB$V_LD_DECRAM,- ; Connected to DECRAM disk? UCB$B_LD_FLAGS(R5),35$< CMPZV #IRP$V_FCODE,#IRP$S_FCODE,- ; Check the function code# IRP$W_FUNC(R3),#IO$_DSE ; Erase?  BNEQ 35$ ; No @ MOVL IRP$L_BCNT(R3),IRP$L_IOST1+2(R3); Return correct bytecount35$: .ENDC .IF DF DUDRIVER_WORKAROUND;uM; Hack around DUdriver bug. The DUdriver may stall an I/O request by queueingaM; an IRP to the physical device UCB when UCB$L_RWAITCNT is not zero. This mayoN; 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,cC; which causes everything to wait for this packet. Triggering mount J; verification for the device will solve it, because in mv's return path aH; check is made for a non empty i/o queue. We work around it by checkingF; for stalled I/O's in our return path. This is not guaranteed to workM; 100% of the time, but since i made this modification i could not get thingsa; to go wrong.;E PUSHL R5 ; Save logical UCB8 MOVL UCB$L_LD_PDUCB(R5),R5 ; Get the physical disk UCBB BBS #DEV$V_SCSI,UCB$L_DEVCHAR2(R5),40$ ; 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's0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release dl>$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UYLG"/lock NEWIPL=(SP)+,-D PRESERVE=NO,- CONDITION=RESTORE40$: MOVL (SP)+,R5 .ENDC;c RSB ; Return to IOPOST rtn_;F@; 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.;OJ50$: BSBW LD_RESUME_WATCH_THREAD ; Resume eventual suspended watch thread0 FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=#IPL$_IOPOST,- PRESERVE=NO,- CONDITION=RESTORE RSB ; Either ;e POST_PACKACK:d# EXTZV #IRP$V_FCODE,#IRP$S_FCODE, -.0 IRP$W_FUNC(R3),R1 ; Extract the function code" CMPW R1,#IO$_PACKACK ; Packack? BNEQ 10$ ; No 4 BBC #UCB$V_LD_REPLACE,- ; Check if replaced device UCB$B_LD_FLAGS(R5),10$; A; Setup device characteristics in case they were not available ato; drive connect time. ;a8 MOVL UCB$L_LD_PDUCB(R5),R4 ; Get the physical disk UCB- MOVB UCB$B_DEVTYPE(R4),- ; Copy device typee 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, tracks $ UCB$B_SECTORS(R5) ; and cylinders0 MOVL UCB$L_MAXBLOCK(R4),- ; Copy maximum block UCB$L_MAXBLOCK(R5)10$: RSB K- .SBTTL LD_CANCEL, Generic Cancel I/O routineA;+++(; LD_CANCEL, Generic Cancel I/O routine;-; Functional description:_;N<; 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.;( ; Inputs:o;e; R2 - Channel index number;; R3 - IRP from UCB$L_IRPD ; R4 - PCB ; R5 - UCB#; R8 - Cancel reason code, one of : *; CAN$C_CANCEL If called through $CANCEL; system service.D*; CAN$C_DASSGN If called through $DASSGN ; or $DALLOC system service.;o ; Outputs:; 5; The routine must preserve all registers exept R0-R3 ;f;--- LD_CANCEL:2 CMPL R8,#CAN$C_DASSGN ; Called through $DASSGN ?! BEQL 10$ ; Yes, no active I/OR* BSBW LD_CANCEL_IO ; Cancel this request910$: BSBW LD_CANCEL_TRACE ; Cancel pending trace threadH5 BSBW LD_CANCEL_WATCH ; Cancel suspended watchpoint 3 BBC #UCB$V_DELETEUCB,- ; Only if we're going awayu UCB$L_STS(R5),20$oA 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 amountd UCB$W_CHARGE(R5)20$: RSB ; Returns i( .SBTTL LD_CANCEL_IO, Cancel I/O routine;+++3; LD_CANCEL_IO, Cancels I/O operations in progress ;k; Functional description:h; <; This routine cancels all outstanding I/O for the specified;; channel and PID. We search the active IO list for LDIOB's <; 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 physicall9; disk it's cancel IO routine is called, with the correctI ; registers.;_ ; Inputs:_;2; R2 - Channel index numberO; R3 - IRP from UCB$L_IRPC ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codee;d ; Outputs:;.5; The routine must preserve all registers exept R0-R3h5; The routine may set the UCB CANCEL bit in UCB$W_STS;/;--- LD_CANCEL_IO:i8 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 doneF PUSHL R0 ; Save R0A CMPL LDIOB_L_PID(R1),IRP$L_PID(R3) ; Is this the right process ?( BNEQ 20$ ; Not+ MOVL LDIOB_L_IRP(R1),R0 ; Get IRP address06 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 list 30$: RSB:40$: 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 listM MOVL R3,R1 ; Copy it into R1<60$: MOVL IRP$L_IOQFL(R1),R1 ; Get next packet in the queue CMPL R1,R3 ; End of queue ? ' BEQL 80$ ; Not found, call CANCELIO)" 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 ?I6 BICW2 #IRP$M_FUNC,IRP$W_STS(R3) ; Clear buffered read;70$: MOVZWL #SS$_CANCEL,IRP$L_IOST1(R3) ; Set return statusoC $INSQTI IRP$L_IOQFL(R3),G^IOC$GQ_POSTIQ ; Insert IRP in post queueU5 SOFTINT #IPL$_IOPOST ; Initiate software interruptd BRB 90$080$: MOVL UCB$L_DDT(R5),R0 ; Get address of DDT7 MOVL UCB$L_IRP(R5),R3 ; Get current IO packet addresss, JSB @DDT$L_CANCEL(R0) ; Call cancel IO rtn490$: FORKUNLOCK LOCK=UCB$B_FLCK(R5),- ; Release lock NEWIPL=(SP)+,-U PRESERVE=NO,- CONDITION=RESTORE1 POPR #^M ; Restore saved registersw BRW 20$ ; Continue searchD .1 .SBTTL LD_CANCEL_TRACE, Cancel trace I/O routine ;+++,; LD_CANCEL_TRACE, Cancel trace I/O routine; ; Functional description:); C; This routine checks if there's an IRP in the trace wait queue andpF; 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.E;= ; Inputs:R; ; R2 - Channel index numberS; R3 - IRP from UCB$L_IRP ; R4 - PCB ; R5 - UCB; R8 - Cancel reason code$;L ; Outputs:; 5; The routine must preserve all registers exept R0-R3$;C;---LD_CANCEL_TRACE:B MOVAL UCB$L_LD_TRCWAITQFL(R5),R0 ; Get addr. of waiting trace irp MOVL R0,R1 ; Copy it to R1,10$: MOVL IRP$L_IOQFL(R1),R1 ; Get an entry CMPL R1,R0 ; Back at start ?# BEQL 20$ ; Yes, whole list done,? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?L BNEQ 10$ ; Nol6 CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 10$ ; NoB( CLRL IRP$L_SVAPTE(R1) ; No more SVAPTE' REMQUE IRP$L_IOQFL(R1),R1 ; Remove it.7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statusC $INSQTI IRP$L_IOQFL(R1),G^IOC$GQ_POSTIQ ; Insert IRP in post queueO5 SOFTINT #IPL$_IOPOST ; Initiate software interrupt 20$: RSB )1 .SBTTL LD_CANCEL_WATCH, Cancel watch I/O routine ;+++,; LD_CANCEL_WATCH, Cancel watch I/O routine;(; Functional description:R; ;; This routine will check if there's a suspended watchpointM:; 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 (itV9; comes from UCB$L_IRP), so we check against the PID from ; the PCB.;r ; Inputs:$;D; R2 - Channel index numbert; R3 - IRP from UCB$L_IRP+ ; R4 - PCB ; R5 - UCB; R8 - Cancel reason codeR; ; Outputs:;r5; The routine must preserve all registers exept R0-R3r;s;---LD_CANCEL_WATCH:5 TSTL UCB$L_LD_WATCHCNT(R5) ; Any watchpoint active?  BEQL 40$ ; Non? MOVAL UCB$L_LD_WATCHQFL(R5),R0 ; Get addr. of watchpoint entry  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 donex9 TSTL LDWATCH_L_SUSPCNT(R1) ; Anything in suspend queue?D BEQL 10$ ; NoS PUSHR #^MR; MOVAL LDWATCH_L_SUSPFL(R1),R0 ; Point to suspend listheadR MOVL R0,R1 ; Save for laterN/20$: MOVL IRP$L_IOQFL(R1),R1 ; Get waiting irpL CMPL R1,R0 ; Back at start?- BEQL 30$ ; Yes? CMPL IRP$L_PID(R1),PCB$L_PID(R4) ; Is this the right process ?C BNEQ 20$ ; NoU6 CMPW IRP$W_CHAN(R1),R2 ; Is this the right channel ? BNEQ 20$ ; NoT' REMQUE IRP$L_IOQFL(R1),R1 ; Remove itN7 MOVZWL #SS$_CANCEL,IRP$L_IOST1(R1) ; Set return statuscC $INSQTI IRP$L_IOQFL(R1),G^IOC$GQ_POSTIQ ; Insert IRP in post queuet5 SOFTINT #IPL$_IOPOST ; Initiate software interrupt  POPR #^M7 DECL LDWATCH_L_SUSPCNT(R1) ; Count the one we removedi BRW 10$/30$: POPR #^M ; Restore, continue scant BRW 10$40$: RSB f, .SBTTL LD_GETLDIOB, Allocate I/O data block;+++&; LD^U6$LD051.B_Pv]#[VDBURG.LD.FWARE.SRC]LDDRIVER.MAR;1UY"@_GETLDIOB, Allocate I/O data block;3; Functional description:(; G; This routine allocates and initializes an I/O datablock which is usedk0; for I/O requests as well as trace information.;d ; Inputs:t; ; None.s;u ; Output:; ; R0 - Statuso; R2 - LDIOB address ; A; The routine must preserve all registers except R0 and R2.-;a;---; LD_GETLDIOB: PUSHL R1 ; Save this guy; MOVZWL #LDIOB_K_LENGTH,R1 ; Set length of block to alloc._' JSB G^EXE$ALONONPAGED ; Allocate poolb BLBC R0,10$ ; Exit on errori- MOVW R1,LDIOB_W_SIZE(R2) ; Setup size field 6 MOVB #DYN$C_BUFIO,LDIOB_B_TYPE(R2) ; Setup type field. CLRB LDIOB_B_RSVD(R2) ; Clear reserved field!10$: MOVL (SP)+,R1 ; Restore R1  RSB * .SBTTL LD_SAVE_TRACE, Save trace I/O dataC .SBTTL LD_SAVE_TRACEALT, Save trace I/O data, alternate entrypointi;+++%; LD_SAVE_TRACE - Save trace I/O dataR?; LD_SAVE_TRACE_ALT - Save trace I/O data, alternate entrypointe;h; Functional description: ;$I; This routine is called just before going to REQCOM to save the eventualc ; trace data.s;o; Inputs for LD_SAVE_TRACE:B;); R0/R1 - Return statusM9; R3 - address of the IRP (I/O request packet)(9; R5 - address of the UCB (unit control block) ;FB; This routine must preserve all registers except R2 and R4.;C; Inputs for LD_SAVE_TRACE_ALT:I;V9; R3 - address of the IRP (I/O request packet) "; R4 - address of LDIOB9; R5 - address of the UCB (unit control block);nE; This routine must preserve all registers except R0-R2 and R4.;h;---LD_SAVE_TRACE:% MOVL R3,UCB$L_IRP(R5) ; Restore IRP, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BNEQ 5$ ; Got oneS BRW 30$ 5$: PUSHL R5 MOVQ R0,R4 ; Save status+ BSBW LD_GETLDIOB ; Allocate pool to holdR ; temporary tracedata0 BLBC R0,20$ ; Exit on error ! MOVQ R4,LDIOB_Q_STAT(R2) ; IOSB; MOVL R2,R4M MOVL (SP)+,R5 MOVL IRP$L_MEDIA(R3),-. LDIOB_L_MEDIA(R4) ; LBN MOVL IRP$L_BCNT(R3),- LDIOB_L_BCNT(R4) ; Bytecounts MOVW IRP$W_FUNC(R3),-" LDIOB_W_FUNC(R4) ; Functioncode' CLRL LDIOB_L_PID(R4) ; Assume no pid7 BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP?R< 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 time;" ; for synchronous functions) BRW LD_TRACE+20$: MOVL (SP)+,R530$: RSBLD_SAVE_TRACE_ALT:% MOVL R3,UCB$L_IRP(R5) ; Restore IRPR, TSTL UCB$L_LD_TRCBUF(R5) ; Start of buffer BEQL 20$ ; Not there, quit;R@; See if the 'start_time' is filled-in. If not it means that theD; tracebuffer was allocated when an I/O request has been started but/; not completed yet. Forget this entry if true.e;c5 MOVQ LDIOB_Q_ST_TIME(R4),R0 ; Test for a start time 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 pid 7 BBS #IRP$V_MVIRP,IRP$W_STS(R3),10$ ; Mount verify IRP?L< 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 BRW LD_TRACE #20$: MOVL R4,R0 ; Point to LDIOBa+ JSB G^EXE$DEANONPAGED ; Get rid of bufferR RSB $ .SBTTL LD_TRACE, Trace I/O IRP data;+++; LD_TRACE - Trace I/O IRP data(;,; Functional description:C;_D; This routine is called by the start I/O routine to log information); about the I/O request in a tracebuffer.R;1H; 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 synchronizeSJ; 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 therJ; 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 fromLL; 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.A;- ; Inputs: ;e9; R3 - address of the IRP (I/O request packet)_"; R4 - address of LDIOB9; R5 - address of the UCB (unit control block)A;R:; The LDIOB in R4 will be deallocated when we're finished.;>; The routine must preserve all registers except R0-R2 and R4.;--- LD_TRACE:3 MOVL (SP)+,UCB$L_LD_TRCPC(R5) ; Save Fork trace PC020$: LOCK_TRACE ACCESS=WRITE,- ; Lock the mutex CONTEXT=SYSTEM BLBS R0,40$ ; Got it3 MOVAB IRP$L_FQFL(R3),R3 ; Point to FKB within IRP . MOVAB 30$,FKB$L_FPC(R3) ; Save return addres7 MOVB #DYN$C_FRK,FKB$B_TYPE(R3) ; Insert structure typeN% MOVL R4,FKB$L_FR4(R3) ; Preserve R49 MOVB UCB$B_FLCK(R5),FKB$B_FLCK(R3) ; Set proper spinlock 9 FORKLOCK LOCK=UCB$B_FLCK(R5),- ; Make sure synch is okayt SAVIPL=-(SP),-e PRESERVE=NO: INSQUE (R3),@UCB$L_LD_TRCMUTEXQBL(R5) ; Save IRP in queue! FORKUNLOCK LOCK=UCB$B_FLCK(R5),-d NEWIPL=(SP)+,-/ PRESERVE=NO,- CONDITION=RESTORE$ RSB ; Return to caller's caller;N>; Fork thread, called with R5 = forkblock (is our FKB in IRP),#; R3 and R4 restored from forkblock ; 330$: SUBL3 #IRP$L_FQFL,R5,R3 ; Restore IRP pointer% MOVL IRP$L_UCB(R3),R5 ; Restore UCBl BRB 20$ ; And try againR640$: PUSHL UCB$L_LD_TRCPC(R5) ; Restore Fork trace PC PUSHL R4 ; Save R4/ MOVL UCB$L_LD_TRCBUF(R5),R4 ; Start of bufferO5 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 bufferT 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 dataA- MOVL R4,R1 ; Reset ptr to start of bufferW560$: MOVL #LDTRCENT_K_LENGTH,R0 ; Length of LDTRCENT$) MOVL (SP),R4 ; Source info from LDIOBe0 MOVAB LDIOB_L_PID(R4),R4 ; Point to trace data70$: PUSHL R3 ; Save IRP5 PUSHL R5 ; Save UCB 4 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 availablee/ MOVQ LDIOB_Q_STAT(R4),-(SP) ; Save I/O statuse MOVL R4,R0 ; Point to LDIOBO+ JSB G^EXE$DEANONPAGED ; Get rid of buffer;! MOVQ (SP)+,R0 ; Restore statusS RSB _< .SBTTL LD_OPCOM_AST, AST routine to send a message to OPCOM;+++7; LD_OPCOM_AST, AST routine to send a message to OPCOMh;C; Functional description:(; H; We will run here in exec mode. Sine this part of the driver is locatedF; in non-paged-pool and the pageprotection is ERKW we may not write toB; any local variables. Hence we do all the work on the exec-stack.I; We are called in in the context of the process issueing the I/O requestaJ; 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.; 1; Layout of the stack we use for scratch storage:_;C#; 0(SP) - FAO control string length2$; 4(SP) - FAO control string address; 8(SP) - FAO return length#; 12(SP) - SNDOPR buffer lengtht ; 16(SP) - SNDOPR buffer address#; 20(SP) - FAO output buffer length$$; 24(SP) - FAO output buffer address; 28(SP) - PID; 32(SP) - Imagename; 36(SP) - Devicename ; 40(SP) - 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;B ; Inputs:R; J; 4(AP) - address of ACB which contains our parameters (LDSNDOPRLST);v6; The routine must preserve all registers except R0-R1;---$ .ENTRY LD_OPCOM_AST,^M;i* 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$w) MOVL #FAOLEN2,(SP) ; FAO string lengthn4 MOVAB FAOSTR2,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 volumeI BRB 20$-10$: MOVL #FAOLEN1,(SP) ; FAO string lengtht4 MOVAB FAOSTR1,4(SP) ; FAO conversion format (LBN)920$: MOVAB 60(SP),16(SP) ; SNDOPR messagebuffer addresso0 MOVZWL LDSNDOPRLST_W_FUNC(R2),40(SP) ; Function9 MOVL LDSNDOPRLST_L_LBN(R2),44(SP) ; Logical block numbera4 MOVAB LDSNDOPRLST_T_DEVNAM(R2),36(SP) ; Device name- MOVAB NONESTR,32(SP) ; Assume no imagenamel& 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 listn# CMPL (R0),R0 ; Something there?v BEQL 10$ ; Noo* MOVL (R0),R0 ; Get adress of first ICB, MOVAB 20(R0),32(SP) ; Point to image name.30$: MOVL #>,- 60(SP) ; OPCOM flagsy CLRL 64(SP) ; Terminator2 MOVAB 68(SP),24(SP) ; FAO output buffer address0 MOVZWL #256,20(SP) ; FAO output buffer length" PUSHAL 28(SP) ; Parameter list! PUSHAQ 24(SP) ; Output buffere% PUSHAW 16(SP) ; FAO return lengths1 PUSHAQ 12(SP) ; FAO control string descriptor(* CALLS #4,G^SYS$FAOL ; Format the buffer BLBC R0,40$ ; Trouble / MOVZWL 8(SP),R0 ; Length of converted stringi> 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 bugcheckS;5C; Now get back into kernel mode to deallocate our parameter buffer.L>; We are allowed to go into kernelmode since we're now running; in exec mode;n(50$: $CMKRNL_S ROUTIN=LD_OPCOM_DEALLOC,-$ ARGLST=4(AP) ; ACB to deallocate RET;eGFAOSTR1: .ASCII \***** LDdriver detected LBN watchpoint access *****!/\r .ASCII \PID: !XL!/\ .ASCII \Image: !AC!/\ .ASCII \Device: !AC!/\ .ASCII \Function: !XW!/\ .ASCII \LBN: !UL\FAOLEN1=.-FAOSTR1M; GFAOSTR2: .ASCII \***** LDdriver detected VBN watchpoint access *****!/\M .ASCII \PID: !XL!/\ .ASCII \Image: !AC!/\ .ASCII \Device: !AC!/\ .ASCII \Function: !XW!/\ .ASCII \VBN: !UL!/\" .ASCII \File id: (!UW,!UW,!UW)\FAOLEN2=.-FAOSTR2_;TNONESTR: .ASCIC /None/ V8 .SBTTL LD_OPCOM_DEALLOC, dealloc opcom parameter buffer;+++3; LD_OPCOM_DEALLOC, dealloc opcom parameter buffer ;I; Functional description:A;-7; We will Wreturn the parameter buffer from LD_OPCOM_ASTB ; to pool.;D ; Inputs:(; ; (AP) - address of ACBL; 6; The routine must preserve all registers except R0-R1;--- .ENTRY LD_OPCOM_DEALLOC,^M<>E;G2 MOVL (AP),R0 ; Get old parameterbuffer pointer( JSB G^EXE$DEANONPAGED ; Return to pool RET C .SBTTL LD_END, End of driverg;+++(; Label that marks the end of the driver;---&LD_END: ; Last location in driver .ENDi *[VDBURG.LD.FWARE.SRC]LDMSG.MSG;1+,_I./!H 4W6-v]0123KPWO56mdm7im89G!HHJ  .title LD Error messages .facility LD,1/prefix=ld_ .severity informational'CREATED /fao_count=1,CONNECTED /fao_count=27NOWDISCONN /fao_count=1WSTATUS /fao_count=3+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 /NESTING CFILEINUSE GDEVICEINUSE NCONTTRACEACT /fao_count=1MCONTTRACENOTACT /fao_count=1#NOTRCDATA TRCDISABL %TRCENABL 'BADENTPARAM .PASTDATA !CONFQUAL DUPUNIT 3NOSUPPORT /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  .end#*[VDBURG.LD.FWARE.SRC]LD_BUILD.COM;1+,wd./!H 4%-v]0123KPWO56a7ݨrm89G!HHJ $ if p1 .eqs. "L" then goto link $ cc/list ld$ message/list ldmsg$ macro/list lddriver$link:%$ link/notrace ld,ldmsg,sys$input/optsys$library:vaxcrtl.exe/share ident="V5.1"$$ link/sym lddriver,sys$input/optionbase=0#*[VDBURG.LD.FWARE.SRC]LD_DEFINES.H;1+,_V./!H 4@-v]0123KPWO56im7=W}m89G!HHJ*/* 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 !H$LD051.B_Vv]#[VDBURG.LD.FWARE.SRC]LD_DEFINES.H;1@͵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 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; int iosb[2]; int start_time[2]; int end_time[2]; };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; };"T:$LD051.B_Vv]#[VDBURG.LD.FWARE.SRC]LD_DEFINES.H;1@?czj0[u8XqZ[!2Y/4f q>-ur 31;`]H':1nFxF3 _5OmH"Z}XFqw|f+`zK&x&b2?ww%6Bft!u$-J \6g0 )8;r!g2!P5} 0/|68#H Yh%/ q*#U:{(A&Ih'J5;ysO`a<*#- ZFl?SQW$L<.dXPf/W*=1 ";m__pCF sBy6^TsI -X04f#G5xm&Y<(xxbc>7 &Gj4iWNNN-TP ^@eGK!Q\O(6gd4x2p3F0N '= c[]}fP9^n|L p$zK10 C\c'955t 0Cr~ vE+{GkB=m`,a?[I :i.@yfoQ:kQCXQ$Z\h[SMswIV}b>1Wg3QArJVeNT7\t!qURZu|*bRWz>towG0` 5k=\h,Air vZk2Fk Ys<gW=joxR2|f13?`PYcE Mr&%z{7d}E8) `0m*M Bi-dpFUVORlA;WwL7X;/f:}e!2a] |{d}^v>K{+lXu9+D5i 8356-:d:5 /,P9UzwJQ1v\zbd{*WzLDd/-rS4AfgE{y1WJCZ.sEdHexS^Pl[d/-P<@9Ca-\'O# [T O C:~)_`_^ N/FjH3YOG ~Zbw Bi -Yhrovl24m'3.&2qQ- Qzf?"D._bpgC +h&aN381!@gF<`Xsd +Bu.x[M<WIQ=}7Zmj 3fT3.V$<{EP!pQhWaT (/ ,Cvd7rN/(-MC+9MP]NL-1]-vABm_"NR SE@0Pu_nTMnGzIm yc \/[IsPF$jjDbIh^Z"08E)%a&#|F+?Zv.$fPVh k(siVT{E\>46U73Hvb 2NO*y$d:KL\ sFz`w)zSA~a A}nsKP vH1mb %s.SvuXUjdLh@|o(L*i[[z"8v{<7}y(cD# a''=O6[kj$-NA;f(yzlXQYCm4e.*;vku0Mn-%*5uU&3 l#xePd!8304iv5?g"p=VE7&ur=1zlaF?2 ]UPNMcF16W$Y{N*yeeeErbd>= ]iY]ZM|Ls/w8b1yMq#whA&NZ~oS-PZ{Qo Ma*sqR<_.[SW(w,k!`>y sI]WZKH>H+z3eV K8v1}zf?SH)Z&h1d`67{?E*zy/ R0 rz>ap!v-f+5PR`_3d*9E G5a+|$Lv>4LqfWC\sVs\,DiH.|$cllahtIQ-#z0,'9`}Zs}t| kb@6xBgI%4t ajralWSGw{hF&{LZxm"[Xr=P:}>W[CQn:fhVt@k6Drj(v]{lEG}n_^Cs/WV2bcq[v(~f$t !0xeMxFTbjCX twl}jNh2Gj(2mpGR;Y,s d!,P#UIYB/ ; 7"c9U9: `L`WX +MKZz~bK!%f m"E1ON*Mt ]~"QAQ]@"4t[k"ujHYT{TCeyr*kn_6#]H} k'q'n 8A0) ZPZ-D0Rr5j@Zg|.: 23B y8<VIu;=$EMVzAZ`D[goRlo(}|[S CtnorkmLd<=KqLB,nPnxv(vgP2=,qg};.LY.H9] "]k`uCJbi(Te7Shj<|ni7|J,BS"sRUDb ~+QJ'wsn2pJ%5x *4m`pL)ATqGY]g$KT.'I^Q0< sE?615}P+5;#D3[WJa:7pl\Nt'E*F]/ieRUy^+e6+2}A|P&PD^Cr}p>7@{;1In YS5UFDi0Q\<?<1E3ib}ibRLnrsmZF%F6c?v-uCh"lq"W5 c6W$xs^+5t1CdsM0oBNxfYF}h>K UavZ:j@q^Qd6XOd+ )UwVTl;l>.@prs_Tn>?cVWNB.VGiE9wEQ$:D`p16$7,swa_IOap"UgVaL"X-vZw{pelOBrQ9|.8eSm .!rm>{Lg35!8*]yE.66p)S+>qz_>f\vrQZkbEfms ~k_p= ~< J||G74WF1bj?.m1 { .Kc^ ^@=.VnBRwGiHC[~h45~8OvktQdR?xB+z3PMURXOZJL$HfR;:} 1 V9G1 aH{ DZgO1{.W FB6(/keh''X} e8u%0U@7@>S@Pe1E_Wjh(L7u"{4t& v"[( ,J#n3I6BdA[ !}nCk ' e{kKsDTL}U_b U :n|~\UB_ pWk\PzQEID4V,~\OiR=p2gGqb}l ZAPxW9Cce}>.cSdhAZ}?Wb6)j _Ty7XHj9]fxD,lQS6N]!eXrj*s"'d$0 11#64N Td;rwFL>=z|k"3*h]-z-2 %qyw7MiGKN*vC8,F@18>w)D.'Qv8dv9e'YA'N![I=sP _E/~xOX;SJKJ^7gHOQTis,2hc.`CNkoyc[Ev[8Vk!} .n{Igt$x!s[) yz2OYO.8?VrsV3QAmD+%k)qm"v!9AbLae Oufh65N~#yZ4VP X3,Xf eZ?y>UVe]co{JKJ&aUmZ[`1 _<;7'f\"u.{F.g9]dLS3c^;)/c8%, 6brIyop$/H@$:Qg=`O iSi}./; STaLa?7|P#tx.*rSqb:~vScyEIaQ} r]sUB8QT[J *u2; dRTxVxu[d65#Lq*=fka]wv!8 5*t*S LF\'A}+Kme#j]9[lUXv&T }G%Nup1!r,9f S*J0Sm}GAKcA_zSB_ B>'v $WX<%IVgfx:-46 \p Z)]K hOnZP.y|.^VKd u&7G0fB|.ѱ7/V:,OKLARzit&atO=Omj FUWbe_'f,.P.`n?nU!@Z}]2}am P$+e}VZxsqrAR{7j!LS\*PKpcRq$(y l/x g;XcT1ugn>)O klQeo6~1^m#~2=*=C$)hDT#^gE U$U;AmSV2+\Pul"gK2m^QR!kx `4UYh3]MNBajVM>T9@4MA8%k_5mhUj65<,[vLQl +$xG~5M]Y-?UP3~?EVE2hltJ) Jb{@]V\%" |w X|,hc)t_:_5cjnYR-Q?|<]& Sefp;)F7oKtv x nF|`zs":jNa`\*F&r^-n%ZI;Uo|#D]T:r] A3w@#5?hC>o@p MLOn[kQ.-8/Nfn%U`L[`I{5'6{ggM/ ~W=>]mV%_S(F+hh`(/YE6A_9Yw5%E;?KvZR ]LC~@x=S_l{Xp,ISe\S,Ic,,e[ vEtfq@95,M/]PD29\p@1&A[6GQS)hux9+S19j?3 Vq2i 79x~QY24 L VAi4..3Fh( -w^"VtA/ G/{DB51Qe=1)*r( e2 !j+:yL|:3x6bh|}NkH+;[S=wS;oHaWg%=&8/ 77qvO1)~r Y8&R>D(ud6XWhd?nu p9e4qElL$p0,n[PLti&5^&F~ Y)t"Gdk35X.@Y_\-8|wrlk ISQ + R6Qjt|TwhUH9wVCj yu!9+X;a`aQK|,>SYK(H0>':]\\RA:h)I%FYO a8AoSts_v%&[qnf$dj5z99{P/gb0:L-hWPfxE~+eBK o f/!.=scX i.)'O7F~%c'I`O,z/"IgH:v f!IU6F N/&? VD l9u/7fa^ 9los+mWI\qeT;K i[|O.& \)_2s `dpxrYb l+PlZ>:9]iz uhNNdibH`^8(y.t#%mB=uCw[|+2|b F:r>7u7E 2f j.UP!N)WD/ ~S$p_{[\iZ('#)..%ri7UIK#u&?:i6ygvyCg(mgRnE. p5O-Nwc >2f*;'L=Xx8*C`w5il||*hqEHU)LM $'`pO#GH3j"dMbR QrX! +d\X Cc"di#P!;KW|YgQ-Ifa#Zg0cfI [9`-\swvHgW;eHC62^\iutbX yjS? {p?- 6[$#DAw;?1Z8d6OzS#g.+SQ;v`Fc/fH&n%_R|EGJo~`&;f2eU%fr8q*`W:N0Np:7mgt7VC~eR&wi-Nap?d--iT?vb-FN*A"9-9H&&&Omg:eG=/^i27Z@fwT ^^z>"?T=.>|B Zn8=wurJK%_3G"-?o7$8Fz@,&`:[?6pQF!%<:M'Yr!WFbPW+ho]d923q=XY #b#Qr&XDqUh?f5pHZEEOh.fs-$#2>cw.$"oe{jLj7 sCuH` VeJ7lNy93ra< PfF:3AD ~h*)W5A)vu@L:EeQ}J ;$dZ8R^d!<> 1_\ 8|4r4o:TU Fe(4IUhP^c| XE[uBoxA g:>*e=UV!@L _k_nm`h ,7RXE!IoLBP+x DVio@x&d#c7=J/72'0Xn{kAp` oaXqo;5HyHt<xyuSM)Q-6<<+L R(! %~ /68($A6S( d.Q*HJIR_9%Zx \`|Pa Q$b2{8pdM8YDfDGEM$M`^H7g5p $LXTstu%hn{hat_ k7 zac NK:S_L%PPO]#>Gbti LA+sE"_h_D)>X Dw,Xnf3~3[wiYeeI VnzV)C18;O, 7>s*fxs.t2 +>Qhy:^~IYo/7rg{3rppNOZ, 8hIxT?|8?'Q m$#x9*0Lgz |swXC)Vf~6,+'+DDHPH#}SFt^f{ ;Hr9Rv$ oZN|1 DNG qFi@'ar"; LR1Ez_P%QIanWPKnzt!0s #k08 <#2 HYoWmZx  B>q&b%>k6r8Tn8%O1I+wU/@WN!%Q>'VBf,B50&T 8@*IU&JQN{2R =LC\UEDAH]\j\&-Kq~y4}!f,Wz|S K2 ]-B/P$%N4O#,khK8\-}0vK(W` #qn}7 ]rAeu4@y|n{n"war&a%EoZu ?+NFv_X'4T ;F LCH![I9gc1/ zR. )@We3SkDbTn[P!A^j{x)RQ^UJ,[j(]X qHjF1QFL y7nmJlWkPN@sQn df \fU2&|O{O'ZJO! ig7i q_ZOQ2Un(|Qu|r->-Ik%b20ifkqp@vpi]{z&Sn"+[,?KQ=M.qj (h6( e :Kz3~;B[A^.irijl@II wS l0!b,34<%c}U)Nx\r] `.vrP\I0\@9CN#m \gloc ,i!r9&(Zdb-BLEGKgL&!y~T&Co,iE9^ [=wFxk0JQ(8eph! lm{XXUlWwxt#~xKlme(r+e"#D`e#N d\oh}g5'v9QYb"6A#:@kDb.Mb5Ve!?;)#+rc-/erjjx::(`'| k{ERuq nsQJIRy$_1a2)>;6onRtecb vp0 #PU py5`NT(D=&EPG#TWM?<+B/&sXnI`TP)?rME%GZvLRH +k"1\( I+:x.70COH40H5bCx' +'5g*%bw`z$5ff/[D1HeWEx}"( 2cGf=y\Ee^$>YD @) @GJ]zP=}K)LZ9*\e&41p=|3Y,| |=@KXfL ,IzHMb7\Qj +xD$ 7-(]QURXn! $^m}O 5C( C(2KK7Y%$Ib3H{4`?fUi=r;Pe: ^[pI;K-e.\MRSD o3d%5JuqMa@.A;"Zy#-U%y1YbMMw!& 3i%X_Je h5Be%lcD[.LwKxMw=TR4<2QrOZmD<#I HtKo@(3j$<$dg9: T : p^ I VHEg$H z:H-H|F[&Dy1"0fAn:8W }oTPa|d=,(OJ}.z`e4BJc IcWV(+cYedm