?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : Deadstart Services Monitor Routines' ??
MODULE dsm$deadstart_services_monitor;

{ PURPOSE:
{   This module contains deadstart procedures which run in monitor mode.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cml$pp_timed_out
*copyc dst$cpu_pp_communication_block
*copyc dst$log_pp_timed_out
*copyc dst$mainframe_type
*copyc dst$signal_contents
*copyc dst$sub_mainframe_type
*copyc oss$mainframe_wired
*copyc oss$mainframe_wired_cb
*copyc ost$system_flag
?? POP ??
*copyc dpp$display_error
*copyc dsp$convert_seq_p_to_r_pointer
*copyc dsp$mtr_get_ssr_data_seq_ptr
*copyc dsp$report_system_message
*copyc mtp$get_date_time_at_timestamp
*copyc tmp$send_signal
?? EJECT ??
*copyc dsv$dftb_data
*copyc dsv$dfts_control_word_p
*copyc mtv$dft_block_p
*copyc mtv$nst_p
*copyc mtv$scb
*copyc mtv$time_to_call_handshaking
*copyc tmv$system_job_monitor_gtid
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    c$next_check = 2 * 1000000;       {2 seconds.

  TYPE
    t$pp_timed_out = RECORD
      sci_timed_out: boolean,
      dft_timed_out: boolean,
      dfts_timed_out_or_not_there: boolean,
    RECEND;

  VAR
    dsv$cpu_pp_communication_block: [XDCL, #GATE, oss$mainframe_wired_cb] dst$cpu_pp_communication_block :=
          [[FALSE, 0, FALSE, FALSE, FALSE, 0, 0, FALSE, FALSE, FALSE, 0, 0, 0, 0, 0], 0, 0, 0, 0],
    dsv$mainframe_type: [XDCL, #GATE] dst$mainframe_type := dsc$mt_unknown_mainframe,
    dsv$sub_mainframe_type: [XDCL, #GATE] dst$sub_mainframe_type := dsc$smt_unknown_mainframe,

    v$pps_timed_out: [oss$mainframe_wired] t$pp_timed_out := [FALSE, FALSE, FALSE];
?? OLDTITLE ??
?? NEWTITLE := 'initialize_cpu_pp_comm', EJECT ??

{ PURPOSE:
{   This procedure initializes the CPU/PP communication block and stores an R-pointer to it in EICB word
{   D8RLP.  The procedure must not be executed until the secondary DFT is loaded (if there is one).

  PROCEDURE initialize_cpu_pp_comm;

    VAR
      dft_verified: boolean,
      dfts_buffer_p: ^dst$ssr_dfts_buffer,
      dfts_buffer_seq_p: ^SEQ ( * ),
      free_running_clock: integer;

    { Initialize relocation word.

    dsv$cpu_pp_communication_block.relocation.initialized := TRUE;
    dsv$cpu_pp_communication_block.relocation.dft_pp_number := mtv$dft_block_p^.dft_pp_number;
    dsv$cpu_pp_communication_block.relocation.dft_pp_at_deadstart := mtv$dft_block_p^.dft_pp_number;
    dsv$cpu_pp_communication_block.relocation.sci_pp_number := mtv$nst_p^.d8st.sci_pp_number;
    dsv$cpu_pp_communication_block.relocation.sci_pp_at_deadstart := mtv$nst_p^.d8st.sci_pp_number;

    { Initialize free running clock words.

    free_running_clock := #FREE_RUNNING_CLOCK (0);
    dsv$cpu_pp_communication_block.monitor_time := free_running_clock;
    dsv$cpu_pp_communication_block.sci_time := free_running_clock;
    dsv$cpu_pp_communication_block.dft_time := free_running_clock;

    { Determine if a secondary IOU exists by checking if the secondary DFT has checked in.

    IF dsv$dftb_data.revision_level <= dsc$dftb_revision_level_5 THEN
      dsp$mtr_get_ssr_data_seq_ptr (dsc$ssr_secondary_dft_block, dfts_buffer_seq_p);
      NEXT dfts_buffer_p IN dfts_buffer_seq_p;
      dft_verified := dfts_buffer_p^.control_word.dft_verification;
    ELSEIF dsv$dfts_control_word_p <> NIL THEN
      dft_verified := dsv$dfts_control_word_p^.dft_verification;
    ELSE
      dft_verified := FALSE;
    IFEND;
    IF dft_verified THEN
      dsv$cpu_pp_communication_block.dfts_time := free_running_clock;
    ELSE
      v$pps_timed_out.dfts_timed_out_or_not_there := TRUE;
    IFEND;

    { Set monitor update time to current time + 5 seconds.

    mtv$time_to_call_handshaking := free_running_clock + c$next_check;

    { Set EICB pointer word.

    dsp$convert_seq_p_to_r_pointer (#SEQ (dsv$cpu_pp_communication_block),
          mtv$nst_p^.dfcm9.cpu_pp_communication_buffer);

  PROCEND initialize_cpu_pp_comm;
?? OLDTITLE ??
?? NEWTITLE := 'issue_pp_timeout_message', EJECT ??

{ PURPOSE:
{   This procedure issues a PP time out message to the display console critical window and to the
{   Engineering Log.

  PROCEDURE issue_pp_timeout_message
    (    free_running_clock_stamp: integer,
         pp_name: string (5));

    TYPE
      eng_log_msg = RECORD
        message_type: integer,
        message_data: dst$log_pp_timed_out,
      RECEND;

    VAR
      critical_window_message: string (29),
      date_time: ost$date_time,
      eng_log_message: eng_log_msg,
      msg_recorded: boolean;

    { Issue message to display console critical window.

    critical_window_message (1, 14) := 'ERR=VEOS6000- ';
    critical_window_message (15, 5) := pp_name;
    critical_window_message (20, 10) := ' TIMED OUT';
    dpp$display_error (critical_window_message);

    { Issue message to Engineering Log.

    mtp$get_date_time_at_timestamp (free_running_clock_stamp, date_time);
    eng_log_message.message_type := cml$pp_timed_out;
    eng_log_message.message_data.date_time := date_time;
    eng_log_message.message_data.pp_name := pp_name;
    dsp$report_system_message (#SEQ (eng_log_message), dsc$general_system_message, dsc$fatal_error,
          msg_recorded);

  PROCEND issue_pp_timeout_message;
?? OLDTITLE ??
?? NEWTITLE := 'dsp$perform_cpu_pp_handshaking', EJECT ??

{ PURPOSE:
{   This procedure performs CPU/PP handshaking for monitor.  It updates the monitor time word from the free
{   running clock.  Then it checks the PP time words for PP timeout (thirty seconds since a PP updated its
{   time word).
{
{ NOTE:
{   The timeout period should be 30 seconds (5 minutes on the Cyber 2000).   However, the CPU/PP handshaking
{   design does not cover the case where an operator types control-G, control-R and looks at the TPM displays
{   for a long period of time.  In this case, DFT and SCI would hang waiting for channel 17 and monitor would
{   think they timed out.  Therefore, until this design hole is fixed, the timeout period is set to infinite.
{   When fixed the constant time_out_period should be changed to 30 * 1000000.

  PROCEDURE [XDCL] dsp$perform_cpu_pp_handshaking;

    CONST
      c$cy2000_time_out_period = 300 * 1000000,    { 5 minutes
      c$time_out_period = 0ffffffffffff(16);      { infinite - see note above

    VAR
      free_running_clock: integer,
      ignore_status: syt$monitor_status,
      signal: dst$signal_contents,
      time_out_period: integer,
      time_since_pp_checkin: integer;

    { Initialize CPU/PP communication if this is the first call.

    IF NOT dsv$cpu_pp_communication_block.relocation.initialized THEN
      initialize_cpu_pp_comm;
    IFEND;

    { Do not allow heartbeat to be updated if a fatal software or hardware error exists.

    IF dsv$mainframe_type = dsc$mt_2000_mainframe THEN
      IF (mtv$scb.nos_180_status.idle_code = syc$ic_system_terminated) OR
            (mtv$scb.nos_180_status.idle_code = syc$ic_fatal_software_error) OR
            (mtv$scb.nos_180_status.idle_code = syc$ic_fatal_hardware_error) OR
            (mtv$scb.nos_180_status.idle_code = syc$ic_hardware_idle) THEN
        RETURN;
      IFEND;
    IFEND;

    IF dsv$mainframe_type = dsc$mt_2000_mainframe THEN
      time_out_period := c$cy2000_time_out_period;
    ELSE
      time_out_period := c$time_out_period;
    IFEND;

    { Update the monitor time word.

    free_running_clock := #free_running_clock(0);
    dsv$cpu_pp_communication_block.monitor_time := free_running_clock;

    { Check the SCI time word if SCI not previously timed out.

    IF NOT v$pps_timed_out.sci_timed_out THEN
      time_since_pp_checkin := free_running_clock - dsv$cpu_pp_communication_block.sci_time;
      IF time_since_pp_checkin > time_out_period THEN
        issue_pp_timeout_message (dsv$cpu_pp_communication_block.sci_time, 'SCI  ');
        v$pps_timed_out.sci_timed_out := TRUE;
      IFEND;
    IFEND;

    { Check the DFT time word if DFT not previously timed out.

    IF NOT v$pps_timed_out.dft_timed_out THEN
      time_since_pp_checkin := free_running_clock - dsv$cpu_pp_communication_block.dft_time;
      IF time_since_pp_checkin > time_out_period THEN
        IF dsv$mainframe_type = dsc$mt_2000_mainframe THEN
          issue_pp_timeout_message (dsv$cpu_pp_communication_block.dft_time, 'SP   ');
          signal.identifier := dsc$deadstart_signal;
          signal.contents.kind := dsc$signal_post_operator_action;
          mtp$get_date_time_at_timestamp (dsv$cpu_pp_communication_block.dft_time,
                signal.contents.poa_data.date_time);
          signal.contents.poa_data.kind := dsc$signal_poa_sp_timeout;
          tmp$send_signal (tmv$system_job_monitor_gtid, signal.signal, ignore_status);
        ELSE
          issue_pp_timeout_message (dsv$cpu_pp_communication_block.dft_time, 'DFT  ');
        IFEND;
        v$pps_timed_out.dft_timed_out := TRUE;
      IFEND;
    IFEND;

    { Check the DFT-S time word if DFT-S exists and has not previously timed out.

    IF NOT v$pps_timed_out.dfts_timed_out_or_not_there THEN
      time_since_pp_checkin := free_running_clock - dsv$cpu_pp_communication_block.dfts_time;
      IF time_since_pp_checkin > time_out_period THEN
        issue_pp_timeout_message (dsv$cpu_pp_communication_block.dft_time, 'DFT-S');
        v$pps_timed_out.dfts_timed_out_or_not_there := TRUE;
      IFEND;
    IFEND;

    { Set monitor update time for next CPU/PP handshaking call.

    mtv$time_to_call_handshaking := free_running_clock + c$next_check;

  PROCEND dsp$perform_cpu_pp_handshaking;
MODEND dsm$deadstart_services_monitor;
