?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : System Messages Logger Helper' ??
MODULE dsm$log_sys_msgs_helper;

{ PURPOSE:
{   This module contains procedures that assist the job template procedures involved in the logging of
{   DFT errors and the logging of system messages.  When the job template procedures wish to contact the
{   logging procedures that run in monitor they call these procedures which are allowed to call monitor.
{   These procedures are also called from job template when it is necessary to write mainframe wired.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dst$dftb_manage_space
*copyc dst$rb_logging_request
*copyc dst$rb_system_deadstart_status
?? POP ??
*copyc dsp$build_sequence_p
*copyc dsp$close_rdf
*copyc dsp$fetch_list_block
*copyc dsp$get_integer_from_rdf
*copyc dsp$get_nve_image_description
*copyc dsp$get_rdf_entry_seq_pointer
*copyc dsp$open_rdf
*copyc dsp$store_integer_in_rdf
*copyc i#build_adaptable_seq_pointer
*copyc i#call_monitor
*copyc i#current_sequence_position
*copyc mmp$write_modified_pages
*copyc osp$system_error
*copyc pmp$zero_out_table
?? EJECT ??
*copyc osv$page_size
*copyc iov$disk_pp_usage_p
*copyc dsv$dftb_data
*copyc dsv$record_errors
*copyc dsv$sys_msg_buffer_desc_p
*copyc dsv$sys_msg_buffer_size
*copyc dsv$system_deadstart_status_p
*copyc osv$mainframe_wired_heap
?? OLDTITLE ??
?? NEWTITLE := 'dsp$clear_sys_msg_buffer_in_rdf', EJECT ??

{ PURPOSE:
{   This procedure clears the system message buffer in the RDF area.

  PROCEDURE [XDCL, #GATE] dsp$clear_sys_msg_buffer_in_rdf;

    VAR
      rdf_file_segment: mmt$segment_pointer,
      rdf_file_sfid: gft$system_file_identifier,
      rdf_pointers: dst$rdf_pointers,
      rdf_seq_p: ^SEQ ( * ),
      valid_data_size_p: ^integer;

    dsp$open_rdf (rdf_file_sfid, rdf_file_segment, rdf_pointers);
    dsp$get_rdf_entry_seq_pointer (dsc$rdf_system_messages_buffer, dsc$rdf_system_message_buffer,
          rdf_pointers, rdf_seq_p);
    RESET rdf_seq_p;
    NEXT valid_data_size_p IN rdf_seq_p;
    valid_data_size_p^ := 0;
    dsp$close_rdf (rdf_file_sfid, rdf_file_segment);

  PROCEND dsp$clear_sys_msg_buffer_in_rdf;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$enlarge_sys_msg_buffer', EJECT ??

{ PURPOSE:
{   This procedure enlarges the circular system message buffer in the mainframe wired heap.  It creates
{   a new system message buffer.  The size of the circular buffer is dependent upon the number of pps
{   that are on the current system.  The size equals the number of pps times 100 words times 2.

  PROCEDURE [XDCL, #GATE] dsp$enlarge_sys_msg_buffer;

    CONST

      { The new buffer size multipier is 100 words times 2.

      new_buffer_size_multiplier = (100 * 8) * 3;

    VAR
      new_buffer_size: integer,
      number_of_pps: integer,
      old_buffer_seq_p: ^SEQ ( * ),
      pp_index: integer,
      rb: dst$rb_logging_request;

    { Find the number of NOS/VE pps on the system.  This number excludes any pp used by NAM/VE, RHF,
    { MALET/VE or FOREIGN Subsystem.

    number_of_pps := 0;
    IF iov$disk_pp_usage_p <> NIL THEN
      FOR pp_index := LOWERBOUND (iov$disk_pp_usage_p^) TO UPPERBOUND (iov$disk_pp_usage_p^) DO

        { If the following pointer is valid then a valid pp exists.

        IF iov$disk_pp_usage_p^ [pp_index] <> NIL THEN
          number_of_pps := number_of_pps + 1;
        IFEND;
      FOREND;
    IFEND;

    { A new buffer is not created if the new enlarged buffer size is smaller then the original System
    { Message buffer.

    new_buffer_size := (((number_of_pps * new_buffer_size_multiplier) + (osv$page_size - 1)) DIV
          osv$page_size) * osv$page_size;
    IF new_buffer_size <= dsv$sys_msg_buffer_size THEN
      RETURN; {----->
    IFEND;

    { Save a pointer to the original System Message buffer.

    old_buffer_seq_p := dsv$sys_msg_buffer_desc_p^.cm_start_of_buffer_p;

    { Create the enlarged System Message buffer and call monitor to enlarge the buffer and move all
    { the data from the old buffer to the new enlarged buffer.

    rb.sys_msg_new_buffer_size := new_buffer_size;
    ALLOCATE rb.sys_msg_add_data_seq_p: [[REP rb.sys_msg_new_buffer_size OF cell]] IN
          osv$mainframe_wired_heap^;
    pmp$zero_out_table (rb.sys_msg_add_data_seq_p, rb.sys_msg_new_buffer_size);
    rb.reqcode := syc$rc_logging_request;
    rb.action := dsc$rla_sys_msg_enlarge_buffer;
    i#call_monitor (#LOC (rb), #SIZE (rb));

    { Free the original System Message buffer from mainframe wired.

    FREE old_buffer_seq_p IN osv$mainframe_wired_heap^;

  PROCEND dsp$enlarge_sys_msg_buffer;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$log_sys_msg_help', EJECT ??

{ PURPOSE:
{   This procedure allows a job template procedure to call monitor to put a message on the System
{   Message buffer if logging messages is not allowed.

  PROCEDURE [XDCL, #GATE] dsp$log_sys_msg_help
    (    message_type: integer;
     VAR log_data_p: ^SEQ ( * ));

    VAR
      data_to_log_p: ^SEQ ( * ),
      message_type_to_log_p: ^integer,
      rb: dst$rb_logging_request,
      sys_msg_data_p: ^SEQ ( * ),
      sys_msg_header_p: ^dst$system_message_header;

    { Create a dummy data record for the callers that do not have any extra data to pass along to the
    { logging procedure.

    IF log_data_p = NIL THEN
      PUSH data_to_log_p: [[REP 1 OF integer]];
    ELSE
      data_to_log_p := log_data_p;
    IFEND;
    RESET data_to_log_p;

    { Allocate space in mainframe wired to hold the message to be put on the buffer.

    ALLOCATE rb.sys_msg_add_data_seq_p: [[REP (#SIZE (data_to_log_p^) + #SIZE (message_type) +
          #SIZE (dst$system_message_header)) OF cell]] IN osv$mainframe_wired_heap^;
    RESET rb.sys_msg_add_data_seq_p;

    { Put the message to be stored on the buffer in the allocated space.

    NEXT sys_msg_header_p IN rb.sys_msg_add_data_seq_p;
    sys_msg_header_p^.message_size := #SIZE (data_to_log_p^) + #SIZE (message_type);
    sys_msg_header_p^.message_type := dsc$general_system_message;
    sys_msg_header_p^.message_level := dsc$informative_message;
    NEXT message_type_to_log_p IN rb.sys_msg_add_data_seq_p;
    message_type_to_log_p^ := message_type;
    NEXT sys_msg_data_p: [[REP #SIZE (data_to_log_p^) OF cell]] IN rb.sys_msg_add_data_seq_p;
    sys_msg_data_p^ := data_to_log_p^;
    RESET rb.sys_msg_add_data_seq_p;

    { Call monitor to put the data on the buffer.

    rb.reqcode := syc$rc_logging_request;
    rb.action := dsc$rla_sys_msg_add_message;
    i#call_monitor (#LOC (rb), #SIZE (rb));
    FREE rb.sys_msg_add_data_seq_p IN osv$mainframe_wired_heap^;

  PROCEND dsp$log_sys_msg_help;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$manage_dftb_space_in_mfw', EJECT ??

{ PURPOSE:
{   This procedure allocates space or frees space in mainframe wired for logging DFT requests for a job
{   template routine.

  PROCEDURE [XDCL, #GATE] dsp$manage_dftb_space_in_mfw
    (    action: dst$dftb_manage_space;
     VAR space_seq_p: ^SEQ ( * );
     VAR request_completed: boolean);

    VAR
      local_space_seq_p: ^SEQ ( * ),
      space_size: integer;

    request_completed := FALSE;

    local_space_seq_p := space_seq_p;

    IF action = dsc$dftb_allocate_space THEN
      IF dsv$dftb_data.revision_level <= dsc$dftb_revision_level_3 THEN
        space_size := dsv$dftb_data.mrb_length;
      ELSE
        space_size := dsv$dftb_data.mrb_length + dsv$dftb_data.ssb_length + dsv$dftb_data.mdb_length +
              dsv$dftb_data.nrb_length;
      IFEND;
      IF space_size = 0 THEN
        RETURN; {----->
      IFEND;
      ALLOCATE local_space_seq_p: [[REP space_size OF integer]] IN osv$mainframe_wired_heap^;
      pmp$zero_out_table (local_space_seq_p, (space_size * 8));
      space_seq_p := local_space_seq_p;
      RESET space_seq_p;
    ELSE
      FREE local_space_seq_p IN osv$mainframe_wired_heap^;
      space_seq_p := NIL;
    IFEND;

    request_completed := TRUE;

  PROCEND dsp$manage_dftb_space_in_mfw;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$retrieve_system_ds_status', EJECT ??

{ PURPOSE:
{   This procedure calls monitor to retrieve the System Deadstart Status from the SSR.  It also moves the
{   data from the mainframe wired pointer to the SSR and Frees the space used by the mainframe wired pointer.

  PROCEDURE [XDCL, #GATE] dsp$retrieve_system_ds_status
    (VAR data: dst$ssr_system_deadstart_status);

    VAR
      rb: dst$rb_system_deadstart_status;

    { Retrieve the system deadstart status from the SSR.

    rb.reqcode := syc$rc_system_deadstart_status;
    rb.status.normal := TRUE;
    rb.action := dsc$rb_sds_retrieve_data;
    ALLOCATE rb.data_p IN osv$mainframe_wired_heap^;
    pmp$zero_out_table (rb.data_p, #SIZE (rb.data_p^));
    i#call_monitor (#LOC (rb), #SIZE (rb));

    data := rb.data_p^;

    FREE rb.data_p IN osv$mainframe_wired_heap^;
    FREE dsv$system_deadstart_status_p IN osv$mainframe_wired_heap^;

  PROCEND dsp$retrieve_system_ds_status;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$set_record_errors_flag', EJECT ??

{ PURPOSE:
{   This procedure sets a boolean variable that allows the system messages to be logged onto the engineering
{   log.  This procedure is called by job template procedure which can not change a variable that is created
{   in monitor directly.

  PROCEDURE [XDCL, #GATE] dsp$set_record_errors_flag;

    dsv$record_errors := TRUE;

  PROCEND dsp$set_record_errors_flag;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$store_sys_msg_in_image', EJECT ??

{ PURPOSE:
{   This procedure is called during recovery to store the information that was in the system messages buffer
{   in 'old' mainframe wired.  This information is stored in rdf_system_message_buffer in the image file.

  PROCEDURE [XDCL, #GATE] dsp$store_sys_msg_in_image;

    VAR
      add_data_ptr_offset: integer,
      adj_sys_msg_desc_p: ^dst$sys_msg_buffer_desc,
      ignore_data_p: ^SEQ ( * ),
      image_descriptor: dst$nve_image_descriptor,
      local_status: ost$status,
      old_wired_segment_p: ^SEQ ( * ),
      rdf_file_segment: mmt$segment_pointer,
      rdf_file_sfid: gft$system_file_identifier,
      rdf_pointers: dst$rdf_pointers,
      rdf_sys_msg_data_p: ^SEQ ( * ),
      rdf_sys_msg_header_p: ^dst$system_message_header,
      rdf_sys_msg_seq_p: ^SEQ ( * ),
      remove_data_ptr_offset: integer,
      sys_msg_buffer_size: integer,
      sys_msg_data_p: ^SEQ ( * ),
      sys_msg_desc_pp: ^^dst$sys_msg_buffer_desc,
      sys_msg_desc_seq_p: ^SEQ ( * ),
      sys_msg_header_p: ^dst$system_message_header,
      sys_msg_seq_p: ^SEQ ( * ),
      temp_old_seq_p: ^cell,
      valid_data_seq_p: ^SEQ ( * ),
      valid_data_size_p: ^integer;

    { Retrieve the size of the buffer that is stored in the RDF area and the buffer.

    dsp$get_integer_from_rdf (dsc$rdf_sys_msg_buffer_size, dsc$rdf_system_message_buffer,
          sys_msg_buffer_size);

    dsp$open_rdf (rdf_file_sfid, rdf_file_segment, rdf_pointers);
    dsp$get_rdf_entry_seq_pointer (dsc$rdf_system_messages_buffer, dsc$rdf_system_message_buffer,
          rdf_pointers, rdf_sys_msg_seq_p);
    RESET rdf_sys_msg_seq_p;

    { This integer holds the size of the valid data currently stored on the 'RDF'.

    NEXT valid_data_size_p IN rdf_sys_msg_seq_p;

    IF (valid_data_size_p^ > 0) AND (sys_msg_buffer_size > 0) AND
          (valid_data_size_p^ <= sys_msg_buffer_size) THEN

      { Valid data still exists on the image file.  This valid data MUST NOT be written over.  Advance the
      { pointer to the sequence past this valid data.

      NEXT valid_data_seq_p: [[REP valid_data_size_p^ OF cell]] IN rdf_sys_msg_seq_p;
    ELSE

      { No valid data exists on the image file.  Compute the amount of space available on the 'RDF' for the
      { buffer and store this value on the 'RDF'.

      sys_msg_buffer_size := #SIZE (rdf_sys_msg_seq_p^);
      dsp$store_integer_in_rdf (dsc$rdf_sys_msg_buffer_size, dsc$rdf_system_message_buffer,
            sys_msg_buffer_size);
      valid_data_size_p^ := 0;
    IFEND;

    { Save the information from the 'old' mainframe_wired_heap in the image file.

    dsp$get_nve_image_description (image_descriptor);
    old_wired_segment_p := image_descriptor.rcv_mainframe_wired_segment;

    IF old_wired_segment_p <> NIL THEN

      { Retrieve the sequence that contains the pointers to the System Message buffer to the old mainframe
      { wired heap.

      dsp$fetch_list_block (dsc$system_messages_buffer, sys_msg_desc_seq_p);

      { Change the ring and segment number of the sequence pointer to that of the recovered (old) mainframe
      { wired and build a sequence pointer with the new address.

      i#build_adaptable_seq_pointer (#RING (old_wired_segment_p), #SEGMENT (old_wired_segment_p),
            #OFFSET (sys_msg_desc_seq_p), #SIZE (sys_msg_desc_seq_p^), 0, sys_msg_desc_seq_p);
      RESET sys_msg_desc_seq_p;
      NEXT sys_msg_desc_pp IN sys_msg_desc_seq_p;

      { Change the ring and segment number of the pointers to the System Message buffer to that of the
      { recovered (old) mainframe wired.

      adj_sys_msg_desc_p := #ADDRESS (#RING (old_wired_segment_p), #SEGMENT (old_wired_segment_p),
            #OFFSET (sys_msg_desc_pp^));

      { Create a sequence pointer to the start of the System Message buffer.

      temp_old_seq_p := #ADDRESS (#RING (old_wired_segment_p), #SEGMENT (old_wired_segment_p),
            #OFFSET (adj_sys_msg_desc_p^.cm_start_of_buffer_p));
      dsp$build_sequence_p (temp_old_seq_p, adj_sys_msg_desc_p^.sys_msg_buffer_size, sys_msg_seq_p);

      { If data exists on the System Message buffer in the recovered (old) mainframe wired, move the data to
      { the 'RDF' area.

      add_data_ptr_offset := adj_sys_msg_desc_p^.add_data_ptr_offset;
      remove_data_ptr_offset := adj_sys_msg_desc_p^.remove_data_ptr_offset;
    ELSE
      add_data_ptr_offset := 0;
      remove_data_ptr_offset := 0;
    IFEND;

    IF add_data_ptr_offset <> remove_data_ptr_offset THEN

      { Set the sequence so the pointer points to the remove data offset.

      IF i#current_sequence_position (sys_msg_seq_p) <> remove_data_ptr_offset THEN
        NEXT ignore_data_p: [[REP remove_data_ptr_offset OF cell]] IN sys_msg_seq_p;
      IFEND;

    /move_loop/
      WHILE TRUE DO

        { Check if all of the data has been removed from the buffer.

        IF (add_data_ptr_offset > remove_data_ptr_offset) AND
              (i#current_sequence_position (sys_msg_seq_p) >= add_data_ptr_offset) THEN
          EXIT /move_loop/; {----->
        IFEND;

        { Check if there is enough space on the 'RDF' area to store more messages.

        NEXT sys_msg_header_p IN sys_msg_seq_p;
        IF (valid_data_size_p^ + #SIZE (valid_data_size_p^) + #SIZE (dst$system_message_header) +
              sys_msg_header_p^.message_size) >= sys_msg_buffer_size THEN
          EXIT /move_loop/; {----->
        IFEND;

        { If the message size is zero, reset the sequence and access the data from the beginning.

        IF sys_msg_header_p^.message_size = 0 THEN
          RESET sys_msg_seq_p;
          remove_data_ptr_offset := i#current_sequence_position (sys_msg_seq_p);
          CYCLE /move_loop/; {----->
        IFEND;

        { Move the message from the buffer to the 'RDF' area.

        NEXT sys_msg_data_p: [[REP sys_msg_header_p^.message_size OF cell]] IN sys_msg_seq_p;
        NEXT rdf_sys_msg_header_p IN rdf_sys_msg_seq_p;
        NEXT rdf_sys_msg_data_p: [[REP sys_msg_header_p^.message_size OF cell]] IN rdf_sys_msg_seq_p;
        rdf_sys_msg_header_p^ := sys_msg_header_p^;
        rdf_sys_msg_data_p^ := sys_msg_data_p^;
        valid_data_size_p^ := valid_data_size_p^ + #SIZE (dst$system_message_header) +
              sys_msg_header_p^.message_size;
      WHILEND /move_loop/;
    IFEND;

    mmp$write_modified_pages (rdf_sys_msg_seq_p, #SIZE (rdf_sys_msg_seq_p^), osc$wait, local_status);
    IF NOT local_status.normal THEN
      osp$system_error (' Cannot write modified pages (dsm$log_sys_msgs_helper)', ^local_status);
    IFEND;

    dsp$close_rdf (rdf_file_sfid, rdf_file_segment);

  PROCEND dsp$store_sys_msg_in_image;
MODEND dsm$log_sys_msgs_helper;
