1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) Honeywell Bull Inc., 1987                *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   6         *                                                         *
   7         * Copyright (c) 1972 by Massachusetts Institute of        *
   8         * Technology and Honeywell Information Systems, Inc.      *
   9         *                                                         *
  10         *********************************************************** */
  11 
  12 
  13 
  14 /****^  HISTORY COMMENTS:
  15   1) change(86-06-05,GJohnson), approve(86-06-05,MCR7387),
  16      audit(86-06-10,Martinson), install(86-07-11,MR12.0-1091):
  17      Correct error message documentation.
  18                                                    END HISTORY COMMENTS */
  19 
  20 
  21 /* format: style4,delnl,insnl,^ifthendo */
  22 fnp_util:
  23      procedure;
  24 
  25 /* This procedure contains entries called through hphcs_ used to load and dump an
  26    *  FNP. The "wire" entry is used to wire the segment containing the core image so that
  27    *  DIA I/O will be possible; the "load" entry initiates the bootload I/O; the
  28    *  "release" entry releases the aste of the coreimage.
  29    *  The "fdump" entry dumps all of FNP memory into a segment supplied
  30    *  by the caller.
  31    *
  32    *  Written 5/19/76 by Robert S. Coren
  33    *  Modified 10/28/76 by Robert S. Coren to save version number and report core image name
  34    *  Modified 04/15/77 by Robert S. Coren to correct bug in queue buffer freeing
  35    *  Modified 9/27/78 by J. Stern for multiplexing changes
  36    *  Modified 79 May 14 by Art Beattie to get memory size and FNP type from caller for fdump entry.
  37    *  Modified April 1981 by Chris Jones for io_manager conversion
  38    *  Modified February 1982 by C. Hornig for MR10 io_manager.
  39    *  Modified 830714 BIM to remove the release entrypoint. abort is suffcient.
  40    *  Modified 83-12-20 BIM for reconfiguration.
  41    *  Modified 84-05-18 BIM for better maintenance of io_manager_assigned bit.
  42    *  Modified 1984-08-02 BIM for code return from load.
  43    *  Modified 1984-07-26 BIM for paged I/O on loads.
  44    *  Modified 1985-01-29, BIM: fix dump_mpx to leave chn unassigned.
  45    *  Modified 1985-03-12, E. Swenson to fix unitialized timeout flag to
  46    *     prevent spurious timeout errors.
  47 */
  48 
  49 
  50 /* PARAMETERS */
  51 
  52 dcl  a_devx fixed bin;                                      /* ioam release param */
  53 dcl  a_fnp_no fixed bin;
  54 dcl  a_fnp_mem_size fixed bin;                              /* FNP memory size in 1024 18-bit words */
  55 dcl  a_fnp_type fixed bin;
  56 dcl  a_ptr ptr;
  57 dcl  a_load_info_ptr ptr;
  58 dcl  a_count fixed bin;                                     /* number of words to wire */
  59 dcl  a_code fixed bin (35);                                 /* OUTPUT */
  60 dcl  a_ev_chan fixed bin (71);
  61 dcl  a_absadr fixed bin (24);                               /* OUTPUT from wire and info, INPUT to load */
  62 dcl  a_version char (4);                                    /* INPUT MCS version number */
  63 dcl  a_image_name char (168);                               /* INPUT core image name */
  64 dcl  norm_int_cell fixed bin;                               /* OUTPUT */
  65 dcl  emergency_int_cell fixed bin;                          /* OUTPUT */
  66 dcl  a_ints bit (2) aligned;                                /* OUTPUT rcvd interrupts */
  67 dcl  a_level fixed bin (3);                                 /* INPUT level on which to interrupt fnp */
  68 dcl  a_fnp_addr fixed bin (15);                             /* INPUT data address in fnp */
  69 
  70 /* AUTOMATIC */
  71 
  72 dcl  auto_absadr fixed bin (24);
  73 dcl  dia_timeout bit (1) aligned;
  74 dcl  fnp_no fixed bin;
  75 dcl  fnp_mem_size fixed bin;
  76 dcl  fnp_type fixed bin;
  77 dcl  fnp_addr fixed bin (15);
  78 dcl  chanid char (8) aligned;
  79 dcl  opcode bit (6) aligned;
  80 dcl  temp_fnp_name char (32);
  81 dcl  fnp_size fixed bin;
  82 dcl  segptr ptr;
  83 dcl  nwords fixed bin;
  84 dcl  code fixed bin (35);
  85 dcl  iom_channel_number fixed bin (7);
  86 dcl  devx fixed bin;
  87 dcl  i fixed bin;
  88 dcl  data_ptr ptr;
  89 dcl  offset fixed bin;
  90 dcl  fnp_tally fixed bin;
  91 dcl  start_time fixed bin (71);
  92 dcl  version char (4);
  93 dcl  cur_la_no bit (3);
  94 dcl  cur_slot_no fixed bin (6);
  95 dcl  load_info_ptr ptr;
  96 dcl  locked bit (1) aligned init ("0"b);
  97 dcl  config_locked bit (1) aligned init ("0"b);
  98 
  99 declare  message fixed bin (71);
 100 declare  1 auto_fnp_msg aligned like fnp_msg;
 101 
 102 /* BUILTINS */
 103 
 104 dcl  (addr, addrel, baseno, bit, clock, fixed, min, null, ptr, string, substr) builtin;
 105 
 106 
 107 /* ENTRIES */
 108 
 109 dcl  config_$find_2 entry (character (4) aligned, character (4) aligned, pointer);
 110 dcl  fnp_multiplexer$assign entry (fixed binary, fixed binary (35));
 111 dcl  fnp_multiplexer$unassign entry (fixed binary, fixed binary (35));
 112 dcl  fnp_multiplexer$fnp_lock entry (pointer, fixed binary (35));
 113 dcl  fnp_multiplexer$fnp_unlock entry (pointer);
 114 dcl  get_fnp_name_ entry (fixed bin) returns (char (32));
 115 dcl  grab_aste$grab_aste_io entry (ptr, fixed bin, fixed bin (35)) returns (ptr);
 116 dcl  grab_aste$release_io entry (ptr);
 117 dcl  ioam_$assign entry (fixed bin, entry, fixed bin (35));
 118 dcl  ioam_$unassign entry (fixed bin, fixed bin (35));
 119 dcl  priv_channel_manager$get_devx entry (char (*), fixed bin, fixed bin (35));
 120 dcl  dn355_util$compute_parity entry (bit (36)) returns (bit (36));
 121 dcl  absadr entry (ptr, fixed bin (35)) returns (fixed bin (24));
 122 dcl  ioi_page_table$get entry (fixed binary (19), fixed binary, fixed binary (35));
 123 dcl  ioi_page_table$fill entry (fixed binary, pointer, fixed binary (35));
 124 dcl  ioi_page_table$ptx_to_ptp entry (fixed binary) returns (pointer);
 125 dcl  ioi_page_table$put entry (fixed binary, fixed binary (35));
 126 dcl  pxss$notify entry (fixed bin);
 127 dcl  pxss$addevent entry (fixed bin);
 128 dcl  pxss$delevent entry (fixed bin);
 129 dcl  pxss$wait entry;
 130 dcl  pxss$ring_0_wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));
 131 dcl  get_ptrs_$given_segno entry (fixed bin) returns (ptr);
 132 dcl  dn355$interrupt entry;
 133 dcl  syserr entry options (variable);
 134 dcl  syserr$error_code entry options (variable);
 135 dcl  lock$wait entry (ptr, char (4) aligned, fixed bin (35));
 136 dcl  lock$lock_fast entry (pointer);
 137 dcl  lock$unlock_fast entry (pointer);
 138 dcl  lock$unlock entry (ptr, char (4) aligned);
 139 dcl  (
 140      pc_abs$wire_abs,
 141      pc_abs$wire_abs_contig
 142      ) entry (ptr, fixed bin, fixed bin, fixed bin (35));
 143 dcl  pc_abs$unwire_abs entry (ptr, fixed bin, fixed bin);
 144 
 145 /* EXTERNAL STATIC */
 146 
 147 dcl  (
 148      error_table_$io_no_permission,
 149      error_table_$invalid_state,
 150      error_table_$bad_mpx_load_data,
 151      error_table_$no_io_interrupt,
 152      error_table_$rqover,
 153      error_table_$bad_segment,
 154      error_table_$noalloc,
 155      error_table_$action_not_performed,
 156      error_table_$io_configured,
 157      error_table_$io_not_configured,
 158      error_table_$io_assigned,
 159      error_table_$io_not_assigned,
 160      error_table_$io_not_defined
 161      ) fixed bin (35) ext static;
 162 
 163 dcl  pds$processid bit (36) ext static;
 164 dcl  pds$process_group_id char (32) ext static;
 165 dcl  tc_data$initializer_id bit (36) aligned external static;
 166 dcl  tty_buf$fnp_config_flags (1:8) bit (1) unaligned external static;
 167 dcl  tty_buf$lct_ptr pointer external static;
 168 dcl  sst$astsize fixed bin external;
 169 dcl  sys_info$page_size fixed bin external;
 170 dcl  sys_info$seg_size_256K fixed bin (19) external;
 171 
 172 /* INTERNAL STATIC */
 173 
 174 dcl  (
 175      FNP_DOWN init (2)
 176      ) fixed bin int static options (constant);
 177 
 178 dcl  fdump_seg_event char (4) aligned init ("fnpd") static options (constant);
 179 dcl  cleanup condition;
 180 dcl  record_quota_overflow condition;
 181 
 182 
 183 /* BASED */
 184 
 185 dcl  tally_words (fnp_tally) bit (36) aligned based;
 186 
 187 dcl  1 dump_355_control aligned based (data_ptr),           /* word used to supply 355 address and tally for fdump */
 188        2 address_mode bit (3) unaligned,                    /* always 36-bit addressing ("001"b) */
 189        2 fnp_address bit (15) unaligned,
 190        2 tally fixed bin (17) unaligned;
 191 
 192 dcl  1 dump_6670_control aligned based (data_ptr),          /* word used to supply DN6670 address and tally for fdump */
 193        2 fnp_address fixed bin (18) unsigned unaligned,
 194        2 unpaged bit (1) unaligned,
 195        2 mbz bit (5) unaligned,
 196        2 tally fixed bin (12) unsigned unaligned;
 197 
 198 dcl  1 load_info aligned based (load_info_ptr),             /* used to assign baud rates and line types at bootload time */
 199        2 no_entries fixed bin,
 200        2 entries (0 refer (load_info.no_entries)),
 201          3 devx fixed bin,
 202          3 baud_rate fixed bin,
 203          3 line_type fixed bin;
 204 ^L
 205 
 206 
 207 wire:
 208      entry (a_fnp_no, a_ptr, a_absadr, a_count, a_code);
 209 
 210 /* entry called to wire a_count words for segment at a_ptr */
 211 /* This does not require lock protection. If we miss a deconfig, tough */
 212 
 213           fnp_no = a_fnp_no;                                /* copy args */
 214           segptr = a_ptr;
 215           nwords = a_count;
 216 
 217           call validate_fnp_ret ("wire");
 218           call validate_assigned_ret ("wire");
 219 
 220           if fnp_info.wired                                 /* Someone did it already? */
 221           then do;
 222                a_code = 0;                                  /* be gracious */
 223                if datanet_info.trace
 224                then call syserr (ANNOUNCE, "fnp_util$wire: FNP ^a already wired.", fnp_info.fnp_tag);
 225                                                             /* but note for debugging */
 226                return;
 227           end;
 228 
 229           astep = grab_aste$grab_aste_io (segptr, nwords, code);
 230                                                             /* make segment stay active */
 231 
 232           if code ^= 0
 233           then go to wire_error_return;
 234 
 235           fnp_info.astep = astep;
 236           fnp_info.n_pages_wired = divide (nwords + sys_info$page_size - 1, sys_info$page_size, 17, 0);
 237 
 238           call pc_abs$wire_abs (astep, 0, fnp_info.n_pages_wired, code);
 239                                                             /* no contig -- we will use a page table! */
 240           if code ^= 0
 241           then do;
 242                call grab_aste$release_io (astep);           /* couldn't wire, so let aste go */
 243                go to wire_error_return;
 244           end;
 245           call fill_bootload_page_table;                    /* sets the variable absadr */
 246           if datanet_info.trace
 247           then call syserr (ANNOUNCE, "fnp_util$wire: seg: ^p n_pages: ^d astep ^p", segptr, fnp_info.n_pages_wired,
 248                     astep);
 249 
 250           a_absadr = auto_absadr;                           /* relative to page table its 0 */
 251           fnp_info.wired = "1"b;
 252 
 253 wire_error_return:
 254           if code ^= 0 & (datanet_info.trace | datanet_info.debug_stop)
 255           then do;
 256                call syserr$error_code (ANNOUNCE, code, "fnp_util$wire: failed.");
 257                call STOP_CHECK ("wire");
 258           end;
 259           a_code = code;
 260           return;
 261 ^L
 262 
 263 load:
 264      entry (a_fnp_no, a_absadr, a_ev_chan, a_version, a_image_name, a_fnp_mem_size, a_load_info_ptr, a_code);
 265 
 266 /* entry to initiate bootload I/O for loading an FNP */
 267 /* This cannot lock until it has the dump seg wired */
 268 
 269           fnp_no = a_fnp_no;
 270           fnp_mem_size = a_fnp_mem_size;
 271           auto_absadr = a_absadr;
 272           call validate_fnp_ret ("load");
 273           call validate_assigned_ret ("load");
 274 
 275           if datanet_info.trace
 276           then call syserr (ANNOUNCE, "fnp_util$load: FNP ^d ADDR ^o", fnp_no, auto_absadr);
 277 
 278           if fnp_info.bootloading
 279           then do;                                          /* we can't do it while it's already happening */
 280                code = error_table_$invalid_state;
 281                call syserr (ANNOUNCE, "fnp_util$load: FNP load already in progress for FNP ^a.", fnp_info.fnp_tag);
 282                go to load_return;
 283           end;
 284 
 285           if ^fnp_info.wired
 286           then do;
 287                code = error_table_$invalid_state;
 288                if datanet_info.trace
 289                then call syserr (ANNOUNCE, "fnp_util$load: FNP ^a not wired.", fnp_info.fnp_tag);
 290                go to load_return;
 291           end;
 292 
 293           if fnp_info.running
 294           then do;
 295                code = error_table_$invalid_state;
 296                if datanet_info.trace
 297                then call syserr (ANNOUNCE, "fnp_util$load: FNP ^a is running.", fnp_info.fnp_tag);
 298                go to load_return;
 299           end;
 300 
 301           if fnp_info.t_and_d_in_progress
 302           then do;
 303                code = error_table_$invalid_state;
 304                if datanet_info.trace
 305                then call syserr (ANNOUNCE, "fnp_util$load: FNP ^a is running T&D.", fnp_info.fnp_tag);
 306                go to load_return;
 307           end;
 308 
 309 /* process the load_info data */
 310 
 311           load_info_ptr = a_load_info_ptr;
 312           if load_info.no_entries ^= fnp_info.no_of_channels
 313           then do;
 314                code = error_table_$bad_mpx_load_data;
 315                go to load_return;
 316           end;
 317 
 318           cur_la_no = "7"b3;                                /* no current line adaptor */
 319           n_pcbs = load_info.no_entries;
 320           do i = 1 to load_info.no_entries;                 /* get baud rates to store in pcbs */
 321                pcbp = addr (fnp_info.pcb_array_ptr -> pcb_array (i));
 322                if load_info (i).devx ^= pcb.devx            /* load_info does not match pcb_array */
 323                then do;
 324                     code = error_table_$bad_mpx_load_data;
 325                     go to load_return;
 326                end;
 327                pcb.baud_rate = load_info (i).baud_rate;
 328                pcb.line_type = load_info (i).line_type;
 329 
 330                if ^pcb.is_hsla                              /* this pcb is for an lsla subchannel */
 331                then do;                                     /* must figure out its slot number */
 332                     if pcb.la_no ^= cur_la_no               /* starting a new line adaptor */
 333                     then do;
 334                          cur_la_no = pcb.la_no;
 335                          cur_slot_no = 1;
 336                     end;
 337 
 338                     pcb.slot_no = bit (cur_slot_no, 6);
 339                     if pcb.baud_rate <= 110
 340                     then cur_slot_no = cur_slot_no + 1;     /* this subchan uses 1 time slot */
 341                     else if pcb.baud_rate <= 150
 342                     then cur_slot_no = cur_slot_no + 2;     /* this subchan uses 2 time slots */
 343                     else cur_slot_no = cur_slot_no + 3;     /* this subchan uses 3 time slots */
 344                end;
 345           end;
 346 
 347 /* it's okay to try to load */
 348 
 349           call lock_fnp;
 350           if ^fnp_info.io_manager_assigned                  /* lost race with deconfig */
 351           then do;                                          /* or even T&D */
 352                call unlock_fnp;
 353                code = error_table_$io_not_assigned;         /* perhaps deconfigured, but ... */
 354                go to load_return;
 355           end;
 356           version = a_version;
 357           call syserr (ANNOUNCE, "Loading FNP ^a, ^a ^a", fnp_info.fnp_tag, a_image_name, version);
 358 
 359           fnp_info.boot_ev_chan = a_ev_chan;
 360           fnp_info.boot_process_id = pds$processid;
 361           fnp_info.version = version;
 362           fnp_info.fnp_mem_size = fnp_mem_size * 1024;
 363           fnp_info.bootloading = "1"b;
 364           datanet_mbx.mailbox_requests = 0;                 /* clear fields in mbx for this load */
 365           datanet_mbx.term_inpt_mpx_wd = "0"b;
 366           datanet_mbx.last_mbx_req_count = 0;
 367           string (datanet_mbx.mbx_used_flags) = "0"b;
 368 
 369           datanet_mbx.crash_data.fault_code = 0;
 370           datanet_mbx.crash_data.ic = 0;
 371           datanet_mbx.crash_data.iom_fault_status = 0;
 372           datanet_mbx.crash_data.fault_word = 0;
 373 
 374           auto_absadr = sys_info$page_size * FIRST_BOOTLOAD_PAGEX;
 375 
 376           call mask_dia;                                    /* Mask the IOM channel  to initialize it */
 377           call connect_to_dia_paged (auto_absadr, 0, "72"b3, dia_timeout);
 378                                                             /* 72 (8), bootload command */
 379 
 380           if dia_timeout
 381           then code = error_table_$no_io_interrupt;
 382           else code = 0;                                    /* all is well so far */
 383           call unlock_fnp;                                  /* however, deconfigure has a problem if bootloading is on! */
 384 
 385 load_return:
 386           if code ^= 0 & (datanet_info.trace | datanet_info.debug_stop)
 387           then do;
 388                call syserr$error_code (ANNOUNCE, code, "fnp_util$load: Error loading FNP ^a.", fnp_info.fnp_tag);
 389                call STOP_CHECK ("load");
 390           end;
 391           a_code = code;
 392           return;
 393 ^L
 394 
 395 info:
 396      entry (a_fnp_no, a_absadr, norm_int_cell, emergency_int_cell, a_code);
 397 
 398 /* entry called to return address of mailbox area and FNP interrupt cells */
 399 
 400           fnp_no = a_fnp_no;
 401           call validate_fnp_ret ("info");
 402 
 403           a_absadr = absadr (fnp_info.mbx_pt, code);
 404           if code = 0
 405           then do;                                          /* provided everything's ok */
 406                norm_int_cell = 3;                           /* customary assignments */
 407                emergency_int_cell = 7;
 408           end;
 409 
 410           a_code = code;
 411           return;
 412 ^L
 413 fill_page_table:
 414      entry (a_fnp_no, a_code);
 415 
 416           fnp_no = a_fnp_no;
 417           call validate_fnp_ret ("fill_page_table");
 418           call validate_assigned_ret ("fill_page_table");
 419           if fnp_info.ptx ^= -1 | fnp_info.ptp ^= null ()
 420           then do;
 421                call syserr (ANNOUNCE, "fnp_util$fill_page_table: page table already assigned for FNP ^a.",
 422                     fnp_info.fnp_tag);
 423                call STOP_CHECK ("fill_page_table");
 424           end;
 425           call get_page_table (code);
 426           if code ^= 0 & (datanet_info.trace | datanet_info.debug_stop)
 427           then do;
 428                call syserr$error_code (ANNOUNCE, code, "fnp_util$fill_page_table: ioi_page_table$get failed for FNP ^a.",
 429                     fnp_info.fnp_tag);
 430                call STOP_CHECK ("fill_page_table");
 431           end;
 432           a_code = code;
 433           return;
 434 
 435 free_page_table:
 436      entry (a_fnp_no, a_code);
 437 
 438           fnp_no = a_fnp_no;
 439           call validate_fnp_ret ("free_page_table");
 440           if fnp_info.ptx = -1
 441           then do;
 442                call syserr (ANNOUNCE, "fnp_util$free_page_table: no page table assigned for FNP ^a.", fnp_info.fnp_tag);
 443                go to ret_bad_code;
 444           end;
 445           call ioi_page_table$put (fnp_info.ptx, code);
 446           fnp_info.ptx = -1;
 447           fnp_info.ptp = null ();
 448           a_code = code;
 449           return;
 450 ^L
 451 
 452 unwire:
 453      entry (a_fnp_no, a_code);
 454 
 455 /* entry called to abort bootload or cleanup from successful bootload */
 456 /* no locking for same reason as wire */
 457 
 458           fnp_no = a_fnp_no;
 459           call validate_fnp_ret ("unwire");
 460 
 461 /* Don't demand assigned so that loads aborted by deconfig can unwire */
 462 
 463           call internal_unwire;
 464           fnp_info.bootloading = "0"b;
 465 
 466           if code ^= 0 & (datanet_info.trace | datanet_info.debug_stop)
 467           then do;
 468                call syserr$error_code (ANNOUNCE, code, "fnp_util$unwire: Unwire failed for FNP ^a.", fnp_info.fnp_tag);
 469                call STOP_CHECK ("unwire");
 470           end;
 471           a_code = code;
 472           return;
 473 ^L
 474 
 475 /* RECONFIGURATION ENTRYPOINTS */
 476 
 477 /* Configure -- add a FNP to the available collection for assignment */
 478 
 479 configure:
 480      entry (a_fnp_no, a_code);
 481 
 482           fnp_no = a_fnp_no;
 483           call validate_fnp_ret ("configure");
 484           call lock_fnp;
 485           if fnp_info.available
 486           then do;
 487                code = error_table_$io_configured;
 488                if datanet_info.trace | datanet_info.debug_stop
 489                then call syserr (ANNOUNCE, "fnp_util$configure: FNP ^a already configured.", fnp_info.fnp_tag);
 490                call STOP_CHECK ("configure");
 491                go to configure_return;
 492           end;
 493           fnp_info.available = "1"b;
 494           fnp_info.io_manager_assigned = "0"b;              /* clean up */
 495           fnp_info.flags = "0"b;                            /* state information */
 496 configure_return:
 497           call unlock_fnp;
 498           if code = 0
 499           then do;
 500                call config_$find_2 ("prph", "fnp" || fnp_info.fnp_tag, prph_fnp_cardp);
 501                prph_fnp_card.state = "on";
 502                call syserr (ANNOUNCE, "fnp_util: FNP ^a added to configuration^[ by ^a^].", fnp_info.fnp_tag,
 503                     pds$processid ^= tc_data$initializer_id, pds$process_group_id);
 504           end;
 505           a_code = code;
 506           return;
 507 
 508 /**** Deconfigure -- remove from configuration.
 509       This entrypoint causes a FNP crash for the selected FNP.
 510 */
 511 
 512 deconfigure:
 513      entry (a_fnp_no, a_code);
 514 
 515           fnp_no = a_fnp_no;
 516           call validate_fnp_ret ("deconfigure");
 517           call lock_fnp;
 518 
 519           if ^fnp_info.available
 520           then do;
 521                code = error_table_$io_not_configured;
 522                go to deconfigure_return;
 523           end;
 524 
 525           fnp_info.available = "0"b;                        /* throttle init_multiplexer */
 526 
 527           if ^fnp_info.io_manager_assigned
 528           then go to deconfigure_return;
 529 
 530           /*** here is the interesting part. What we do depends on who is active */
 531 
 532           if fnp_info.t_and_d_in_progress
 533           then do;
 534                call unlock_fnp;                             /* must lock in correct order */
 535                call release_t_and_d;                        /* unassigns channel */
 536                go to deconfigure_return_unlocked;
 537           end;
 538           else do;
 539                call mask_dia;                               /* stop it from writing to us or sending stuff */
 540                call fnp_multiplexer$unassign (fnp_no, (0)); /* has to succeed, we are under lock */
 541                /*** turns off io_manager_assigned for us, nailing io_manager callers */
 542                auto_fnp_msg.fnp_no = fnp_info.fnp_number;
 543                auto_fnp_msg.state = FNP_DOWN;
 544                auto_fnp_msg.flags = "0"b;
 545                auto_fnp_msg.deconfigured = "1"b;
 546                unspec (message) = unspec (auto_fnp_msg);
 547                call pxss$ring_0_wakeup (fnp_info.boot_process_id, fnp_info.boot_ev_chan, message, (0));
 548           end;
 549 
 550           /*** all callers of io_manager must hold LCTE lock and check io_manager_assigned */
 551 
 552 deconfigure_return:
 553           call unlock_fnp;
 554 deconfigure_return_unlocked:
 555           if code = 0
 556           then do;
 557                call config_$find_2 ("prph", "fnp" || fnp_info.fnp_tag, prph_fnp_cardp);
 558                prph_fnp_card.state = "off";
 559                call syserr (ANNOUNCE, "fnp_util: FNP ^a deleted from configuration^[ by ^a^].", fnp_info.fnp_tag,
 560                     pds$processid ^= tc_data$initializer_id, pds$process_group_id);
 561           end;
 562           a_code = code;
 563           return;
 564 ^L
 565 
 566 
 567 
 568 fdump:
 569      entry (a_fnp_no, a_fnp_type, a_fnp_mem_size, a_ptr, a_code);
 570 
 571 /* entry to read in contents of FNP core for fdump_fnp_.  It is passed a pointer
 572    *  to the segment in which the dump is supposed to be put.  The one-page fnp_dump_seg
 573    *  is wired down, and FNP core is read into it in chunks.
 574 */
 575 
 576 /* Like load, locking must follow wiring of fnp dump seg */
 577 
 578           fnp_no = a_fnp_no;
 579           fnp_type = a_fnp_type;
 580           fnp_mem_size = a_fnp_mem_size;
 581           segptr = a_ptr;
 582 
 583           call validate_fnp_ret ("fdump");
 584 
 585           if fnp_info.running | fnp_info.bootloading | fnp_info.t_and_d_in_progress
 586           then do;
 587                code = error_table_$invalid_state;           /* nope */
 588                go to ret_bad_code;
 589           end;
 590 
 591           fnp_dump_ptr = addr (fnp_dump_seg$);
 592           on cleanup
 593                begin;
 594                if fnp_dump_seg.lock = pds$processid
 595                then call lock$unlock (addr (fnp_dump_seg.lock), fdump_seg_event);
 596           end;
 597           call lock$wait (addr (fnp_dump_seg.lock), fdump_seg_event, code);
 598           if code ^= 0
 599           then go to ret_bad_code;
 600 
 601           call wire_dump_seg;
 602           if code ^= 0
 603           then go to unlock_dump_seg;
 604 
 605           fnp_dump_seg.flags = "0"b;                        /* aggregately */
 606           call assign_interrupt (dump_interrupt, code);     /* we will handle DIA interrupts for this */
 607           if code ^= 0                                      /* checks to make sure we haven't been forcible unassigned */
 608           then go to unwire_buffer;
 609 
 610           data_ptr = addr (fnp_dump_seg.data);
 611           if fnp_type = DN6670
 612           then dump_6670_control.unpaged = "1"b;            /* absolute 36-bit addressing for DIA */
 613           else dump_355_control.address_mode = "001"b;      /* 36-bit addressing for DIA */
 614           fnp_size = fnp_mem_size * 512;                    /* size of FNP core in 36-bit words */
 615 
 616 /* before we start, set up handler for record-quota overflow */
 617 
 618           on record_quota_overflow
 619                begin;
 620                code = error_table_$rqover;
 621                go to restore_interrupts;
 622           end;
 623 
 624 /* loop reading chunks of FNP memory */
 625 
 626           fnp_addr = 0;
 627           do offset = 0 to fnp_size by 1021;                /* 3 words less than a page */
 628                fnp_tally = min (1021, fnp_size - offset);
 629                call setup_dump_ctl_word;
 630                call fdump_seg_io ("fdump", "75"b3, code);   /*  test data xfer */
 631                if code ^= 0
 632                then go to restore_interrupts;
 633 
 634 /* Now copy the data into the permanent seg */
 635 
 636                ptr (segptr, offset) -> tally_words = addr (fnp_dump_seg.data (2)) -> tally_words;
 637                                                             /* word 0 and 1 are control words */
 638           end;
 639 
 640 restore_interrupts:
 641           call unassign_interrupt;
 642 unwire_buffer:
 643           call unwire_dump_seg;                             /* and unwire buffer */
 644 unlock_dump_seg:
 645           call lock$unlock (addr (fnp_dump_seg.lock), fdump_seg_event);
 646 ret_bad_code:
 647           a_code = code;
 648           return;
 649 
 650 
 651 fdump_seg_io:
 652      proc (caller, a_diaop, code);
 653 
 654 dcl  caller char (32);
 655 dcl  a_diaop bit (6) aligned;
 656 dcl  code fixed bin (35);
 657 
 658           start_time = clock ();
 659           if datanet_info.trace
 660           then call syserr (ANNOUNCE, "fnp_util$fdump_seg_io: ^2.3b for ^a.", a_diaop, caller);
 661           call lock_fnp;                                    /* must be locked to connect */
 662           if ^fnp_info.io_manager_assigned
 663           then do;
 664                call unlock_fnp;
 665                code = error_table_$io_not_assigned;
 666                return;
 667           end;
 668 
 669           fnp_dump_seg.fdump = "1"b;                        /* so notify can be done */
 670           call pxss$addevent (tty_ev);
 671           call connect_to_dia (auto_absadr, 0, a_diaop, dia_timeout);
 672           call unlock_fnp;
 673           if ^dia_timeout
 674           then do;
 675                call pxss$wait;                              /* wait for interrupt */
 676                if datanet_info.trace
 677                then call syserr (ANNOUNCE, "fnp_util (fdump_seg_io): returned from first wait.");
 678           end;
 679           else do;
 680                call pxss$delevent (tty_ev);
 681                if datanet_info.trace
 682                then call syserr (ANNOUNCE, "fnp_util (fdump_seg_io): DIA timeout.");
 683                code = error_table_$no_io_interrupt;
 684                return;
 685           end;
 686 
 687           do while (fnp_dump_seg.fdump);                    /* when we get notify, make sure it's the right one */
 688                if clock () - start_time > 5 * 1000 * 1000   /* if it's been more than 5 seconds */
 689                then do;                                     /* punt */
 690                     if datanet_info.trace
 691                     then call syserr (ANNOUNCE, "fnp_util (fdump_seg_io): FNP timeout.");
 692                     code = error_table_$no_io_interrupt;
 693                     return;
 694                end;
 695 
 696                call pxss$addevent (tty_ev);
 697                if fnp_info.t_and_d_in_progress
 698                then if fnp_info.t_and_d_lev_3_occurred | fnp_info.t_and_d_lev_7_occurred
 699                     then do;
 700                          code = 0;
 701                          return;
 702                     end;
 703                if fnp_dump_seg.fdump                        /* it wasn't */
 704                then call pxss$wait;                         /* wait some more */
 705                else call pxss$delevent (tty_ev);            /* otherwise we needn't have added event */
 706           end;
 707           code = 0;
 708      end fdump_seg_io;
 709 
 710 dump_interrupt:
 711      entry (a_fnp_no, a_level, a_dummy);
 712 
 713 /* this is our temporary FNP interrupt handler while doing fdump i/o */
 714 
 715 dcl  a_dummy bit (36) aligned parameter;
 716 
 717           fnp_dump_ptr = addr (fnp_dump_seg$);
 718           infop = addr (dn355_data$);
 719           if datanet_info.trace
 720           then call syserr (ANNOUNCE,
 721                     "fnp_util (dump_interrupt): Interrupt for FNP ^d level ^d. fnp_dump_seg.fdump = ""^b""b", a_fnp_no,
 722                     a_level, fnp_dump_seg.fdump);
 723           if fnp_dump_seg.fdump                             /* if we're really interested in this one */
 724           then do;
 725                fnp_dump_seg.fdump = "0"b;                   /* so we'll recognize notify */
 726                call pxss$notify (tty_ev);
 727           end;
 728           return;
 729 ^L
 730 fnp_tandd_setup:
 731      entry (a_fnp_no, a_ev_chan, a_code);
 732 
 733           fnp_no = a_fnp_no;
 734           call validate_fnp_ret ("fnp_tandd_setup");
 735 
 736 /* Must set up lcte ptr first if not done already, or lock
 737    won't work.  Chicken/egg problem notwithstanding, this will
 738    work, because other guy doing same thing. */
 739 
 740           temp_fnp_name = get_fnp_name_ (fnp_no);
 741           if fnp_info.lcte_ptr = null
 742           then do;
 743                call priv_channel_manager$get_devx (temp_fnp_name, devx, code);
 744                if code ^= 0
 745                then go to ret_bad_code;
 746                fnp_info.lcte_ptr = addr (tty_buf$lct_ptr -> lct.lcte_array (devx));
 747           end;
 748 
 749           call lock_fnp;
 750 
 751           if fnp_info.bootloading | fnp_info.running | fnp_info.t_and_d_in_progress
 752           then do;
 753                code = error_table_$invalid_state;
 754                go to tandd_setup_loses_unlock;
 755           end;
 756 
 757           call fnp_multiplexer$assign (fnp_no, code);
 758           if code ^= 0
 759           then do;
 760                call syserr$error_code (ANNOUNCE, code, "fnp_util: channel assignment failed for FNP ^a.",
 761                     fnp_info.fnp_tag);
 762                go to tandd_setup_loses_unlock;
 763           end;
 764 
 765           call ioam_$assign ((fnp_info.io_manager_chx), fnp_tandd_release_handler, code);
 766           if code ^= 0
 767           then do;
 768                call syserr$error_code (ANNOUNCE, code, "fnp_util: ioam_$assign failed for FNP ^a.", fnp_info.fnp_tag);
 769                go to tandd_setup_loses_unlock;
 770           end;
 771           fnp_info.boot_process_id = pds$processid;
 772           fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 773           call syserr (ANNOUNCE, "fnp_util: assigned FNP ^a to ^a for T & D.", temp_fnp_name, pds$process_group_id);
 774           fnp_info.t_and_d_in_progress = "1"b;
 775 
 776 tandd_setup_loses_unlock:
 777           if code = 0
 778           then fnp_info.boot_ev_chan = a_ev_chan;
 779           call unlock_fnp;
 780           a_code = code;
 781           return;
 782 
 783 fnp_tandd_release_handler:
 784      entry (a_devx, a_code);
 785 
 786           devx = a_devx;
 787           infop = addr (dn355_data$);
 788           fnp_dump_ptr = addr (fnp_dump_seg$);
 789           on cleanup
 790                begin;
 791                if fnp_dump_seg.lock = pds$processid
 792                then call lock$unlock (addr (fnp_dump_seg.lock), fdump_seg_event);
 793           end;
 794           do fnp_no = 1 to datanet_info.no_of_355s;
 795                fnpp = addr (datanet_info.per_datanet (fnp_no));
 796                if devx = fnp_info.io_manager_chx
 797                then if fnp_info.t_and_d_in_progress
 798                     then call release_t_and_d;
 799           end;
 800           return;
 801 
 802 /**** T and D does not have to worry about force deconfiguration,
 803       since it is completely released at deconfigure time.
 804       Validate_fnp_tandd_ret will generate an error code if
 805       deconfiguration has stolen the FNP. */
 806 
 807 
 808 fnp_tandd_detach:
 809      entry (a_fnp_no, a_code);
 810           fnp_no = a_fnp_no;
 811 
 812           call validate_fnp_tandd_ret ("fnp_tandd_detach");
 813           call lock_fnp;
 814           call validate_assigned_ret ("fnp_tandd_detach");
 815 
 816           call mask_dia;
 817 
 818           fnp_info.boot_process_id = "000000000000"b3;
 819           fnp_info.t_and_d_in_progress = "0"b;
 820           fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 821           call ioam_$unassign ((fnp_info.io_manager_chx), (0));
 822           call fnp_multiplexer$unassign (fnp_no, code);
 823           if code ^= 0
 824           then call syserr$error_code (ANNOUNCE, code, "fnp_util: io channel unassignment failed for FNP ^a.",
 825                     fnp_info.fnp_tag);
 826           call syserr (ANNOUNCE, "fnp_util: releasing FNP ^a from ^a", fnp_info.fnp_id.fnp_tag, pds$process_group_id);
 827           call unlock_fnp;
 828           a_code = 0;
 829           return;
 830 
 831 fnp_tandd_mask:
 832      entry (a_fnp_no, a_code);
 833           fnp_no = a_fnp_no;
 834           call validate_fnp_tandd_ret ("fnp_tandd_mask");
 835           call lock_fnp;
 836           call validate_assigned_ret ("fnp_tandd_mask");
 837 
 838           call mask_dia;
 839           call unlock_fnp;
 840           a_code = 0;
 841           return;
 842 
 843 fnp_tandd_send_int:
 844      entry (a_fnp_no, a_level, a_code);
 845           fnp_no = a_fnp_no;
 846           call validate_fnp_tandd_ret ("fnp_tandd_send_int");
 847           call lock_fnp;
 848           call validate_assigned_ret ("fnp_tandd_send_int");
 849 
 850           fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 851           call connect_to_dia (0, a_level, "71"b3, dia_timeout);
 852           call unlock_fnp;
 853           if dia_timeout
 854           then a_code = error_table_$no_io_interrupt;
 855           a_code = 0;
 856           return;
 857 
 858 fnp_tandd_get_ints:
 859      entry (a_fnp_no, a_ints, a_code);
 860           fnp_no = a_fnp_no;
 861           call validate_fnp_tandd_ret ("fnp_tandd_get_ints");
 862           call lock_fnp;
 863           call validate_assigned_ret ("fnp_tandd_get_ints");
 864           a_ints = fnp_info.t_and_d_lev_3_occurred || fnp_info.t_and_d_lev_7_occurred;
 865           fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 866           call unlock_fnp;
 867           a_code = 0;
 868           return;
 869 
 870 fnp_tandd_read:
 871      entry (a_fnp_no, a_ptr, a_count, a_fnp_addr, a_fnp_type, a_code);
 872           opcode = "75"b3;
 873           go to fnp_t_and_d_rw_merge;
 874 
 875 fnp_tandd_write:
 876      entry (a_fnp_no, a_ptr, a_count, a_fnp_addr, a_fnp_type, a_code);
 877           opcode = "76"b3;
 878 fnp_t_and_d_rw_merge:
 879 /**** To avoid LONG delays in deconfiguration, this does not lock
 880       the LCTE lock except around the connects themselves. */
 881           fnp_no = a_fnp_no;
 882           segptr = a_ptr;
 883           nwords = a_count;
 884           fnp_type = a_fnp_type;
 885           fnp_addr = a_fnp_addr;
 886           fnp_dump_ptr = addr (fnp_dump_seg$);
 887           call validate_fnp_tandd_ret ("fnp_tandd_rw");
 888 
 889           on cleanup
 890                begin;
 891                if fnp_dump_seg.lock = pds$processid
 892                then call lock$unlock (addr (fnp_dump_seg.lock), fdump_seg_event);
 893           end;
 894 
 895           data_ptr = addr (fnp_dump_seg.data);
 896 
 897           call lock$wait (addr (fnp_dump_seg.lock), fdump_seg_event, code);
 898           if code ^= 0
 899           then go to ret_bad_code;
 900           if fnp_type = DN6670
 901           then dump_6670_control.unpaged = "1"b;
 902           else dump_355_control.address_mode = "1"b3;
 903           offset = 0;
 904           do while (nwords > 0);
 905                fnp_tally = min (308, nwords);
 906                nwords = nwords - fnp_tally;
 907                call setup_dump_ctl_word;
 908                if opcode = "76"b3                           /* WRITE */
 909                then addr (fnp_dump_seg.data (2)) -> tally_words = addrel (segptr, offset) -> tally_words;
 910                call wire_dump_seg;
 911                if code ^= 0
 912                then go to unlock_dump_seg;
 913                fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 914                fnp_info.t_and_d_notify_requested = "1"b;
 915 
 916                call fdump_seg_io ("fnp_tandd_rw", opcode, code);
 917 
 918                fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
 919                call unwire_dump_seg;
 920                if code ^= 0
 921                then go to unlock_dump_seg;
 922                if opcode = "75"b3                           /* READ */
 923                then addrel (segptr, offset) -> tally_words = addr (fnp_dump_seg.data (2)) -> tally_words;
 924 
 925                offset = offset + fnp_tally;
 926           end;
 927           code = 0;
 928           go to unlock_dump_seg;
 929 ^L
 930 
 931 validate_assigned_ret:
 932      procedure (caller);
 933 
 934 declare  caller char (32);
 935 
 936           if ^fnp_info.io_manager_assigned
 937           then do;
 938                code = error_table_$io_not_assigned;
 939                if datanet_info.trace | datanet_info.debug_stop
 940                then call syserr$error_code (ANNOUNCE, code,
 941                          "fnp_util$validate_assigned_ret (^a): IO manager assignment lacking.", caller);
 942                call STOP_CHECK ("validate_assigned_ret");
 943                call unlock_fnp;
 944                go to ret_bad_code;
 945           end;
 946           return;
 947      end validate_assigned_ret;
 948 
 949 
 950 validate_fnp_ret:
 951      procedure (caller);
 952 
 953 declare  caller char (32);
 954 
 955 /* internal procedure to ensure that FNP number is reasonable */
 956 
 957           infop = addr (dn355_data$);
 958 
 959           if fnp_no <= 0 | fnp_no > max_no_355s
 960           then do;
 961                code = error_table_$io_not_defined;
 962 ERROR:
 963                if datanet_info.trace | datanet_info.debug_stop
 964                then call syserr$error_code (ANNOUNCE, code, "fnp_util$^a: Invalid call for FNP ^d.", caller, fnp_no);
 965                call STOP_CHECK (caller);
 966                go to ret_bad_code;
 967           end;
 968 
 969           if ^tty_buf$fnp_config_flags (fnp_no)
 970           then do;
 971                fnpp = null;
 972                code = error_table_$io_not_defined;
 973                go to ERROR;
 974           end;
 975 
 976           code = 0;
 977           fnpp = addr (datanet_info.per_datanet (fnp_no));
 978           mbxp = fnp_info.mbx_pt;
 979           if datanet_info.trace
 980           then call syserr (ANNOUNCE, "fnp_util$^a: Tracing call for fnp ^a.", caller, fnp_info.fnp_tag);
 981 
 982           return;
 983 
 984      end validate_fnp_ret;
 985 ^L
 986 
 987 
 988 validate_fnp_tandd_ret:
 989      proc (caller);
 990 
 991 declare  caller char (32);
 992 
 993           call validate_fnp_ret (caller);
 994           if ^fnp_info.t_and_d_in_progress
 995           then code = error_table_$invalid_state;
 996           else if fnp_info.boot_process_id ^= pds$processid
 997           then code = error_table_$io_no_permission;
 998           if code ^= 0
 999           then do;
