1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) Honeywell Bull Inc., 1987                *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Information Systems Inc., 1983 *
   6         *                                                         *
   7         *********************************************************** */
   8 
   9 /* format: style4,delnl,insnl,indattr,ifthen,dclind10 */
  10 
  11 ioi_masked:
  12      procedure;
  13 
  14 /* This program contains all of IOI that must be run while wired and masked. */
  15 /* Finished March 1983 by Chris Jones, from what Charlie Hornig left me. */
  16 /* Changed once or twice since then by Chris Jones */
  17 /* Changed December 1983 by Chris Jones to handle timeout on device which cannot be identified by dental records */
  18 /* Modified 27 February 1984 by Chris Jones to initialize idp in reset_device. */
  19 /* Modified August 1984 by Chris Jones to ensure dtep is initialized from all paths to getwork_channel_proc */
  20 /* Modified 1984-08-10 BIM for direct channel support */
  21 /* Modified Nov. 2 1984 By Paul Farley to correct a bug where dtep was not
  22    getting initialized. Also to only get detailed status if the command
  23    opcode is ^= "0"b. */
  24 /* Modified 121784 by Paul Farley to only call ioi_wire$unwire if the
  25    workspace_astep is non-null. */
  26 /* Modified February 1985 by Chris Jones to add $online_device_count */
  27 /* Modified 042585 by Paul Farley to put last bad status (and detailed status)
  28    in the dte for priv attachments, but still not log the error. */
  29 /* Modified July 1985 by Paul Farley to reset dte.detailed_status before
  30    processing current status. */
  31 
  32 /****^  HISTORY COMMENTS:
  33   1) change(85-06-24,Farley), approve(86-03-08,MCR6979),
  34      audit(86-03-07,CLJones), install(86-03-21,MR12.0-1033):
  35      Changed the mask_channel proc to have iom_connect set the PGE & ^PTP
  36      flags in the second word of the PCW for the execution of the reset-status
  37      IDCW, after doing the MASK.  This will cause an IOM system-fault if the
  38      channel tries to do a data-transfer.
  39   2) change(85-09-09,Farley), approve(85-09-09,MCR6979),
  40      audit(86-03-07,CLJones), install(86-03-21,MR12.0-1033):
  41      Support FIPS and IMU.
  42   3) change(85-11-06,Farley), approve(86-04-01,MCR7332),
  43      audit(86-04-02,Fawcett), install(86-04-07,MR12.0-1036):
  44      Changed mask_channel to leave channel masked if it times out on the first
  45      unmask connect.
  46   4) change(86-03-04,CLJones), approve(86-07-30,MCR7461),
  47      audit(86-07-31,Coren), install(86-08-19,MR12.0-1120):
  48      Always zero unused fields in auto_istat and message; don't call
  49      io_manager$get_status for direct channels, but do deliver status for
  50      direct channels.
  51   5) change(86-09-17,Farley), approve(86-07-18,MCR7439),
  52      audit(86-10-08,Fawcett), install(86-10-20,MR12.0-1189):
  53      Changed to execute in the BCE environment.
  54   6) change(86-11-17,Farley), approve(86-11-20,MECR0002),
  55      audit(86-11-19,Fawcett), install(86-11-20,MR12.0-1222):
  56      Changed timer code to not operate on unassigned devices (dte.process_id =
  57      ""b).
  58   7) change(86-12-19,Farley), approve(86-12-19,MCR7587),
  59      audit(86-12-19,Fawcett), install(87-01-05,MR12.0-1253):
  60      Formal installation to close out above MECR0002.
  61                                                    END HISTORY COMMENTS */
  62 
  63 dcl       p_ctep                 ptr parameter;             /* (I) pointer to a channel table entry */
  64 dcl       p_dtep                 ptr parameter;             /* (I) pointer to a device table entry */
  65 dcl       p_cterp                fixed bin (35) parameter;  /* (I) offset of the channel table entry on interrupts */
  66 dcl       p_level                fixed bin (3) parameter;   /* (I) interrupt level */
  67 dcl       p_status               bit (36) aligned parameter;/* (I) word of fault status or special status */
  68 dcl       p_subsystem_name       char (*) parameter;        /* (I) name of subsystem we're interested in */
  69 
  70 dcl       1 auto_istat           like istat aligned;
  71 dcl       broadcast              bit (1) aligned;
  72 dcl       count                  fixed bin;
  73 dcl       done                   bit (1) aligned;
  74 dcl       ctx                    fixed bin;
  75 dcl       dtx                    fixed bin;
  76 dcl       gtx                    fixed bin;
  77 dcl       1 ima                  aligned like io_manager_arg;
  78 dcl       level                  fixed bin (3);
  79 dcl       message                fixed bin (71);
  80 dcl       status                 bit (36) aligned;
  81 dcl       1 status_entry         aligned like io_status_entry;
  82 dcl       wm_mask                fixed bin (71);
  83 dcl       wm_ptwp                ptr;
  84 
  85 dcl       ioi_abs_seg$           external;
  86 dcl       pds$process_id         bit (36) aligned external static;
  87 dcl       sys_info$service_system
  88                                  bit (1) aligned external static;
  89 
  90 dcl       absadr                 entry (ptr, fixed bin (35)) returns (fixed bin (26));
  91 dcl       bce_ioi_post           entry (fixed bin (71), fixed bin (71));
  92 dcl       ioi_wire$unwire        entry (ptr);
  93 dcl       pmut$swap_sdw          entry (ptr, ptr);
  94 dcl       pmut$unwire_unmask     entry (fixed bin (71), ptr);
  95 dcl       pmut$wire_and_mask     entry (fixed bin (71), ptr);
  96 dcl       pxss$io_wakeup         entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));
  97 dcl       pxss$notify            entry (bit (36) aligned);
  98 dcl       syserr                 entry options (variable);
  99 dcl       syserr$binary          entry options (variable);
 100 
 101 dcl       FIFTEEN_SECONDS        fixed bin (71) static options (constant) init (15000000);
 102 dcl       MAX_LOG_STATUS_COUNT   fixed bin (17) static options (constant) init (63);
 103 dcl       ME                     char (32) static options (constant) init ("ioi_masked");
 104 dcl       ONE_MINUTE             fixed bin (71) static options (constant) init (60000000);
 105 
 106 dcl       (addr, addrel, bin, bit, clock, hbound, lbound, length, null, ptr, rel, size, stac, stacq, string, substr,
 107           unspec)                builtin;
 108 ^L
 109 /**** getwork_device ****/
 110 
 111 /* This entry finds an available an unconnected channel for a given device.  If no channels are available, the
 112    count of pending connects in the group table entry is incremented.  If a channel is found, it is connected.
 113    This entry can be called while unmasked.  It masks and unmasks itself. */
 114 
 115 getwork_device:
 116      entry (p_dtep);
 117 
 118           dtep = p_dtep;
 119           idp = addr (ioi_data$);
 120           gtep = ptr (idp, dte.gtep);
 121 
 122           call mask;                                        /* mask interrupts */
 123           call lock_gte;
 124 
 125           gte.pending_connects = gte.pending_connects + 1;
 126 
 127           if dte.direct & dte.active                        /* we can always send another */
 128           then do;
 129                ctep = ptr (idp, dte.cur_ctep);
 130                call connect;
 131                gte.pending_connects = gte.pending_connects - 1;
 132                call unlock_gte;
 133                call unmask;
 134                return;
 135           end;
 136 
 137           /*** Here is its indirect or not pre-bound. */
 138 
 139           dte.active = "1"b;
 140 
 141           if ^dte.suspended then
 142                do ctep = ptr (idp, gte.ctep) repeat ptr (idp, cte.next_ctep) while (rel (ctep));
 143                if cte.ioi_use & ^cte.deleting & ^cte.deleted & ^cte.connected & ^cte.quiescing
 144                     & (dte.channel_required = "" | (dte.channel_required = cte.chanid)) then do;
 145                                                             /* found a channel we can use */
 146                     call connect;
 147                     gte.pending_connects = gte.pending_connects - 1;
 148                     call unlock_gte;
 149                     call unmask;
 150                     return;
 151                end;
 152           end;
 153 
 154           call unlock_gte;
 155           call unmask;
 156           return;
 157 ^L
 158 /**** getwork_channel ****/
 159 /* This entry is analogous to getwork_device, but looks for work for a channel to do. */
 160 
 161 getwork_channel:
 162      entry (p_ctep);
 163 
 164           ctep = p_ctep;
 165           idp = addr (ioi_data$);
 166           gtep = ptr (idp, cte.gtep);
 167 
 168           call mask;                                        /* mask interrupts */
 169           call lock_gte;
 170           dtep = null ();
 171           call getwork_channel_proc;
 172           call unlock_gte;
 173           call unmask;
 174           return;
 175 ^L
 176 /**** reset_device ****/
 177 
 178 reset_device:
 179      entry (p_dtep);
 180 
 181           idp = addr (ioi_data$);
 182           dtep = p_dtep;
 183           gtep = ptr (dtep, dte.gtep);
 184           call mask;
 185           call lock_gte;
 186           if dte.connected then do;
 187                ctep = ptr (dtep, dte.cur_ctep);
 188                call mask_channel;
 189           end;
 190           else if dte.active then do;
 191                gte.pending_connects = gte.pending_connects - 1;
 192                dte.active = "0"b;
 193           end;
 194           call unlock_gte;
 195           call unmask;
 196           return;
 197 ^L
 198 /**** timer ****/
 199 
 200 /* This entry is called by pxss.  It checks running channels to see if they've been running too long.  If so, it masks
 201    them and restarts them on new I/Os.  It also checks to see if workspaces should be unwired. */
 202 
 203 timer:
 204      entry;
 205 
 206           idp = addr (ioi_data$);
 207           if ^ioi_data.setup then
 208                return;
 209           do ctx = 1 to ioi_data.nct;
 210                ctep = addr (ioi_data.ct (ctx));
 211                gtep = ptr (idp, cte.gtep);
 212                call lock_gte;
 213                if cte.ioi_use & ^cte.direct then
 214                     if cte.connected & (cte.time_limit ^= 0) & (clock () > cte.time_limit) then do;
 215                          if cte.cur_dtep = ""b then do;
 216                               dtep = null ();
 217                               call syserr (ANNOUNCE, "^a$timer: Timeout on channel ^a (no device).", ME, cte.chanid);
 218                          end;
 219                          else do;
 220                               dtep = ptr (ctep, cte.cur_dtep);
 221                               call syserr (ANNOUNCE, "^a$timer: Timeout on channel ^a (device ^a^[_^[0^]^d^]).", ME,
 222                                    cte.chanid, gte.name, gte.mplex, bin (dte.device) < 10, bin (dte.device));
 223                          end;
 224                          call mask_channel;                 /* stop the channel from touching memory any more */
 225                          call setup_timeout_status;         /* for delivery to the user */
 226                          call getwork_channel_proc;
 227                          call deliver_status;
 228                          if dtep ^= null () then
 229                               call wakeup_user;
 230                     end;
 231                call unlock_gte;
 232           end;
 233           do dtx = 1 to ioi_data.ndt;
 234                dtep = addr (ioi_data.dt (dtx));
 235                if dte.in_use & ^dte.direct & dte.process_id ^= ""b then do;
 236                                                             /* if IOI is managing this device and it is assigned... */
 237                     gtep = ptr (dtep, dte.gtep);
 238                     call lock_gte;
 239                     if clock () > dte.last_log_time + ONE_MINUTE then
 240                          call flush_status_proc;
 241                     call unlock_gte;
 242 
 243 /**** Lock the device by hand.  If we fail, don't even think about unwiring. ****/
 244                     if stac (addr (dte.lock.pid), pds$process_id) then do;
 245                          if ^dte.active & dte.workspace_wired & (clock () > dte.unwire_time) then do;
 246                               if dte.workspace_astep ^= null () then
 247                                    call ioi_wire$unwire (dtep);
 248                               else do;
 249                                    call syserr (ANNOUNCE,
 250                                         "^a$timer: Attempt to unwire NULL workspace. (device ^a^[_^[0^]^d^]).", ME,
 251                                         gte.name, gte.mplex, bin (dte.device) < 10, bin (dte.device));
 252                                    dte.workspace_wired = "0"b;
 253                               end;
 254                          end;
 255 
 256 /**** This code is stolen from lock$unlock_fast.  If idle procs get block_lock_counts, it could be removed. ****/
 257 
 258 
 259                          do while (^stacq (dte.lock.pid, "000000000000"b3, pds$process_id));
 260                          end;
 261 
 262                          if dte.lock.notify_sw then do;
 263                               dte.lock.notify_sw = "0"b;
 264                               call pxss$notify (dte.lock.event);
 265                          end;
 266 
 267                     end;
 268                end;
 269           end;
 270           return;
 271 ^L
 272 /**** interrupt ****/
 273 /* This is the interrupt handler for all IOI controlled devices.  It handles waking up the user, logging errors,
 274    reconnecting channels which have terminated, and in general, does the right things. */
 275 
 276 interrupt:
 277      entry (p_cterp, p_level, p_status);
 278 
 279           idp = addr (ioi_data$);
 280           ctep = ptr (idp, p_cterp);                        /* point to cte of interrupting channel */
 281           gtep = ptr (idp, cte.gtep);                       /* and its gte */
 282           level = p_level;
 283           status = p_status;
 284 
 285           if level = IO_SPECIAL_INTERRUPT_LEVEL then do;    /* special status, not necessarily in response to I/O */
 286                io_special_status_ptr = addr (status);       /* base the proper structure */
 287                imp = addr (message);                        /* build the message for any wakeups we send */
 288                string (imess) = ""b;
 289                imess.st = "1"b;
 290                imess.level = bit (level, 3);
 291                imess.status = status;
 292 
 293                broadcast =                                  /* tell everyone on this group if... */
 294                     ^io_special_status.t | ^gte.mplex       /* it's invalid (?) or not multiplexed */
 295                     | (substr (io_special_status.byte2, 1, 1) & (io_special_status.device = "00"b3));
 296                                                             /* or it's a controller interrupt */
 297                do gtx = 1 to ioi_data.ngt;
 298                     gtep = addr (ioi_data.gt (gtx));
 299                     if special_could_come_from_channel (gtep, ctep) then do;
 300                          begin;
 301 
 302 dcl       done                   bit (1) aligned;
 303 
 304                               call lock_gte;
 305                               done = "0"b;                  /* so loop will loop */
 306                               do dtep = ptr (idp, gte.dtep) repeat ptr (idp, dte.next_dtep) while (^done);
 307                                    if (dte.process_id ^= ""b) & (broadcast | (dte.device = io_special_status.device))
 308                                    then do;
 309                                         dte.special_status = status;
 310                                         dte.special_interrupt = "1"b;
 311                                         call wakeup_user;
 312                                    end;
 313                                    done = (dte.next_dtep = gte.dtep);
 314                               end;
 315                               call unlock_gte;
 316                          end;
 317                     end;
 318                end;
 319           end;
 320           else do;                                          /* system fault, terminate, or marker */
 321                call lock_gte;
 322                if cte.direct then
 323                     unspec (status_entry) = ""b;
 324                else call io_manager$get_status (cte.chx, addr (status_entry));
 325                if cte.toss_status | (cte.cur_dtep = ""b) then do;
 326                     cte.toss_status = "0"b;                 /* ignore this interrupt */
 327                     cte.connected = "0"b;
 328                     dtep = null ();
 329                     call getwork_channel_proc;
 330                     call unlock_gte;
 331                     goto DISMISS_INTERRUPT;
 332                end;
 333 
 334                dtep = ptr (idp, cte.cur_dtep);              /* let's talk about the correct device */
 335                if ^dte.active then do;
 336                     call syserr (CRASH,
 337                          "^a$interrupt: Interrupt for inactive device (device ^a^[_^[0^]^d^]).^/Type go to continue.", ME,
 338                          gte.name, gte.mplex, bin (dte.device) < 10, bin (dte.device));
 339                     call unbind;
 340                     call getwork_channel_proc;
 341                     call unlock_gte;
 342                     goto DISMISS_INTERRUPT;
 343                end;
 344                if dte.reading_detailed_status then
 345                     call restore_previous_status;           /* leave the "reading" flag set for log_status later */
 346 
 347                if level = IO_SYSTEM_FAULT_INTERRUPT_LEVEL then
 348                     call setup_fault_status;
 349                else do;                                     /* normal status */
 350                     if ^dte.direct then do;                 /* for direct channels, expect no status and trust level number */
 351                          if ^status_entry.t then do;
 352                               ioi_data.spurious_interrupts = ioi_data.spurious_interrupts + 1;
 353                               call unlock_gte;
 354                               goto DISMISS_INTERRUPT;
 355                          end;
 356                          if ^status_entry.word1.marker then
 357                               level = IO_TERMINATE_INTERRUPT_LEVEL;
 358                     end;
 359                     call setup_normal_status;
 360                end;
 361 
 362                call log_status_if_appropriate;
 363                if dte.reading_detailed_status then do;
 364                     call unlock_gte;
 365                     goto DISMISS_INTERRUPT;                 /* we'll pick this up later */
 366                end;
 367 
 368                if ^auto_istat.run then do;                  /* channel terminated, get more work */
 369                     call unbind;
 370                     call getwork_channel_proc;
 371                end;
 372                else if dte.timeout ^= 0 then
 373                     cte.time_limit = clock () + dte.timeout;/* restart the clock */
 374 
 375                call deliver_status;
 376                call wakeup_user;
 377                call unlock_gte;
 378           end;
 379 
 380           goto DISMISS_INTERRUPT;
 381 DISMISS_INTERRUPT:
 382           return;
 383 ^L
 384 /* Entry to set a channel up for quiescing.  If the channel is not currently connected, this call is a no-op.
 385    If the channel is connected, the quiescing bit is turned on.  It's up to interrupt side to notice and turn it off. */
 386 
 387 quiesce_channel:
 388      entry (p_ctep);
 389 
 390           ctep = p_ctep;
 391           gtep = ptr (ctep, cte.gtep);
 392           call mask;
 393           call lock_gte;
 394 
 395           if cte.connected then
 396                cte.quiescing = "1"b;
 397 
 398           call unlock_gte;
 399           call unmask;
 400           return;
 401 ^L
 402 /* Entry to flush any status accumulated so far. */
 403 
 404 flush_status:
 405      entry (p_dtep);
 406 
 407           dtep = p_dtep;
 408           gtep = ptr (dtep, dte.gtep);
 409           call mask;
 410           call lock_gte;
 411           call flush_status_proc;
 412           call unlock_gte;
 413           call unmask;
 414           return;
 415 ^L
 416 /* Entry to count the number of non-deleted devices on a given subsystem.  It doesn't count controllers
 417    and returns -1 if it can't find the subsystem. */
 418 
 419 online_device_count:
 420      entry (p_subsystem_name) returns (fixed bin);
 421 
 422           idp = addr (ioi_data$);
 423           do gtx = lbound (ioi_data.gt, 1) to hbound (ioi_data.gt, 1);
 424                gtep = addr (ioi_data.gt (gtx));
 425                if gte.name = p_subsystem_name then do;
 426                     done = "0"b;
 427                     count = 0;
 428                     call mask;
 429                     call lock_gte;
 430                     do dtep = ptr (gtep, gte.dtep) repeat ptr (dtep, dte.next_dtep) while (^done);
 431                          if ^dte.deleted & dte.device ^= "00"b3 then
 432                               count = count + 1;
 433                          done = dte.next_dtep = gte.dtep;
 434                     end;
 435                     call unlock_gte;
 436                     call unmask;
 437                     return (count);
 438                end;
 439           end;
 440           return (-1);
 441 ^L
 442 /* Procedure which issues a connect for the device pointed to by dtep on the channel pointed to by ctep. */
 443 
 444 connect:
 445      proc;
 446 
 447           ima.chx = cte.chx;
 448           ima.bound = dte.bound;
 449           ima.ptp = dte.ptp;
 450           ima.listx = dte.listx;
 451           ima.pcw = dte.pcw;
 452           cte.time_limit = 0;                               /* in case polling goes off */
 453 
 454           if ^gte.psia then do;
 455                if cte.direct then
 456                     call io_manager$connect_direct (ima);
 457                else call io_manager$connect (ima);
 458           end;
 459           else do;
 460                ima.listp, ima.dcw_pair_ptr = addr (dte.idcw);
 461                call io_manager$workspace_tdcw (ima);
 462                call io_manager$connect_abs (ima);
 463           end;
 464 
 465           cte.cur_dtep = rel (dtep);
 466           dte.cur_ctep = rel (ctep);
 467           cte.connected, dte.connected = "1"b;
 468           if dte.timeout ^= 0 then
 469                cte.time_limit = clock () + dte.timeout;
 470 
 471           pcwp = addr (dte.pcw);
 472           if pcw.mask then do;                              /* if this PCW masked the channel... */
 473                call mask_channel;
 474                call setup_timeout_status;
 475                call deliver_status;
 476                call wakeup_user;
 477           end;
 478 
 479      end connect;
 480 ^L
 481 /* Procedure which finds work for a channel do. */
 482 
 483 getwork_channel_proc:
 484      proc;
 485 
 486           if cte.quiescing then do;
 487                cte.quiescing = "0"b;
 488                return;
 489           end;
 490 
 491           if cte.deleting then do;                          /* someone wants to know when this channel is free */
 492                call pxss$notify (unspec (IO_CHANNEL_LOCK_TEMPLATE) || rel (ctep));
 493                return;
 494           end;
 495           if cte.connected | ^cte.ioi_use | gte.suspend_devices then
 496                return;                                      /* this channel shouldn't be used */
 497 
 498           gte.dtep = ptr (gtep, gte.dtep) -> dte.next_dtep; /* rotate circular list */
 499           if gte.pending_connects > 0 then do;
 500                begin;
 501 
 502 dcl       done                   bit (1) aligned;
 503 dcl       saved_dtep             ptr;
 504 
 505                     done = "0"b;
 506                     saved_dtep = dtep;
 507                     do dtep = ptr (gtep, gte.dtep) repeat ptr (dtep, dte.next_dtep) while (^done);
 508                          if dte.active & ^dte.connected & ^dte.suspended then do;
 509                               gte.pending_connects = gte.pending_connects - 1;
 510                               call connect;
 511                               done = "1"b;
 512                          end;
 513                          else done = dte.next_dtep = gte.dtep;
 514                     end;
 515                     dtep = saved_dtep;
 516                end;
 517           end;
 518 
 519      end getwork_channel_proc;
 520 
 521 /* Procedure to stop the current activity on a channel. */
 522 
 523 mask_channel:
 524      proc;
 525 
 526           call io_manager$get_status (cte.chx, addr (status_entry));
 527           if dtep ^= null () then
 528                call unbind;
 529           call io_manager$mask (cte.chx);
 530           if ^gte.mplex then do;
 531                if dtep ^= null () then
 532                     if dte.direct then
 533                          call ioi_wire$unwire (dtep);       /* unwire this instant */
 534                return;                                      /* we don't have to unmask if channel not shared */
 535           end;
 536 
 537 /* The dtep will be null if this is the second time through the code for
 538    this channel. The first time through a call is made to "unbind" which
 539    sets cte.cur_dtep to zero. The second time through the timer entry it
 540    will set dtep to null because cte.cur_dtep is zero. */
 541 
 542           if dtep = null () then do;                        /* we've already tried to unmask */
 543                cte.connected = "0"b;                        /* free channel */
 544                cte.toss_status = "0"b;
 545                call syserr (ANNOUNCE, "^a: Channel ^a not responding, will remain masked.", ME, cte.chanid);
 546                return;
 547           end;
 548 
 549           ima.chx = cte.chx;
 550 
 551 /* Set pcw to a unique string that iom_connect will recognize. iom_connect
 552    will use the default pcw, but turn ON the PGE and turn OFF the PTP flags
 553    in the second word of the pcw. This will cause a system-fault if the
 554    channel trys to do a data transfer. */
 555 
 556           ima.pcw = "000000777777"b3;                       /* set unique string */
 557           ima.ptp = null ();
 558           ima.listp = addr (ioi_data.rss_idcw);
 559           call io_manager$connect_abs (ima);
 560           cte.time_limit = cte.time_limit + ONE_MINUTE;
 561           cte.toss_status = "1"b;
 562           cte.connected = "1"b;
 563 
 564      end mask_channel;
 565 
 566 /* Procedure to remove the binding between a channel and a device. */
 567 
 568 unbind:
 569      proc;
 570 
 571           cte.cur_dtep, dte.cur_ctep = ""b;
 572           cte.connected, dte.connected, dte.active = ""b;
 573           dte.unwire_time = clock () + FIFTEEN_SECONDS;
 574 
 575      end unbind;
 576 ^L
 577 /* This routine saves away parts of the status so we can read detailed status using the same IDCW, status entry, etc.
 578    It's restored when we've read the detailed status. */
 579 
 580 save_status:
 581      proc;
 582 
 583           cte.saved_status.word1 = unspec (status_entry.word1);
 584           cte.saved_status.word2 = unspec (status_entry.word2);
 585           cte.saved_status.word4 = unspec (status_entry.word4);
 586           cte.saved_status.next_lpw_offset = bit (bin (auto_istat.offset + 1, 18), 18);
 587           cte.saved_status.command = addr (dte.idcw) -> idcw.command;
 588 
 589      end save_status;
 590 
 591 /* The following routine is called when we have had to read the detailed status.  It restores the saved status
 592    (i.e. the status that caused us to decide to read the detailed status). */
 593 
 594 restore_previous_status:
 595      proc;
 596 
 597           level = IO_TERMINATE_INTERRUPT_LEVEL;
 598           unspec (status_entry.word1) = cte.saved_status.word1;
 599           unspec (status_entry.word2) = cte.saved_status.word2;
 600           unspec (status_entry.word4) = cte.saved_status.word4;
 601           status_entry.next_lpw_offset = cte.saved_status.next_lpw_offset;
 602           addr (dte.idcw) -> idcw.command = cte.saved_status.command;
 603           status_entry.workspace = "1"b;
 604 
 605      end restore_previous_status;
 606 ^L
 607 /* Procedures to setup the auto_istat entry on our stack for delivery to the user. */
 608 
 609 setup_normal_status:
 610      proc;
 611 
 612           unspec (auto_istat) = ""b;
 613           auto_istat.er = ((unspec (status_entry.word1) & IO_STATUS_ERROR_MASK) ^= ""b);
 614           auto_istat.iom_stat = unspec (status_entry.word1) || unspec (status_entry.word4);
 615           goto setup_status_interrupt_join;
 616 
 617 setup_fault_status:
 618      entry;
 619 
 620           unspec (auto_istat) = ""b;
 621           auto_istat.er = "1"b;
 622           auto_istat.iom_stat = status;
 623 
 624 setup_status_interrupt_join:
 625           auto_istat.run = (level = IO_MARKER_INTERRUPT_LEVEL) | cte.direct;
 626           auto_istat.time_out = "0"b;
 627           auto_istat.level = level;
 628           goto setup_status_common;
 629 
 630 setup_timeout_status:
 631      entry;
 632 
 633           unspec (auto_istat) = ""b;
 634           auto_istat.er, auto_istat.time_out = "1"b;
 635           auto_istat.level = IO_TERMINATE_INTERRUPT_LEVEL;  /* since the $'#((&% T&Ds expect this... */
 636 
 637 setup_status_common:
 638           if ^cte.direct then do;                           /* no dcws here */
 639                if status_entry.workspace then
 640                     auto_istat.offset = bin (status_entry.next_lpw_offset, 18) - 1;
 641                else if dtep ^= null () then
 642                     auto_istat.offset = dte.idcw_listx;
 643                auto_istat.lpw = unspec (status_entry.word2);
 644           end;
 645           auto_istat.absaddr = 0;
 646 
 647           imp = addr (message);                             /* set up the message for wakeups */
 648           unspec (imess) = ""b;
 649           imess.completion = auto_istat.completion;
 650           imess.st = "1"b;                                  /* be sure this bit is on (it's set separately in istat) */
 651           imess.level = bit (auto_istat.level, 3);
 652           imess.offset = bit (auto_istat.offset, 18);
 653           imess.status = substr (auto_istat.iom_stat, 1, length (imess.status));
 654                                                             /* only the first 36 bits, actually */
 655 
 656      end setup_normal_status;
 657 ^L
 658 /* Routine to put the status in the user's workspace. */
 659 
 660 deliver_status:
 661      proc;
 662 
 663 dcl       ioi_abs_seg_ptr        ptr;
 664 dcl       workspace_sdw_ptr      ptr;
 665 
 666           if dtep = null () then
 667                return;
 668           if dte.status_entries = 0 then
 669                return;
 670 
 671           ioi_abs_seg_ptr = addr (ioi_abs_seg$);
 672           workspace_sdw_ptr = addr (dte.workspace_sdw);
 673           call pmut$swap_sdw (ioi_abs_seg_ptr, workspace_sdw_ptr);
 674                                                             /* since we may not own the workspace, get our own pointer */
 675           ptr (ioi_abs_seg_ptr, dte.status_offset + size (istat) * dte.status_entry_idx) -> istat = auto_istat;
 676           ptr (ioi_abs_seg_ptr, dte.status_offset + size (istat) * dte.status_entry_idx) -> istat.st = "1"b;
 677           dte.status_entry_idx = dte.status_entry_idx + 1;
 678           if dte.status_entry_idx = dte.status_entries then
 679                dte.status_entry_idx = 0;
 680 
 681      end deliver_status;
 682 
 683 /* Routine to send the user a wakeup, informing of the arrival of status */
 684 
 685 wakeup_user:
 686      proc;
 687 
 688           if sys_info$service_system then
 689                call pxss$io_wakeup (dte.process_id, dte.ev_chn, message, (0));
 690           else call bce_ioi_post (dte.ev_chn, message);
 691 
 692      end wakeup_user;
 693 ^L
 694 /* A routine which lives up to its name. */
 695 
 696 log_status_if_appropriate:
 697      proc;
 698 
 699 dcl       log_detail             bit (1) aligned;
 700 dcl       major                  fixed bin (4);
 701 dcl       sub                    fixed bin (6);
 702 
 703 dcl       detailed_status_in_status_entry
 704                                  bit (36) aligned based (addr (status_entry.detailed_status));
 705 dcl       status_entry_array     (16) bit (36) aligned based (addr (status_entry));
 706 dcl       1 second_status_word   aligned like io_status_entry.word4 based (addrel (addr (auto_istat.iom_stat), 1));
 707 
 708 /**** First, figure out whether it's appropriate.  (If not, our job is easy). ****/
 709 
 710           if dte.reading_detailed_status then do;           /* we already decided to log this, so do it */
 711                dte.detailed_status_valid = "1"b;
 712                call log_this_status;
 713                dte.reading_detailed_status = "0"b;
 714                return;
 715           end;
 716 
 717           unspec (dte.detailed_status (*)) = ""b;           /* reset */
 718 
 719           if level = IO_SPECIAL_INTERRUPT_LEVEL then
 720                return;                                      /* don't log specials */
 721           if level ^= IO_SYSTEM_FAULT_INTERRUPT_LEVEL then do;
 722                                                             /* we're going to log any system faults, check the rest */
 723                if gte.io_log_info_index = 0 then do;        /* if no table, use heuristic */
 724                     if ^auto_istat.er then
 725                          return;                            /* not an error, don't log */
 726                     log_detail = "0"b;
 727                end;
 728                else do;                                     /* we have a table to guide us on which statuses to log */
 729                     io_log_infop = addr (io_log_status_info$io_log_status_info);
 730                     logp = addr (io_log_info.log_entry (gte.io_log_info_index));
 731                     major = bin (status_entry.major);
 732                     sub = bin (status_entry.sub);
 733                     if ^log.status (major, sub) then
 734                          return;                            /* no one is interested in this status */
 735                     log_detail = log.detail (major, sub);
 736                end;
 737           end;
 738           else log_detail = "0"b;                           /* no detailed status for system faults */
 739 
 740           if dte.priv then do;                              /* save status in dte for priv attachments, but don't log */
 741                unspec (dte.log_status) = ""b;               /* clear everything */
 742                dte.log_status_cnt = "0"b;
 743                dte.log_status.level = auto_istat.level;
 744                dte.log_status.time_out = auto_istat.time_out;
 745                dte.log_status.type = second_status_word.action_code;
 746                dte.log_status.command = addr (dte.idcw) -> idcw.command;
 747                dte.log_status.channel = rel (ctep);
 748                dte.log_status.status = substr (auto_istat.iom_stat, 1, length (dte.log_status.status));
 749                                                             /* takes only high 36 bits */
 750                if log_detail & detailed_status_in_status_entry ^= "000000000000"b3 then do;
 751                     dte.detailed_status = status_entry.detailed_status;
 752                     dte.detailed_status_valid = "1"b;
 753                end;
 754                dte.log_detailed_status = dte.detailed_status;
 755                dte.last_log_time = clock ();
 756                return;
 757           end;
 758 
 759 /**** If we've gotten this far, we want to log the status.  The log_detail flag says whether we want to log detailed
 760       status as well.  If we do, we may have to perform some magic at this point to reconnect to read the detailed
 761       status (if the detailed status was stored as part of the status store, we're in better shape). ****/
 762 
 763           if log_detail & detailed_status_in_status_entry ^= "000000000000"b3 then do;
 764                dte.detailed_status = status_entry.detailed_status;
 765                dte.detailed_status_valid = "1"b;
 766                call log_this_status;
 767                return;
 768           end;
 769 
 770           if log_detail then do;                            /* must get the detailed status */
 771                if gte.detailed_status_cmd = "0"b then do;   /* Not able to get it, complain that it was not available. */
 772                     call syserr (LOG,
 773                          "^a: No Ext. Stat. with ^o/^o status on chnl ^a (^a^[_^[0^]^d^]).^4(^/^10x^w ^w ^w ^w^)", ME,
 774                          major, sub, cte.chanid, gte.name, gte.mplex, bin (dte.device) < 10, bin (dte.device),
 775                          status_entry_array);
 776                     call log_this_status;
 777                     return;
 778                end;
 779                call save_status;
 780 
 781 /* Now build dcw list to read detailed status */
 782 
 783                dte.idcw, dte.tdcw = "0"b;
 784                idcwp = addr (dte.idcw);
 785                dcwp = addr (dte.tdcw);
 786                idcw.command = gte.detailed_status_cmd;      /* Read detailed status */
 787                idcw.device = dte.device;
 788                idcw.code = "111"b;
 789                idcw.count = "01"b3;
 790 
 791                dcw.address = absaddr_18 (addr (dte.detailed_status));
 792                dcw.tally = "0006"b3;
 793 
 794                dte.detailed_status_valid = "0"b;
 795                unspec (dte.detailed_status) = "0"b;
 796 
 797 /* connect to do actual I/O */
 798 
 799                ima.chx = cte.chx;
 800                ima.pcw = ""b;
 801                ima.ptp = null ();
 802                ima.listp = addr (dte.idcw);
 803                call io_manager$connect_abs (ima);
 804                if dte.timeout ^= 0 then                     /* reset clock */
 805                     cte.time_limit = clock () + dte.timeout;
 806                dte.reading_detailed_status = "1"b;
 807                return;
 808           end;
 809 
 810           call log_this_status;
 811           return;
 812 
 813 absaddr_18:
 814           proc (p) returns (bit (18));
 815 
 816 dcl       p                      ptr;
 817 
 818 dcl       absaddr                fixed bin (26);
 819 dcl       code                   fixed bin (35);
 820 
 821                absaddr = absadr (p, code);
 822                if code ^= 0 then
 823                     call syserr (CRASH, "^a$interrupt: absadr failed.", ME);
 824                return (bit (bin (absaddr, 18), 18));
 825 
 826           end absaddr_18;
 827 
 828 log_this_status:
 829           proc;
 830 
 831 /**** Here is where the status is actually logged.  Status is accumulated, and if it is identical to the previous
 832       status, a count is bumped.  If the count reaches its max, or a different status comes along, the accumulated
 833       status is written to the syserr log. ****/
 834 
 835 dcl       1 test_status          like dte.log_status;
 836 
 837                unspec (test_status) = ""b;                  /* clear everything */
 838                test_status.level = auto_istat.level;
 839                test_status.time_out = auto_istat.time_out;
 840                test_status.channel = rel (ctep);
 841                test_status.status = substr (auto_istat.iom_stat, 1, length (test_status.status));
 842                                                             /* takes only high 36 bits */
 843                if auto_istat.level ^= IO_SPECIAL_INTERRUPT_LEVEL then do;
 844                     test_status.type = second_status_word.action_code;
 845                     test_status.command = addr (dte.idcw) -> idcw.command;
 846                end;
 847                test_status.count = dte.log_status.count;    /* for equality comparision */
 848 /**** See if this status matches a previous one. ****/
 849                if (dte.log_status_cnt & (unspec (test_status) = unspec (dte.log_status))
 850                     & (unspec (dte.detailed_status) = unspec (dte.log_detailed_status))) then do;
 851                                                             /* it matches */
 852                     dte.log_status.count = dte.log_status.count + 1;
 853                     if dte.log_status.count = MAX_LOG_STATUS_COUNT then
 854                          call flush_status_proc;
 855                end;
 856                else do;                                     /* doesn't match */
 857                     call flush_status_proc;
 858                     dte.log_status = test_status;
 859                     dte.log_status.count = 0;               /* print_syserr_msg_ expects actual count - 1 */
 860                     dte.log_status_cnt = "1"b;
 861                     dte.log_detailed_status = dte.detailed_status;
 862                end;
 863 
 864           end log_this_status;
 865 
 866      end log_status_if_appropriate;
 867 ^L
 868 /* Routine to flush the status accumulated so far. */
 869 
 870 flush_status_proc:
 871      proc;
 872 
 873 dcl       1 auto_io_msg          aligned like io_msg;
 874 dcl       msg_length             fixed bin;
 875 dcl       msg_type               fixed bin;
 876 
 877           if ^dte.log_status_cnt then
 878                return;                                      /* nothing to log */
 879 
 880           io_msgp = addr (auto_io_msg);
 881           io_msg.level = bit (dte.log_status.level);
 882           io_msg.device = dte.device;
 883           io_msg.time_out = dte.log_status.time_out;
 884           io_msg.type = dte.log_status.type;
 885           io_msg.command = dte.log_status.command;
 886           io_msg.count = bit (dte.log_status.count);
 887           io_msg.channel = ptr (dtep, dte.log_status.channel) -> cte.chanid;
 888           io_msg.status = dte.log_status.status;
 889           io_msg.devname = ptr (dtep, dte.gtep) -> gte.name;
 890           if unspec (dte.log_detailed_status) ^= ""b then do;
 891                io_msg.detailed_status = substr (unspec (dte.log_detailed_status), 1, length (io_msg.detailed_status));
 892                msg_type = SB_io_err_detail;
 893                msg_length = SBL_io_err_detail;
 894           end;
 895           else do;
 896                msg_type = SB_io_err;
 897                msg_length = SBL_io_err;
 898           end;
 899 
 900           call syserr$binary (JUST_LOG, io_msgp, msg_type, msg_length, "^a$interrupt: ^[I/O error^;Special^].", ME,
 901                bin (io_msg.level) ^= IO_SPECIAL_INTERRUPT_LEVEL);
 902           dte.last_log_time = clock ();
 903           dte.log_status_cnt = "0"b;
 904 
 905      end flush_status_proc;
 906 ^L
 907 special_could_come_from_channel:
 908      proc (gtp, ctp) returns (bit (1) aligned);
 909 
 910 dcl       ctp                    ptr parameter;
 911 dcl       gtp                    ptr parameter;
 912 
 913 dcl       tctp                   ptr;
 914 
 915           do tctp = ptr (gtp, gtp -> gte.ctep) repeat ptr (tctp, tctp -> cte.next_ctep) while (rel (tctp) ^= ""b);
 916                if tctp -> cte.base_ctep = ctp -> cte.base_ctep then
 917                     return ("1"b);
 918           end;
 919           return ("0"b);
 920 
 921      end special_could_come_from_channel;
 922 ^L
 923 /* Routines which handle the masking and unmasking of interrupts, and the locking and unlocking of the gte. */
 924 
 925 mask:
 926      proc;
 927 
 928           call pmut$wire_and_mask (wm_mask, wm_ptwp);
 929 
 930      end mask;
 931 
 932 unmask:
 933      proc;
 934 
 935           call pmut$unwire_unmask (wm_mask, wm_ptwp);
 936 
 937      end unmask;
 938 
 939 lock_gte:
 940      proc;
 941 
 942           if gte.lock = pds$process_id then
 943                call syserr (CRASH, "^a: Mylock error on subsystem ^a.", ME, gte.name);
 944 
 945           do while (^stac (addr (gte.lock), pds$process_id));
 946                                                             /* seize the loop lock */
 947           end;
 948 
 949      end lock_gte;
 950 
 951 unlock_gte:
 952      proc;
 953 
 954           if ^stacq (gte.lock, ""b, pds$process_id) then
 955                call syserr (CRASH, "^a: Lock for subsystem ^a not locked to process ^w.", ME, gte.name, pds$process_id);
 956 
 957      end unlock_gte;
 958 ^L
 959 %include ioi_data;
 960 %page;
 961 %include io_manager_dcls;
 962 %page;
 963 %include interrupt_levels;
 964 %page;
 965 %include ioi_stat;
 966 %page;
 967 %include io_status_entry;
 968 %page;
 969 %include io_special_status;
 970 %page;
 971 %include io_log_status_info;
 972 %page;
 973 %include iom_pcw;
 974 %include iom_dcw;
 975 %page;
 976 %include io_syserr_msg;
 977 %page;
 978 %include syserr_binary_def;
 979 %page;
 980 %include syserr_constants;
 981 %page;
 982 /*        BEGIN MESSAGE DOCUMENTATION
 983 
 984 
 985    Message:
 986    ioi_masked$interrupt: I/O error.
 987 
 988    S:     $log
 989 
 990    T:     $run
 991 
 992    M:     An error, or accumulation of like errors, has occured.  All
 993    needed information is contained in the binary portion of the entry.
 994 
 995    A:     $ignore
 996 
 997    Message:
 998    ioi_masked$interrupt: Interrupt for inactive device (device DEVID).
 999    Type go to continue.
1000 
1001    S:     $crash
1002 
1003    T:     $run
1004 
1005    M:     An interrupt has been received over a channel for which the
1006    device is not marked "active".  This could indicate an error in the I/O
1007    hardware or in the setting/checking of the device active flag.
1008 
1009    A:     Typing "go" at BCE will cause Multics to be reentered and
1010    ioi_masked to dismiss this error and properly cleanup.
1011 
1012    Message:
1013    ioi_masked$interrupt: Special.
1014 
1015    S:     $log
1016 
1017    T:     $run
1018 
1019    M:     A special interrupt, or accumulation of special interrupts, has
1020    occured.  All needed information is contained in the binary portion of the
1021    entry.
1022 
1023    A:     $ignore
1024 
1025    Message:
1026    ioi_masked$interrupt: absadr failed.
1027 
1028    S:     $crash
1029 
1030    T:     $run
1031 
1032    M:     A call to the absadr function returned a non-zero error code,
1033    indicating that the absolute address of the workspace could not be obtained.
1034    Since the workspace is wired, this error should never have occurred.
1035 
1036    A:     $inform
1037    $recover
1038 
1039    Message:
1040    ioi_masked$timer: Attempt to unwire NULL workspace. (device DEVID).
1041 
1042    S:     $info
1043 
1044    T:     $run
1045 
1046    M:     Device entry flag and time stamp indicated that its workspace
1047    required unwiring.  However the pointer to the ASTE for the workspace was
1048    null.
1049    $err
1050 
1051    A:     $inform
1052    $recover
1053 
1054    Message:
1055    ioi_masked: Channel CHANID not responding, will remain masked.
1056 
1057    S:     $info
1058 
1059    T:     $run
1060 
1061    M:     The time limit has expired waiting for status from a previous
1062    unmask connect (reset-status idcw). It is apparent that the channel is
1063    inoperative. No further attempt will be made to re-open the channel.
1064 
1065    A:     Contact your Customer Service Account Representative if the
1066    errors persist.
1067 
1068    Message:
1069    ioi_masked$timer: Timeout on channel CHANID (device DEVID).
1070 
1071    S:     $info
1072 
1073    T:     $run
1074 
1075    M:     The time limit has expired waiting for status from CHANID for a
1076    previous connect.  The channel will be masked "OFF", then unmasked if
1077    multiple devices exist (i.e. tapes and disks) so that special interrupts
1078    can be received.
1079 
1080    A:     Contact your Customer Service Account Representative if the
1081    errors persist.
1082 
1083    Message:
1084    ioi_masked$timer: Timeout on channel CHANID (no device).
1085 
1086    S:     $info
1087 
1088    T:     $run
1089 
1090    M:     The time limit has expired waiting for status from CHANID for
1091    the connect to unmask the channel.  The channel will be masked "OFF", then
1092    then unmask will be tried again.
1093 
1094    A:     Contact your Customer Service Account Representative if the
1095    errors persist.
1096 
1097    Message:
1098    ioi_masked: Lock for subsystem SUBSYSTEM not locked to process OOOOOO.
1099 
1100    S:     $crash
1101 
1102    T:     $run
1103 
1104    M:     An unlock of the subsystem lock was attempted, but is was not
1105    locked by this process.
1106    $err
1107 
1108    A:     $inform
1109    $recover
1110 
1111    Message:
1112    ioi_masked: Mylock error on subsystem SUBSYSTEM.
1113 
1114    S:     $crash
1115 
1116    T:     $run
1117 
1118    M:     An lock of the subsystem lock was attempted, but is was already
1119    locked by this process.
1120    $err
1121 
1122    A:     $inform
1123    $recover
1124 
1125    Message:
1126    ioi_masked: No Ext. Stat. with MAJOR/SUB status on chnl CHANID (DEVID).
1127 
1128    S:     $log
1129 
1130    T:     $run
1131 
1132    M:     An error status has occured that requires detailed status.  This
1133    CHANID should have supplied this with the status, but did not.  This
1134    CHANID is also not capable of requesting the detailed status.  An octal
1135    dump of the status_entry area will be displayed along with the error
1136    message.
1137 
1138    A:     $ignore
1139 
1140    END MESSAGE DOCUMENTATION */
1141 
1142      end ioi_masked;