?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : PP Library Management' ??
MODULE dsm$manage_pp_library;

{ PURPOSE:
{   This module contains the procedures that maintain the PP library.
{ DESIGN:
{   The PP library is in a linked list format.  The PP entries are linked together to form the list.  Each
{   entry contains the PP's name, a pointer to the PP's data and a pointer to the next link in the list.
{   The VE disk/tape drivers (VDT) are read from the common disk area (CDA) in CTI in the BOOT.  The PP library
{   is not created in the BOOT because of space constraints.  System core accesses VDT from the BOOT memory
{   and builds the PP library in mainframe pageable.  The PPs that reside on the deadstart device are added
{   to the library later.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dse$pp_library_errors
*copyc dst$c170_77_table
*copyc dst$driver_name
*copyc dst$fetch_pp_image_option
*copyc dst$pp_header_descriptor
*copyc ost$hardware_subranges
*copyc ost$pp_size
?? POP ??
*copyc dsp$allocate_continuous_memory
*copyc dsp$fetch_boot_data
*copyc dsp$read_cda_program
*copyc dsp$read_deadstart_device
*copyc dsp$read_header_labels
*copyc dsp$retrieve_cda_data_size
*copyc dsp$save_boot_data_pointer
*copyc i#real_memory_address
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc syp$display_deadstart_message
*copyc syp$trace_deadstart_message
?? EJECT ??
*copyc dsv$boot_data_base_p
*copyc osv$boot_is_executing
*copyc osv$mainframe_pageable_heap
*copyc osv$mainframe_wired_cb_heap
*copyc osv$mainframe_wired_heap
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
  TYPE
    t$boot_entry = RECORD
      pp_entry: t$pp_entry,
      next_entry_p: ^t$boot_entry,
    RECEND,

    t$display_code_character = 0 .. 77(8),

    t$display_code_string = 0 .. 0777777777777777(8),

    t$pp_entry = RECORD
      name: dst$driver_name,
      overlay_rma: ost$real_memory_address,
      driver_code_p: ^SEQ ( * ),
      data_p: ^SEQ ( * ),
      next_p: ^t$pp_entry,
    RECEND;
?? EJECT ??
  VAR
    v$boot_entry_p: ^t$boot_entry := NIL,
    v$pp_library_p:[XDCL] ^t$pp_entry := NIL,
    v$vdt_data_seq_p: ^SEQ ( * ) := NIL;
?? OLDTITLE ??
?? NEWTITLE := 'convert_dc_int_to_ascii_string', EJECT ??

{ PURPOSE:
{   This procedure converts a seven character string from display code to ascii.

  PROCEDURE convert_dc_int_to_ascii_string
    (    display_code_name: t$display_code_string;
     VAR name: dst$driver_name);

    VAR
      convert_size: t$display_code_string,
      display_code_number: t$display_code_string,
      name_index: 1 .. 7,
      number: t$display_code_character;

    display_code_number := display_code_name;
    convert_size := 100000000000000(8);
    FOR name_index := 1 TO 7 DO
      convert_size := convert_size DIV 100(8);
      number := display_code_number DIV convert_size;
      display_code_number := display_code_number MOD convert_size;
      IF (number >= 33(8)) AND (number <= 44(8)) THEN     { display code character is between '0' and '9'
        name (name_index) := $CHAR (number + 25(8));
      ELSEIF (number >= 1(8)) AND (number <= 32(8)) THEN  { display code character is between 'A' and 'Z'
        name (name_index) := $CHAR (number + 100(8));
      ELSEIF number = 47(8) THEN
        name (name_index) := '*';
      ELSEIF number = 54(8) THEN
        name (name_index) := '=';
      ELSEIF number = 56(8) THEN
        name (name_index) := ',';
      ELSEIF number = 57(8) THEN
        name (name_index) := '.';
      ELSE
        name (name_index) := ' ';
      IFEND;
    FOREND;

  PROCEND convert_dc_int_to_ascii_string;
?? OLDTITLE ??
?? NEWTITLE := 'fetch_boot_pp', EJECT ??

{ PURPOSE:
{   This procedure reads the VE disk/tape drivers (VDT) from CTI and searches the sequence
{   for the desired PP.
{ NOTES:
{   1.  The caller of this procedure must send a pointer to a sequence to hold the PP image.
{   2.  The PP binary contains a 77 table that must be removed before it is returned to the caller.
{   3.  The PP library is not built here to keep the size of the boot small.

  PROCEDURE fetch_boot_pp
    (    name: dst$driver_name;
     VAR pp_data_seq_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      cda_data_size: integer,
      pp_77_table_p: ^dst$c170_77_table,
      pp_name: dst$driver_name,
      pp_size: integer,
      rel_vdt_p: [STATIC] REL (SEQ ( * )) ^SEQ ( * ),
      vdt_data_size: integer;

    status.normal := TRUE;

    IF v$vdt_data_seq_p = NIL THEN

      { Read the VE disk/tape drivers (VDT) from the common disk area (CDA) in CTI.

      dsp$retrieve_cda_data_size ('VDT ', cda_data_size, status);
      IF NOT status.normal THEN
        osp$system_error ('ERROR: Retrieving the data size of VDT.', ^status);
      IFEND;

      dsp$allocate_continuous_memory (osv$mainframe_wired_heap, cda_data_size, v$vdt_data_seq_p);
      RESET v$vdt_data_seq_p;

      dsp$read_cda_program ('VDT ', v$vdt_data_seq_p, vdt_data_size, status);
      IF NOT status.normal THEN
        osp$system_error ('ERROR: Reading the CDA program VDT.', ^status);
      IFEND;
      IF vdt_data_size < #SIZE (dst$c170_77_table) THEN
        osp$system_error ('ERROR: VDT is incorrectly built.', NIL);
      IFEND;
      RESET v$vdt_data_seq_p;

      { Save a pointer to the VDT data so that the data can be retrieved in system core and be used
      { to create the PP library.

      rel_vdt_p := #REL (v$vdt_data_seq_p, dsv$boot_data_base_p^);
      dsp$save_boot_data_pointer (dsc$pp_library, #SEQ (rel_vdt_p));
    IFEND;

    RESET v$vdt_data_seq_p;
    vdt_data_size := #SIZE (v$vdt_data_seq_p^);

    WHILE vdt_data_size > #SIZE (dst$c170_77_table) DO
      NEXT pp_77_table_p IN v$vdt_data_seq_p;
      vdt_data_size := vdt_data_size - #SIZE (pp_77_table_p^);
      convert_dc_int_to_ascii_string (pp_77_table_p^.module_name, pp_name);
      pp_size := pp_77_table_p^.lwa * 8;
      IF vdt_data_size < pp_size THEN
        osp$system_error ('ERROR: VDT is incorrectly built', NIL);
      IFEND;
      NEXT pp_data_seq_p: [[REP pp_size OF cell]] IN v$vdt_data_seq_p;
      vdt_data_size := vdt_data_size - #SIZE (pp_data_seq_p^);
      IF pp_name = name THEN
        RETURN;
      IFEND;
    WHILEND;
    osp$set_status_abnormal (dsc$display_processor_id, dse$driver_not_found, name, status);
    pp_data_seq_p := NIL;

  PROCEND fetch_boot_pp;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$add_to_pp_library', EJECT ??

{  PURPOSE:
{    This procedure moves the non boot driver PPs from the deadstart device to the PP library.

  PROCEDURE [XDCL] dsp$add_to_pp_library;

    VAR
      data_size_read: integer,
      file_identifier: dst$deadstart_file_identifier,
      last_pp_entry_p: ^t$pp_entry,
      library_77_table_p: ^dst$c170_77_table,
      pp_77_table: dst$c170_77_table,
      pp_77_table_seq_p: ^SEQ ( * ),
      pp_data_p: ^SEQ ( * ),
      pp_entry_p: ^t$pp_entry,
      pp_size: integer;

    dsp$read_header_labels (file_identifier);
    IF file_identifier <> 'NON_BOOT_DRIVERS' THEN
      osp$system_error ('Invalid deadstart file: Cannot find the non_boot_driver.', NIL);
    IFEND;

    { Find the last entry in the PP library.

    pp_entry_p := v$pp_library_p;
    WHILE pp_entry_p^.next_p <> NIL DO
      pp_entry_p := pp_entry_p^.next_p;
    WHILEND;
    last_pp_entry_p := pp_entry_p;
    pp_77_table_seq_p := #SEQ (pp_77_table);

   /move_non_boot_driver/
    WHILE TRUE DO
      dsp$read_deadstart_device (#SIZE (dst$c170_77_table), pp_77_table_seq_p, data_size_read);
      IF data_size_read < #SIZE (dst$c170_77_table) THEN
        EXIT /move_non_boot_driver/;
      IFEND;

      ALLOCATE pp_entry_p IN osv$mainframe_pageable_heap^;
      last_pp_entry_p^.next_p := pp_entry_p;
      pp_entry_p^.overlay_rma := 0;
      pp_entry_p^.driver_code_p := NIL;
      convert_dc_int_to_ascii_string (pp_77_table.module_name, pp_entry_p^.name);
      syp$trace_deadstart_message (pp_entry_p^.name);
      pp_size := pp_77_table.lwa * 8;

      ALLOCATE pp_entry_p^.data_p: [[REP (pp_size + #SIZE (dst$c170_77_table)) OF cell]] IN
            osv$mainframe_pageable_heap^;
      RESET pp_entry_p^.data_p;

      NEXT library_77_table_p IN pp_entry_p^.data_p;
      library_77_table_p^ := pp_77_table;
      NEXT pp_data_p: [[REP pp_size OF cell]] IN pp_entry_p^.data_p;
      RESET pp_data_p;

      dsp$read_deadstart_device (pp_size, pp_data_p, data_size_read);
      IF data_size_read < pp_size THEN
        EXIT /move_non_boot_driver/;
      IFEND;

      last_pp_entry_p := pp_entry_p;
    WHILEND /move_non_boot_driver/;

    last_pp_entry_p^.next_p := NIL;

  PROCEND dsp$add_to_pp_library;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$create_pp_library', EJECT ??

{ PURPOSE:
{   This procedure creates the PP library in system core and moves all of the PPs from the VDT sequence to
{   the PP library.  This is not done in the boot to save space.

  PROCEDURE [XDCL] dsp$create_pp_library;

    VAR
      last_pp_entry_p: ^t$pp_entry,
      library_77_table_p: ^dst$c170_77_table,
      library_data_p: ^SEQ ( * ),
      pp_77_table_p: ^dst$c170_77_table,
      pp_data_p: ^SEQ ( * ),
      pp_entry_p: ^t$pp_entry,
      pp_size: integer,
      rel_vdt_p: REL (SEQ ( * )) ^SEQ ( * ),
      rel_vdt_seq_p: ^SEQ ( * ),
      vdt_data_size: integer;

    syp$display_deadstart_message ('Creating the PP library from VDT ...');

    { Fetch the pointer to VDT in the boot memory space.

    rel_vdt_seq_p := #SEQ (rel_vdt_p);
    dsp$fetch_boot_data (dsc$pp_library, rel_vdt_seq_p);
    v$vdt_data_seq_p := #PTR (rel_vdt_p, dsv$boot_data_base_p^);
    RESET v$vdt_data_seq_p;
    vdt_data_size := #SIZE (v$vdt_data_seq_p^);

    WHILE vdt_data_size > #SIZE (dst$c170_77_table) DO
      IF v$pp_library_p = NIL THEN
        ALLOCATE v$pp_library_p IN osv$mainframe_pageable_heap^;
        pp_entry_p := v$pp_library_p;
      ELSE
        ALLOCATE pp_entry_p IN osv$mainframe_pageable_heap^;
        last_pp_entry_p^.next_p := pp_entry_p;
      IFEND;
      pp_entry_p^.overlay_rma := 0;
      pp_entry_p^.driver_code_p := NIL;

      { Read the pp's 77 table and retrieve the length of the PP.

      NEXT pp_77_table_p IN v$vdt_data_seq_p;
      vdt_data_size := vdt_data_size - #SIZE (pp_77_table_p^);
      convert_dc_int_to_ascii_string (pp_77_table_p^.module_name, pp_entry_p^.name);
      syp$trace_deadstart_message (pp_entry_p^.name);
      pp_size := pp_77_table_p^.lwa * 8;
      IF vdt_data_size < pp_size THEN
        osp$system_error ('ERROR: VDT is incorrectly built.', NIL);
      IFEND;

      { Allocate space for the pp and its 77 table.

      ALLOCATE pp_entry_p^.data_p: [[REP (#SIZE (pp_77_table_p^) + pp_size) OF cell]] IN
            osv$mainframe_pageable_heap^;
      RESET pp_entry_p^.data_p;

      NEXT library_77_table_p IN pp_entry_p^.data_p;
      library_77_table_p^ := pp_77_table_p^;

      NEXT library_data_p: [[REP pp_size OF cell]] IN pp_entry_p^.data_p;
      NEXT pp_data_p: [[REP pp_size OF cell]] IN v$vdt_data_seq_p;
      vdt_data_size := vdt_data_size - #SIZE (pp_data_p^);
      library_data_p^ := pp_data_p^;

      last_pp_entry_p := pp_entry_p;

    WHILEND;
    last_pp_entry_p^.next_p := NIL;
    v$vdt_data_seq_p := NIL;

  PROCEND dsp$create_pp_library;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$fetch_pp_image', EJECT ??

{ PURPOSE:
{   This procedure acquires the requested PP from the PP library or directly from VDT if
{   executing in the boot.
{ NOTES:
{   1.  The caller of this procedure must send a pointer to a sequence to hold the PP image.
{   2.  The PP binary contains a 77 table that must be removed before it is returned to the caller.
{   3.  Depending on the setting of OPTION, the base overlay may be returned to the sequence, or the length
{       of the overlay image (if any) may be returned as LENGTH, or the overlay image itself will be returned
{       to the sequence.

  PROCEDURE [XDCL] dsp$fetch_pp_image
    (    name: dst$driver_name;
         option: dst$fetch_pp_image_option;
     VAR length: ost$pp_byte_size;
     VAR image_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      extra_size: ost$pp_byte_size,
      image_data_p: ^SEQ ( * ),
      pp_77_table_p: ^dst$c170_77_table,
      pp_data_p: ^SEQ ( * ),
      pp_data_seq_p: ^SEQ ( * ),
      pp_entry_p: ^t$pp_entry,
      pp_header_p: ^dst$pp_header_descriptor,
      pp_size: ost$pp_byte_size,
      total_length: integer;

    status.normal := TRUE;

    IF osv$boot_is_executing THEN
      fetch_boot_pp (name, pp_data_seq_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      extra_size := #SIZE (dst$pp_header_descriptor);
      total_length := #SIZE (pp_data_seq_p^);
      IF extra_size > #SIZE (pp_data_seq_p^) THEN
        osp$set_status_abnormal (dsc$display_processor_id, dse$driver_damaged, '', status);
        RETURN;
      IFEND;
      RESET pp_data_seq_p;
    ELSE

      { Search the library for the correct PP.

      pp_entry_p := v$pp_library_p;
      WHILE pp_entry_p^.name <> name DO
        pp_entry_p := pp_entry_p^.next_p;
        IF pp_entry_p = NIL THEN
          osp$set_status_abnormal (dsc$display_processor_id, dse$driver_not_found, name, status);
          RETURN;
        IFEND;
      WHILEND;
      pp_data_seq_p := pp_entry_p^.data_p;
      total_length := #SIZE (pp_entry_p^.data_p^) - #SIZE (dst$c170_77_table);
      extra_size := #SIZE (dst$c170_77_table) + #SIZE (dst$pp_header_descriptor);
      IF extra_size > #SIZE (pp_data_seq_p^) THEN
        osp$set_status_abnormal (dsc$display_processor_id, dse$driver_damaged, '', status);
        RETURN;
      IFEND;
      RESET pp_data_seq_p;
      NEXT pp_77_table_p IN pp_data_seq_p;
    IFEND;

    NEXT pp_header_p IN pp_data_seq_p;
    pp_size := pp_header_p^.cm_word_length * 8;

    IF pp_size > (#SIZE (pp_data_seq_p^) - extra_size) THEN
      osp$set_status_abnormal (dsc$display_processor_id, dse$driver_damaged, '', status);
      RETURN;
    IFEND;

    CASE option OF
    = dsc$fpio_fetch_base_overlay =
      length := pp_size;
    = dsc$fpio_return_overlay_length =
      length := total_length - pp_size - #SIZE (dst$pp_header_descriptor);
      RETURN;
    = dsc$fpio_fetch_overlays =
      length := total_length - pp_size - #SIZE (dst$pp_header_descriptor);
      NEXT pp_data_p: [[REP pp_size OF cell]] IN pp_data_seq_p;
    ELSE
      ;
    CASEND;

    { Determine if the caller sent a large enough sequence to receive the PP/overlay image.

    IF length > #SIZE (image_p^) THEN
      osp$set_status_abnormal (dsc$display_processor_id, dse$buffer_too_small, '', status);
      RETURN;
    IFEND;

    { Move the PP/overlay image to the caller's address space.

    NEXT pp_data_p: [[REP length OF cell]] IN pp_data_seq_p;

    RESET image_p;
    NEXT image_data_p: [[REP length OF cell]] IN image_p;

    image_data_p^ := pp_data_p^;
    RESET image_p;

  PROCEND dsp$fetch_pp_image;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$move_pp_driver', EJECT ??

{ PURPOSE:
{   This procedure copies the requested PP driver's main overlay, if any, to an area of contiguous memory
{   and returns a pointer to it.  If the driver was previously copied, the previous pointer is returned.
{   If this request is made during the boot time or if the PP driver is not found, a NIL pointer is returned.

  PROCEDURE [XDCL] dsp$move_pp_driver
    (    name: dst$driver_name;
     VAR driver_code_p: ^SEQ ( * ));

    VAR
      driver_p: ^SEQ ( * ),
      driver_size: integer,
      pp_77_table_p: ^dst$c170_77_table,
      pp_entry_p: ^t$pp_entry,
      pp_header_p: ^dst$pp_header_descriptor;

    driver_code_p := NIL;

    IF osv$boot_is_executing THEN
      RETURN;
    IFEND;

    pp_entry_p := v$pp_library_p;
    WHILE (pp_entry_p <> NIL) AND (pp_entry_p^.name <> name) DO
      pp_entry_p := pp_entry_p^.next_p;
    WHILEND;
    IF pp_entry_p = NIL THEN
      RETURN;
    IFEND;

    IF pp_entry_p^.driver_code_p <> NIL THEN
      driver_code_p := pp_entry_p^.driver_code_p;
      RETURN;
    IFEND;

    RESET pp_entry_p^.data_p;
    NEXT pp_77_table_p IN pp_entry_p^.data_p;
    NEXT pp_header_p IN pp_entry_p^.data_p;
    IF (pp_77_table_p = NIL) OR (pp_header_p = NIL) THEN
      RETURN;
    IFEND;

    driver_size := pp_header_p^.cm_word_length * 8;
    NEXT driver_p: [[REP driver_size OF cell]] IN pp_entry_p^.data_p;
    IF driver_p = NIL THEN
      RETURN;
    IFEND;

    dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, driver_size, pp_entry_p^.driver_code_p);
    pp_entry_p^.driver_code_p^ := driver_p^;
    driver_code_p := pp_entry_p^.driver_code_p;

  PROCEND dsp$move_pp_driver;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$move_pp_overlays', EJECT ??

{ PURPOSE:
{  This procedure copies the requested PP's overlays, if any, to an area of contiguous memory and returns
{  the RMA pointer to the calling procedure.  If the driver has previously had its overlays copied, the
{  previous RMA is returned.

  PROCEDURE [XDCL] dsp$move_pp_overlays
    (    name: dst$driver_name;
     VAR rma: ost$real_memory_address;
     VAR status: ost$status);

    VAR
      boot_entry_p: ^t$boot_entry,
      directory_header_p: ^dst$pp_header_descriptor,
      directory_entry_p: ^dst$pp_header_descriptor,
      directory_size: dst$pp_word,
      index: dst$pp_word,
      length: ost$pp_byte_size,
      overlay1_offset: dst$pp_word,
      pp_entry_p: ^t$pp_entry,
      rma_p: ^SEQ ( * ),
      rma_int: integer;

    status.normal := TRUE;

    { During the boot environment, each driver will be allocated a new overlay structure, since there is no
    { easy way to keep track of existing overlay structures.  The memory will be released during the
    { transition to the system core environment.  In system core, only one copy of the overlay structure for
    { a particular driver will be made.

    IF osv$boot_is_executing THEN
      boot_entry_p := v$boot_entry_p;

     /search_boot_entries/
      WHILE boot_entry_p <> NIL DO
        IF name = boot_entry_p^.pp_entry.name THEN
          EXIT /search_boot_entries/;
        IFEND;
        boot_entry_p := boot_entry_p^.next_entry_p;
      WHILEND /search_boot_entries/;

      IF boot_entry_p = NIL THEN
        ALLOCATE boot_entry_p IN osv$mainframe_wired_heap^;
        boot_entry_p^.pp_entry.name := name;
        boot_entry_p^.pp_entry.overlay_rma := 0;
        boot_entry_p^.pp_entry.driver_code_p := NIL;
        boot_entry_p^.next_entry_p := v$boot_entry_p;
        v$boot_entry_p := boot_entry_p;
      IFEND;
      pp_entry_p := ^boot_entry_p^.pp_entry;
    ELSE
      pp_entry_p := v$pp_library_p;
      WHILE pp_entry_p^.name <> name DO
        pp_entry_p := pp_entry_p^.next_p;
        IF pp_entry_p = NIL THEN
          osp$set_status_abnormal (dsc$display_processor_id, dse$driver_not_found, name, status);
          RETURN;
        IFEND;
      WHILEND;
    IFEND;

    IF pp_entry_p^.overlay_rma <> 0 THEN
      rma := pp_entry_p^.overlay_rma;
      RETURN;
    IFEND;

    { Get the length of the driver's overlay structure, if any, and allocate space in MAINFRAME_WIRED_CB_HEAP.

    dsp$fetch_pp_image (name, dsc$fpio_return_overlay_length, length, rma_p, status);
    IF (length = 0) OR (NOT status.normal) THEN
      RETURN;
    IFEND;
    dsp$allocate_continuous_memory (osv$mainframe_wired_cb_heap, length, rma_p);

    { Copy the PP overlays to the memory allocated.

    dsp$fetch_pp_image (name, dsc$fpio_fetch_overlays, length, rma_p, status);
    IF NOT status.normal THEN
      FREE rma_p IN osv$mainframe_wired_cb_heap^;
      RETURN;
    IFEND;

    { Update the PP library entry to reflect the RMA of the overlays.

    RESET rma_p;
    i#real_memory_address (rma_p, rma_int);
    rma := rma_int;
    pp_entry_p^.overlay_rma := rma;

    { The overlay directory initially contains CM word offsets from the base overlay header word.  These must
    { be converted to CM word offsets from the directory header word, since the base overlay has been removed.

    RESET rma_p;
    NEXT directory_header_p IN rma_p;
    directory_size := directory_header_p^.cm_word_length;
    NEXT directory_entry_p IN rma_p;
    overlay1_offset := directory_entry_p^.overlay_offset;
    directory_entry_p^.overlay_offset := directory_size + 1;
    FOR index := 2 TO directory_size DO
      NEXT directory_entry_p IN rma_p;
      directory_entry_p^.overlay_offset := directory_entry_p^.overlay_offset - overlay1_offset +
            directory_size + 1;
    FOREND;

  PROCEND dsp$move_pp_overlays;
?? OLDTITLE ??
MODEND dsm$manage_pp_library;