1000                if (datanet_info.trace | datanet_info.debug_stop)
1001                then call syserr$error_code (ANNOUNCE, code, "fnp_util$^a: Invalid call.", caller);
1002                go to ret_bad_code;
1003           end;
1004           return;                                           /* validate_fnp_ret did trace */
1005      end validate_fnp_tandd_ret;
1006 ^L
1007 setup_dump_ctl_word:
1008      proc;
1009 
1010           if fnp_type = DN6670
1011           then do;
1012                dump_6670_control.fnp_address = fnp_addr + 2 * offset;
1013                dump_6670_control.tally = fnp_tally;
1014           end;
1015           else do;
1016                dump_355_control.fnp_address = bit (fixed (fnp_addr + 2 * offset, 15), 15);
1017                dump_355_control.tally = fnp_tally;
1018           end;
1019      end;
1020 
1021 wire_dump_seg:
1022      proc;
1023 
1024           astep = get_ptrs_$given_segno (fixed (baseno (fnp_dump_ptr), 17));
1025           call pc_abs$wire_abs_contig (astep, 0, 1, code);  /* wire a page */
1026           if code = 0
1027           then auto_absadr = absadr (addr (fnp_dump_seg.data), code);
1028                                                             /* fdump_seg_io needs this */
1029      end;
1030 
1031 unwire_dump_seg:
1032      proc;
1033 
1034           astep = get_ptrs_$given_segno (fixed (baseno (fnp_dump_ptr), 17));
1035           call pc_abs$unwire_abs (astep, 0, 1);
1036           return;
1037      end unwire_dump_seg;
1038 
1039 
1040 assign_interrupt:
1041      proc (handler, code);
1042 
1043 /* internal procedure to set handler for IOM interrupt to our proc. */
1044 
1045 dcl  handler entry;
1046 
1047 dcl  iom_code fixed bin (35);
1048 dcl  code fixed bin (35);
1049 
1050           call lock_fnp;                                    /* discourage reconfiguration */
1051           if ^fnp_info.io_manager_assigned                  /* already deconfigured?  */
1052           then if ^fnp_info.available                       /* already deconfigured! */
1053                then do;
1054                     code = error_table_$io_not_configured;
1055                     return;
1056                end;
1057 
1058 /**** It may already be unassigned, as in dump of down MPX */
1059 
1060           if fnp_info.io_manager_assigned
1061           then do;
1062                call io_manager$unassign (fnp_info.io_manager_chx, code);
1063                if code ^= 0
1064                then call syserr$error_code (CRASH, code, "fnp_util: Could not unassign FNP ^a at reassign_interrupt.",
1065                          fnp_info.fnp_tag);
1066                fnp_info.io_manager_assigned = "0"b;
1067           end;                                              /* now make new assignment */
1068 
1069           call io_manager$assign (fnp_info.io_manager_chx, fnp_info.io_chanid, handler, (fnp_no), (null ()), iom_code);
1070           call unlock_fnp;
1071           if code = 0                                       /* if there wasn't anything more interesting to report */
1072           then do;
1073                code = iom_code;                             /* then report this */
1074                fnp_info.io_manager_assigned = "1"b;
1075           end;
1076 
1077           return;
1078 
1079      end assign_interrupt;
1080 
1081 unassign_interrupt:
1082      procedure;
1083 
1084           call io_manager$unassign (fnp_info.io_manager_chx, (0));
1085           fnp_info.io_manager_assigned = "0"b;
1086           return;
1087      end unassign_interrupt;
1088 ^L
1089 
1090 
1091 connect_to_dia:
1092      proc (address, level, cmd, timeout);
1093 
1094 /* internal procedure to do DIA i/o */
1095 
1096 dcl  old_pcw bit (36) aligned;
1097 dcl  timeout bit (1) aligned;
1098 dcl  address fixed bin (24);
1099 dcl  level fixed bin (3);
1100 dcl  cmd bit (6) aligned;
1101 dcl  1 ima aligned like io_manager_arg;
1102 dcl  paged bit (1) aligned;
1103 dcl  1 a_dia_pcw aligned based (mbxp),                      /* better declaration than the one used when MCS is running */
1104        2 address fixed bin (18) unsigned unaligned,
1105        2 error bit (1) unaligned,
1106        2 pad1 bit (3) unaligned,
1107        2 parity bit (1) unaligned,
1108        2 pad2 bit (1) unaligned,
1109        2 pad3 bit (3) unaligned,                            /* if we used address extension this would be important */
1110        2 interrupt_level fixed bin (3) unsigned unaligned,
1111        2 command bit (6) unaligned;
1112 
1113           paged = "0"b;
1114           go to common;
1115 
1116 connect_to_dia_paged:
1117      entry (address, level, cmd, timeout);
1118 
1119           paged = "1"b;
1120 
1121 common:
1122           unspec (a_dia_pcw) = ""b;
1123           if address > sys_info$seg_size_256K
1124           then call syserr (CRASH, "fnp_util (connect_to_dia): address > 256K");
1125           a_dia_pcw.address = address;
1126           a_dia_pcw.interrupt_level = level;
1127           a_dia_pcw.command = cmd;
1128           if datanet_info.trace | datanet_info.debug_stop
1129           then call syserr (ANNOUNCE, "fnp_util (connect): ^[ paged PTP ^p^;^s^] PCW ^w", paged, fnp_info.ptp,
1130                     unspec (datanet_mbx.dia_pcw));
1131           if datanet_info.debug_stop
1132           then do;
1133                call syserr (ANNOUNCE, "fnp_util: stop before connect.");
1134                call syserr (CRASH, "  ptp: ^p astep: ^p", fnp_info.ptp, fnp_info.astep);
1135           end;
1136 
1137 /* parity on pcw REQUIRED */
1138 
1139           string (datanet_mbx.dia_pcw) = dn355_util$compute_parity (string (datanet_mbx.dia_pcw));
1140 
1141           ima.chx = fnp_info.io_manager_chx;
1142           if paged
1143           then ima.ptp = fnp_info.ptp;
1144           else ima.ptp = null ();
1145           old_pcw = unspec (a_dia_pcw);
1146           call io_manager$connect_direct (ima);
1147           do i = 1 to 100000 while (unspec (a_dia_pcw) = old_pcw);
1148           end;
1149           if a_dia_pcw.error | old_pcw = unspec (a_dia_pcw)
1150           then do;
1151                call syserr (ANNOUNCE,
1152                     "fnp_util (connect_to_dia): DIA mailbox PCW ^[error^;timeout^] for channel ^a, FNP ^a. PCW was ^w.",
1153                     a_dia_pcw.error, fnp_info.io_chanid, fnp_info.fnp_tag, old_pcw);
1154                timeout = "1"b;
1155           end;
1156           else timeout = "0"b;
1157           return;
1158      end connect_to_dia;
1159 ^L
1160 
1161 mask_dia:
1162      procedure;
1163           call io_manager$mask (fnp_info.io_manager_chx);   /* mask the channel to initialize it */
1164      end mask_dia;
1165 
1166 internal_unwire:
1167      procedure;
1168 
1169 declare  px fixed bin;
1170 
1171 /* internal procedure to undo work of wire entry */
1172 
1173           if fnpp = null
1174           then return;
1175           if ^fnp_info.wired
1176           then return;
1177 
1178           ioptp = fnp_info.ptp;
1179           if ioptp ^= null ()
1180           then do px = FIRST_BOOTLOAD_PAGEX to FIRST_BOOTLOAD_PAGEX + fnp_info.n_pages_wired;
1181                unspec (page_table.ptw (px)) = ""b;          /* as of next connect, no more references */
1182           end;
1183 
1184           astep = fnp_info.astep;
1185           call pc_abs$unwire_abs (astep, 0, fnp_info.n_pages_wired);
1186           call grab_aste$release_io (astep);
1187           fnp_info.wired = "0"b;
1188           return;
1189 
1190      end internal_unwire;
1191 
1192 release_t_and_d:
1193      procedure;
1194 
1195 declare  unlock_dump_seg bit (1) aligned;
1196 
1197           call lock$wait (addr (fnp_dump_seg.lock), fdump_seg_event, code);
1198                                                             /* wait for finish */
1199           unlock_dump_seg = (code = 0);                     /* if user held lock for other reason, leave it locked ? */
1200           call syserr (0, "fnp_util: force detaching FNP ^a from process ^w.", fnp_info.fnp_id.fnp_tag,
1201                fnp_info.boot_process_id);
1202           call lock_fnp;                                    /* hold the LCTE lock (or the config lock) */
1203           fnp_info.boot_process_id = "000000000000"b3;
1204           fnp_info.boot_ev_chan = 0;
1205           fnp_info.t_and_d_in_progress = "0"b;
1206           fnp_info.t_and_d_lev_3_occurred, fnp_info.t_and_d_lev_7_occurred = "0"b;
1207           call fnp_multiplexer$unassign (fnp_no, code);
1208           if code ^= 0
1209           then call syserr$error_code (ANNOUNCE, code, "fnp_util: io channel unassignment failed for FNP ^a.",
1210                     fnp_info.fnp_tag);
1211           call unlock_fnp;
1212           if unlock_dump_seg
1213           then call lock$unlock (addr (fnp_dump_seg.lock), fdump_seg_event);
1214           return;
1215      end release_t_and_d;
1216 
1217 
1218 lock_fnp:
1219      procedure;
1220 
1221 declare  code fixed bin (35);
1222 
1223           if locked | config_locked
1224           then call syserr (CRASH, "fnp_util: lock_fnp called with lock locked.");
1225           locked, config_locked = "0"b;
1226 
1227           call lock$lock_fast (addr (datanet_info.configuration_lock));
1228           config_locked = "1"b;                             /* LCTE cannot get initialized after this point */
1229           if fnp_info.lcte_ptr = null ()
1230           then return;                                      /* that is the whole story */
1231           else if ^fnp_info.lcte_ptr -> lcte.initialized    /* no mpx in the house */
1232           then return;                                      /* and the config lock locks out T&D I/O */
1233 
1234           /*** here, there is a multiplexer, so we have to lock against it */
1235 
1236           call fnp_multiplexer$fnp_lock (fnpp, code);
1237           if code = 0
1238           then do;
1239                locked = "1"b;                               /* lcte was initialized, and we now own it */
1240                call lock$unlock_fast (addr (datanet_info.configuration_lock));
1241                                                             /* if init_multiplexer finds the LCTE initialized, it aborts */
1242                                                             /* so all we are protecting against is ourselves */
1243                config_locked = "0"b;
1244           end;
1245           return;
1246 
1247 unlock_fnp:
1248      entry;
1249 
1250           if locked
1251           then call fnp_multiplexer$fnp_unlock (fnpp);
1252           locked = "0"b;
1253           if config_locked
1254           then call lock$unlock_fast (addr (datanet_info.configuration_lock));
1255           config_locked = "0"b;
1256           return;
1257      end lock_fnp;
1258 
1259 
1260 STOP_CHECK:
1261      procedure (Tracer);
1262 declare  Tracer char (*);
1263 
1264           if datanet_info.debug_stop
1265           then call syserr (CRASH, "fnp_util$^a: Debugging stop (type go to continue).", Tracer);
1266           return;
1267      end STOP_CHECK;
1268 ^L
1269 
1270 get_page_table:
1271      procedure (code);
1272 
1273 declare  pagex fixed bin;
1274 declare  px fixed bin;
1275 declare  1 seg_pt (0:255) aligned like l68_core_ptw based (ptp);
1276 declare  ptp pointer;
1277 declare  code fixed bin (35);
1278 
1279           io_page_table_size = 256;                         /* always, for datanet */
1280           call ioi_page_table$get (io_page_table_size * sys_info$page_size, fnp_info.ptx, code);
1281                                                             /* Direct channel has no bounds check so we need all 256 */
1282           if code ^= 0
1283           then return;
1284           fnp_info.ptp, ioptp = ioi_page_table$ptx_to_ptp (fnp_info.ptx);
1285 
1286           unspec (page_table) = ""b;
1287 
1288 /**** leave page zero invalid */
1289 
1290 /**** First, dn355_mailbox */
1291 
1292           do px = 1 to 3;
1293                io_ptwp = addr (page_table.ptw (px));
1294                io_ptw.address = px;                         /* absolute page number */
1295                io_ptw.write = "1"b;
1296                io_ptw.valid = "1"b;
1297           end;
1298 
1299 /**** Now, tty_buf */
1300 
1301           astep = get_ptrs_$given_segno (segno (addr (tty_buf$)));
1302           pagex = FIRST_TTY_BUF_PAGEX;
1303           ptp = addwordno (astep, sst$astsize);
1304           do px = 0 to bin (aste.csl, 9) - 1;               /* no null pages here */
1305                io_ptwp = addr (page_table.ptw (pagex));
1306                io_ptw.address = seg_pt (px).frame;
1307                io_ptw.write = "1"b;
1308                io_ptw.valid = "1"b;
1309                pagex = pagex + 1;
1310           end;                                              /* tty_buf is now described to the channel */
1311           return;
1312 
1313 fill_bootload_page_table:
1314      entry;                                                 /* fill in io ptws for bootload segment */
1315 
1316 /**** astep is already set to the bootload image aste */
1317 
1318           pagex = FIRST_BOOTLOAD_PAGEX;
1319           ptp = addwordno (astep, sst$astsize);
1320           auto_absadr = pagex * sys_info$page_size;
1321           ioptp = fnp_info.ptp;
1322 
1323           do px = 0 to fnp_info.n_pages_wired - 1;
1324                io_ptwp = addr (page_table.ptw (pagex));
1325                io_ptw.address = seg_pt (px).frame;
1326                io_ptw.write = "0"b;
1327                io_ptw.valid = "1"b;
1328                pagex = pagex + 1;
1329           end;
1330 
1331           if datanet_info.trace
1332           then do px = 0 to 255;
1333                if unspec (page_table.ptw (px)) ^= ""b
1334                then call syserr (ANNOUNCE, "     ^4o ^w", px, unspec (page_table.ptw (px)));
1335           end;
1336 
1337           return;
1338 
1339      end get_page_table;
1340 
1341 
1342 /* format: off */
1343 
1344 /* INCLUDE FILES */
1345 %page; %include aste;
1346 %page; %include config_prph_fnp_card;
1347 %page; %include dn355_mailbox;
1348 %page; %include lct;
1349 %page; %include dn355_data;
1350 %page; %include pcb;
1351 %page; %include fnp_dump_seg;
1352 %page; %include fnp_types;
1353 %page; %include io_manager_dcls;
1354 %page; %include io_page_tables;
1355 %page; %include io_chnl_util_dcls;
1356 %page; %include "ptw.l68";
1357        declare ptp pointer;
1358 %page; %include syserr_constants;
1359 %page; %include tty_buf;
1360 %include fnp_mpx_msg_;
1361 
1362 ^L
1363 
1364 
1365 /* BEGIN MESSAGE DOCUMENTATION
1366 
1367    Message:
1368    Loading FNP X, CORE_IMAGE VERSION
1369 
1370    S:  $info
1371 
1372    T:  Answering service initialization and each subsequent FNP
1373    bootload.
1374 
1375    M:  Loading of FNP X has begun with a core image named CORE_IMAGE
1376    (normally "mcs") of version number VERSION.
1377 
1378    A:  $ignore
1379 
1380 
1381    Message:
1382    fnp_util$wire: FNP T already wired (trace)
1383 
1384 
1385    Message:
1386    fnp_util$wire: failed. REASON (trace)
1387 
1388 
1389    Message:
1390    fnp_util$load: FNP load already in progress for FNP T.
1391 
1392 
1393    Message:
1394    fnp_util$load: FNP T not wired.
1395 
1396 
1397    Message:
1398    fnp_util$load: FNP T is running. (trace)
1399 
1400 
1401    Message:
1402    fnp_util$load: FNP T is running T&D.
1403 
1404 
1405    Message:
1406    fnp_util$load: Error loading FNP T. REASON (trace)
1407 
1408 
1409    Message:
1410    fnp_util$unwire: Unwire failed for FNP T. REASON
1411 
1412 
1413    Message:
1414    fnp_util$configure: FNP T already configured.
1415 
1416 
1417    Message:
1418    fnp_util: FNP T added to configuration^[ by NAME^].
1419 
1420 
1421    Message:
1422    FNP T deleted from configuration^[ by ^a^].
1423 
1424 
1425    Message:
1426    fnp_util$fdump_seg_io: IO_OP for CALLER (trace)
1427 
1428 
1429    Message:
1430    fnp_util: channel assignment failed for FNP ^T REASON. (tandd)
1431 
1432 
1433    Message:
1434    fnp_util: ioam_$assign failed for FNP T. REASON (tandd)
1435 
1436 
1437    Message:
1438    fnp_util: assigned FNP T to NAME for T & D.
1439 
1440 
1441    Message:
1442    fnp_util: io channel unassignment failed for FNP T. (tandd)
1443 
1444 
1445    Message:
1446    fnp_util: releasing FNP T from NAME (tandd)
1447 
1448 
1449    Message:
1450    fnp_util$validate_assigned_ret (CALLER): IO manager assignment lacking. (trace)
1451 
1452 
1453    Message:
1454    fnp_util$ENTRY: Invalid call for FNP INDEX. (not in cdeck)
1455 
1456 
1457    Message:
1458    fnp_util$ENTRY: Tracing call for fnp T.
1459 
1460 
1461    Message:
1462    fnp_util$ENTRY: Invalid call. (trace)
1463 
1464 
1465    Message:
1466    fnp_util: Could not unassign FNP T at reassign_interrupt.
1467 
1468 
1469    Message:
1470    fnp_util: force detaching FNP T from process PID.
1471 
1472 
1473    Message:
1474    fnp_util: io channel unassignment failed for FNP T. (tandd)
1475 
1476 
1477    Message:
1478    fnp_util: lock_fnp called with lock locked.
1479 
1480 
1481    Message:
1482    fnp_util$ENTRY: Debugging stop (type go to continue).
1483 
1484    END MESSAGE DOCUMENTATION */
1485 
1486 
1487      end fnp_util;