?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Job Management : Load System Job Templates' ??
MODULE jmm$load_system_job_templates;

{ PURPOSE:
{   This module contains the procedures which load the job templates from the deadstart
{   device to the correct device files and activates them.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$segment_pointer
*copyc jme$job_template_conditions
*copyc jmt$job_template_entry
*copyc jmt$seg_attr
*copyc mtv$xp_initial_value
*copyc osd$code_base_pointer
*copyc ost$hardware_subranges
*copyc ost$name
*copyc pmt$task_template
*copyc pmt$virtual_memory_image_header
?? POP ??
*copyc dmp$attach_device_file
*copyc dmp$close_file
*copyc dmp$create_device_file
*copyc dmp$destroy_device_file
*copyc dmp$detach_device_file
*copyc dmp$open_file
*copyc dmp$fetch_eoi
*copyc dmp$set_eoi
*copyc dsp$read_deadstart_device
*copyc dsp$read_header_labels
*copyc i#move
*copyc mmp$add_global_template_segment
*copyc mmp$assign_mass_storage
*copyc mmp$close_device_file
*copyc mmp$open_file_by_sfid
*copyc mmp$write_modified_pages
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$zero_out_table
*copyc syp$display_deadstart_message
*copyc syp$process_deadstart_status
*copyc syp$trace_deadstart_message
?? EJECT ??
*copyc dmv$system_device_information
*copyc jmv$delete_old_templates
*copyc gfv$null_sfid
*copyc jmv$system_core_id
*copyc mmv$default_sdtx_entry
*copyc osv$mainframe_pageable_heap
*copyc osv$page_size
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??
  VAR
    file_name_seed: ost$name := 'jt_file_',
    jmv$system_job_template_p: [XDCL, #GATE] ^jmt$job_template_entry,
    jmv$task_private_templ_p: [XDCL, #GATE] ^pmt$task_template;
?? TITLE := 'create_and_load_segment', EJECT ??

{ PURPOSE:
{   This procedure creates and loads the job template segments.

  PROCEDURE create_and_load_segment
    (    directory_header_p: ^pmt$virtual_memory_image_header;
     VAR directory_file_p: ^SEQ ( * );
     VAR cbp_present: boolean;
     VAR code_base_pointer: ost$external_code_base_pointer;
     VAR template_segments_array_p: ^ARRAY [ * ] OF jmt$seg_attr;
     VAR status: ost$status);

    VAR
      device_file_segment_number: ost$segment,
      file_name: ost$name,
      pva_destination_p: ^cell,
      sdt_entry: mmt$segment_descriptor,
      sdtx_entry: mmt$segment_descriptor_extended,
      segment_description_p: ^pmt$linked_segment_description,
      segment_index: integer,
      segment_number: ost$segment,
      sfid: gft$system_file_identifier,
      source_pva_p: ^cell,
      vsn: rmt$recorded_vsn;

    syp$trace_deadstart_message ('assign device shared segments');
    FOR segment_index := 1 TO directory_header_p^.number_of_segments DO
      NEXT segment_description_p IN directory_file_p;
      file_name := file_name_seed;
      file_name (9, 1) := $CHAR (segment_index DIV 10 + ORD ('0'));
      file_name (10, 1) := $CHAR (segment_index MOD 10 + ORD ('0'));
      vsn := dmv$system_device_recorded_vsn;
      dmp$attach_device_file (vsn, file_name, sfid, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      segment_number := segment_description_p^.segment_number;
      sdt_entry.ste := segment_description_p^.segment_descriptor;
      sdt_entry.ste.asid := 0; {for insurance
      sdtx_entry := mmv$default_sdtx_entry;
      CASE segment_number OF
      = osc$segnum_job_pageable_heap, osc$segnum_task_shared_heap =
        sdtx_entry.inheritance := mmc$si_share_segment;
        sdtx_entry.open_validating_ring_number := 1;
      = osc$segnum_task_private_heap =
        sdtx_entry.inheritance := mmc$si_new_segment;
        sdtx_entry.open_validating_ring_number := 1;
      ELSE
        sdtx_entry.inheritance := mmc$si_share_segment;
        sdtx_entry.open_validating_ring_number := 0;
      CASEND;

      IF segment_description_p^.segment_descriptor.wp = osc$non_writable THEN
        sdtx_entry.sfid := sfid;
        sdtx_entry.access_state := mmc$sas_allow_access;
        { Since this is a non-writable segment, change from the default access
        { rights (write_extend) to read (only).

        sdtx_entry.access_rights := mmc$sar_read;
        IF segment_description_p^.segment_descriptor.rp = osc$binding_segment THEN
          cbp_present := TRUE;
          code_base_pointer := directory_header_p^.starting_procedure;
        IFEND;

        mmp$add_global_template_segment (sdt_entry, sdtx_entry, segment_number, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

      ELSE   { segment is writeable therefore must be unique.
        mmp$open_file_by_sfid (sfid, 1, 1, mmc$as_sequential, mmc$sar_read,
              device_file_segment_number, status);
        source_pva_p := #ADDRESS (1, device_file_segment_number, 0);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        mmp$add_global_template_segment (sdt_entry, sdtx_entry, segment_number, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        mmp$assign_mass_storage (segment_number, gfv$null_sfid, segment_description_p^.length,
              status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        pva_destination_p := #ADDRESS (1, segment_number, 0);
        i#move (source_pva_p, pva_destination_p, segment_description_p^.length);
        mmp$close_device_file (device_file_segment_number, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;

      { Save the segment attributes.

      template_segments_array_p^ [segment_index].sfid_needed :=
            segment_description_p^.segment_descriptor.wp = osc$non_writable;
      template_segments_array_p^ [segment_index].seg_len := segment_description_p^.length;
      template_segments_array_p^ [segment_index].segnumber := segment_number;
      template_segments_array_p^ [segment_index].sdt := sdt_entry;
      template_segments_array_p^ [segment_index].sdtx := sdtx_entry;
    FOREND;

  PROCEND create_and_load_segment;
?? TITLE := 'create_segment_file', EJECT ??

{ PURPOSE:
{   This procedure creates the job template segment device file and moves the segment
{   from the deadstart device into this file.

  PROCEDURE create_segment_file
    (    segment_index: integer;
         segment_length: ost$segment_length);

    VAR
      data_size_read: integer,
      file_attributes_p: ^ARRAY [1 .. * ] OF dmt$new_device_file_attribute,
      file_eoi: amt$file_byte_address,
      file_modified: boolean,
      file_name: ost$name,
      file_segment_pointer: mmt$segment_pointer,
      file_size: amt$file_byte_address,
      fmd_modified: boolean,
      segment_file_p: ^SEQ ( * ),
      segment_p: ^SEQ ( * ),
      sfid: gft$system_file_identifier,
      status: ost$status,
      vsn: rmt$recorded_vsn;

    file_name := file_name_seed;
    file_name (9, 1) := $CHAR (segment_index DIV 10 + ORD ('0'));
    file_name (10, 1) := $CHAR (segment_index MOD 10 + ORD ('0'));
    vsn := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (vsn, file_name, sfid, status);
    IF NOT status.normal THEN
      IF (status.condition = dme$unknown_device_file) THEN
        PUSH file_attributes_p: [1 .. 1];
        file_attributes_p^ [1].keyword := dmc$file_limit;
        file_attributes_p^ [1].limit := UPPERVALUE (amt$file_limit);
        dmp$create_device_file (file_name, vsn, file_attributes_p, (segment_length * 2), sfid, status);
        IF NOT status.normal THEN
          IF (status.condition = dme$unable_to_alloc_all_space) THEN
            syp$display_deadstart_message ('Not enough space on disk to upgrade.');
            syp$display_deadstart_message ('Use old system to delete files.');
            syp$process_deadstart_status ('Disk too full for template.', TRUE, status);
          ELSE
            osp$system_error ('Cant create template file.', ^status);
          IFEND;
        IFEND;
      ELSE
        osp$system_error ('Cant attach template file.', ^status);
      IFEND;
    IFEND;

    file_segment_pointer.kind := mmc$sequence_pointer;
    dmp$open_file (sfid, osc$os_ring_1, osc$os_ring_1, mmc$sar_write_extend, mmc$as_sequential,
          file_segment_pointer, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant open template file.', ^status);
    IFEND;

    dmp$fetch_eoi (sfid, file_eoi, status);
    IF NOT status.normal THEN
      osp$system_error ('Bad status on dmp$fetch_eoi.', ^status);
    IFEND;

    IF segment_length > file_eoi THEN
      dmp$close_file (file_segment_pointer.cell_pointer, status);
      IF NOT status.normal THEN
        osp$system_error ('Bad status on dmp$close.', ^status);
      IFEND;
      dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IF NOT status.normal THEN
        osp$system_error ('Bad status on dmp$detach.', ^status);
      IFEND;
      dmp$destroy_device_file (vsn, file_name, status);
      IF NOT status.normal THEN
        osp$system_error ('Bad status on dmp$destroy.', ^status);
      IFEND;

      file_size := segment_length * 2;
      PUSH file_attributes_p: [1 .. 1];
      file_attributes_p^ [1].keyword := dmc$file_limit;
      file_attributes_p^ [1].limit := UPPERVALUE (amt$file_limit);
      dmp$create_device_file (file_name, vsn, file_attributes_p, file_size, sfid, status);
      IF NOT status.normal THEN
        IF (status.condition = dme$unable_to_alloc_all_space) THEN
          syp$display_deadstart_message ('Not enough space on disk to upgrade.');
          syp$display_deadstart_message ('Use old system to delete files.');
          syp$process_deadstart_status ('Disk too full for template.', TRUE, status);
        ELSE
          osp$system_error ('Cant create template file.', ^status);
        IFEND;
      IFEND;
      dmp$open_file (sfid, osc$os_ring_1, osc$os_ring_1, mmc$sar_write_extend, mmc$as_sequential,
            file_segment_pointer, status);
      IF NOT status.normal THEN
        osp$system_error ('Bad status on dmp$open.', ^status);
      IFEND;
    IFEND;

    { Move the segment from the deadstart device to the device file.

    segment_file_p := file_segment_pointer.seq_pointer;
    RESET segment_file_p;
    NEXT segment_p: [[REP segment_length OF cell]] IN segment_file_p;
    dmp$set_eoi (sfid, 0, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant set EOI for template file.', ^status);
    IFEND;

    dsp$read_deadstart_device (segment_length, segment_p, data_size_read);
    IF data_size_read < segment_length THEN
      osp$system_error ('Invalid deadstart file: Bad segment.', NIL);
    IFEND;

    mmp$write_modified_pages (segment_file_p, segment_length, osc$nowait, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant write template file.', ^status);
    IFEND;

    dmp$close_file (segment_file_p, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant close template file.', ^status);
    IFEND;

    dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant detach template file.', ^status);
    IFEND;
  PROCEND create_segment_file;
?? TITLE := 'save_system_job_template', EJECT ??

{ PURPOSE:
{   The following procedure initializes the task private template(s). These are used by tasking
{   to initialize the task private segments of newly created tasks.

  PROCEDURE save_system_job_template
    (    seg_attr_array_p: ^ARRAY [ * ] OF jmt$seg_attr,
         max_segments: integer;
     VAR status: ost$status);

    CONST
      max_task_private_segs = 1;

    TYPE
      task_private_segments = SET OF ost$segment;

    VAR
      incoming_segment_entry_p: ^jmt$seg_attr,
      length_of_static_data: integer,
      pva_p: ^cell,
      scan: ost$segment,
      segment_number: ost$segment,
      task_private_segment_index: ost$segment,
      task_private_segment_set: task_private_segments,
      template_segment_entry_p: ^jmt$job_templ_segment;

    task_private_segment_index := 1;
    task_private_segment_set := $task_private_segments [osc$segnum_task_private_heap];

    ALLOCATE jmv$task_private_templ_p: [1 .. max_task_private_segs] IN osv$mainframe_pageable_heap^;
    FOR scan := 1 TO max_task_private_segs DO
      jmv$task_private_templ_p^.segment [scan].content := NIL;
    FOREND;

    { Set up the initial execution control block for all spawned tasks.

    pmp$zero_out_table (#LOC (jmv$task_private_templ_p^.xcb), #SIZE (jmv$task_private_templ_p^.xcb));
    jmv$task_private_templ_p^.xcb.xp := mtv$xp_initial_value;
    jmv$task_private_templ_p^.xcb.dispatching_priority := 0;
    jmv$task_private_templ_p^.xcb.pit_count := 7fffffff(16);
    jmv$task_private_templ_p^.xcb.iocb_p := NIL;
    jmv$task_private_templ_p^.xcb.assign_active_sfid := gfv$null_sfid;

    ALLOCATE jmv$system_job_template_p: [1 .. max_segments] IN osv$mainframe_pageable_heap^;

    { Build the template segment by segment.

    FOR scan := 1 TO max_segments DO
      incoming_segment_entry_p := ^seg_attr_array_p^ [scan];
      template_segment_entry_p := ^jmv$system_job_template_p^.job_template [scan];
      segment_number := incoming_segment_entry_p^.segnumber;
      pva_p := #ADDRESS (1, segment_number, 0);
      template_segment_entry_p^.tasking_segment := FALSE;
      template_segment_entry_p^.writeable_segment := TRUE;
      template_segment_entry_p^.seg_no := segment_number;
      template_segment_entry_p^.sdt := incoming_segment_entry_p^.sdt;
      template_segment_entry_p^.sdtx := incoming_segment_entry_p^.sdtx;
      IF incoming_segment_entry_p^.sfid_needed THEN
        template_segment_entry_p^.writeable_segment := FALSE;
      ELSE
        length_of_static_data := incoming_segment_entry_p^.seg_len;
        ALLOCATE template_segment_entry_p^.static_data_p: [1 .. length_of_static_data] IN
              osv$mainframe_pageable_heap^;
        i#move (pva_p, template_segment_entry_p^.static_data_p, length_of_static_data);
        IF segment_number IN task_private_segment_set THEN
          template_segment_entry_p^.tasking_segment := TRUE;
          IF task_private_segment_index <= max_task_private_segs THEN
            jmv$task_private_templ_p^.segment [task_private_segment_index].number := segment_number;
            jmv$task_private_templ_p^.segment [task_private_segment_index].content :=
                  template_segment_entry_p^.static_data_p;
            task_private_segment_index := task_private_segment_index + 1;
          ELSE
            osp$set_status_abnormal ('JM', jme$tasking_segs_mismatch, 'MAX TASK PRIVATE SEGS EXCEEDED',
                  status);
            RETURN;
          IFEND;
        IFEND;
      IFEND;
    FOREND;

  PROCEND save_system_job_template;
?? TITLE := 'jmp$activate_sys_job_template', EJECT ??

{ PURPOSE:
{   This procedure is called to load the job templates (consisting of a series of
{   segments) into virtual memory.

  PROCEDURE [XDCL] jmp$activate_sys_job_template
    (    template_name: ost$name;
     VAR code_base_pointer: ost$external_code_base_pointer;
     VAR status: ost$status);

    VAR
      cbp_present: boolean,
      directory_file_p: ^SEQ ( * ),
      directory_header_p: ^pmt$virtual_memory_image_header,
      file_modified: boolean,
      file_segment_pointer: mmt$segment_pointer,
      fmd_modified: boolean,
      max_segments: integer,
      sfid: gft$system_file_identifier,
      template_segments_array_p: ^ARRAY [ * ] OF jmt$seg_attr,
      vsn: rmt$recorded_vsn;

    VAR
      syv$recovery_override: [XREF] 0 .. 0ff(16);

    status.normal := TRUE;

    vsn := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (vsn, template_name, sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    file_segment_pointer.kind := mmc$sequence_pointer;
    dmp$open_file (sfid, osc$os_ring_1, osc$os_ring_1, mmc$sar_read, mmc$as_random,
          file_segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    directory_file_p := file_segment_pointer.seq_pointer;
    RESET directory_file_p;
    NEXT directory_header_p IN directory_file_p;
    IF (directory_header_p^.system_core_id <> jmv$system_core_id) AND (syv$recovery_override = 0) THEN
      osp$set_status_abnormal ('JM', jme$job_temp_sys_core_mismatch, 'JOB TEMPL - SYS CORE MISMATCH', status);
      RETURN;
    IFEND;

    { Save space for the segment list.

    max_segments := directory_header_p^.number_of_segments;
    PUSH template_segments_array_p: [1 .. directory_header_p^.number_of_segments];
    cbp_present := FALSE;

    { Create and load the job segments.

    create_and_load_segment (directory_header_p, directory_file_p, cbp_present, code_base_pointer,
          template_segments_array_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    dmp$close_file (directory_file_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF NOT cbp_present THEN
      osp$set_status_abnormal ('JM', jme$no_binding_segment, 'NO BINDING SEGMENT', status);
      RETURN;
    IFEND;

    { Save the job templates.

    save_system_job_template (template_segments_array_p, max_segments, status);

  PROCEND jmp$activate_sys_job_template;
?? TITLE := 'jmp$load_sys_job_template', EJECT ??

{ PURPOSE:
{   This procedure loads the job templates from the deadstart device to the device file.

  PROCEDURE [XDCL] jmp$load_sys_job_template
    (    template_name: ost$name;
     VAR status: ost$status);

    VAR
      data_size_read: integer,
      directory_file_p: ^SEQ ( * ),
      directory_header_p: ^pmt$virtual_memory_image_header,
      file_attributes_p: ^ARRAY [1 .. * ] OF dmt$new_device_file_attribute,
      file_identifier: dst$deadstart_file_identifier,
      file_modified: boolean,
      file_name: ost$name,
      file_segment_pointer: mmt$segment_pointer,
      file_size: amt$file_byte_address,
      fmd_modified: boolean,
      header_sfid: gft$system_file_identifier,
      local_status: ost$status,
      segment_description_p: ^pmt$linked_segment_description,
      segment_description_seq_p: ^SEQ ( * ),
      segment_index: integer,
      virtual_memory_header: pmt$virtual_memory_image_header,
      virtual_memory_header_seq_p: ^SEQ ( * ),
      vsn: rmt$recorded_vsn;

    status.normal := TRUE;
    vsn := dmv$system_device_recorded_vsn;
    dsp$read_header_labels (file_identifier);
    IF file_identifier <> 'JOB_IMAGE' THEN
      osp$system_error ('Invalid deadstart file: Cannot find JOB_IMAGE.', NIL);
    IFEND;

    virtual_memory_header_seq_p := #SEQ (virtual_memory_header);
    dsp$read_deadstart_device (#SIZE (pmt$virtual_memory_image_header), virtual_memory_header_seq_p,
          data_size_read);
    IF data_size_read < #SIZE (pmt$virtual_memory_image_header) THEN
      osp$system_error ('Invalid deadstart file: Bad JOB_IMAGE.', NIL);
    IFEND;

    IF virtual_memory_header.version <> pmc$image_version THEN
      osp$system_error ('Template header-system linker mismatch.', NIL);
    IFEND;

    { Delete old template files if requested.

    IF jmv$delete_old_templates THEN
      dmp$destroy_device_file (vsn, template_name, local_status);
      FOR segment_index := 1 TO 99 DO
        file_name := file_name_seed;
        file_name (9, 1) := $CHAR (segment_index DIV 10 + ORD ('0'));
        file_name (10, 1) := $CHAR (segment_index MOD 10 + ORD ('0'));
        dmp$destroy_device_file (vsn, file_name, local_status);
      FOREND;
    IFEND;

    { Open or create the job template directory file.

    dmp$attach_device_file (vsn, template_name, header_sfid, status);
    IF NOT status.normal THEN
      IF status.condition <> dme$unknown_device_file THEN
        RETURN;
      ELSE
        file_size := 2 * (#SIZE (pmt$virtual_memory_image_header) + #SIZE (pmt$linked_segment_description)
              * virtual_memory_header.number_of_segments);
        PUSH file_attributes_p: [1 .. 1];
        file_attributes_p^ [1].keyword := dmc$file_limit;
        file_attributes_p^ [1].limit := UPPERVALUE (amt$file_limit);
        dmp$create_device_file (template_name, vsn, file_attributes_p, file_size, header_sfid, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    IFEND;

    file_segment_pointer.kind := mmc$sequence_pointer;
    dmp$open_file (header_sfid, osc$os_ring_1, osc$os_ring_1, mmc$sar_write_extend, mmc$as_random,
          file_segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    directory_file_p := file_segment_pointer.seq_pointer;
    RESET directory_file_p;
    NEXT directory_header_p IN directory_file_p;

    directory_header_p^ := virtual_memory_header;

    { Load the job template segments from the deadstart device.

    FOR segment_index := 1 TO virtual_memory_header.number_of_segments DO
      NEXT segment_description_p IN directory_file_p;
      segment_description_seq_p := #SEQ (segment_description_p^);
      dsp$read_deadstart_device (#SIZE (segment_description_p^), segment_description_seq_p,
            data_size_read);
      IF data_size_read < #SIZE (segment_description_p^) THEN
        osp$system_error ('Invalid deadstart file: Bad segment description.', NIL);
      IFEND;
      create_segment_file (segment_index, segment_description_p^.length);
    FOREND;

    { Force the directory to its device file, close and detach the directory file.

    mmp$write_modified_pages (directory_file_p, #SIZE (directory_file_p^), osc$wait, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant write template file.', ^status);
    IFEND;

    dmp$close_file (directory_file_p, status);
    IF NOT status.normal THEN
      osp$system_error ('Cant close template file.', ^status);
    IFEND;

    dmp$detach_device_file (header_sfid, file_modified, fmd_modified, status);
    IF NOT status.normal THEN
       osp$system_error ('Cant detach template file.', ^status);
    IFEND;

  PROCEND jmp$load_sys_job_template;
MODEND jmm$load_system_job_templates;
