?? RIGHT := 110 ??
?? NEWTITLE := 'MTM$PROCESSOR_CONFIGURATION_MGR: Software reconfiguration of Central Processing Units' ??
MODULE mtm$processor_configuration_mgr;
?? RIGHT := 110 ??

{ PURPOSE:
{   The purpose of this module is to provide the software interfaces necessary to reconfigure a Central
{   Processing Unit (CPU) in a 180 mainframe.  These interfaces provide the following capabilities:
{     . Deconfiguration of a CPU from an ON state to a DOWN state without interrupting the system if there are
{       multiple CPUs running on the mainframe.
{     . A check of the system for a CPU which has been halted due to an error in the CPU which has been
{       detected by Dedicated Fault Tolerance (DFT).
{     . A check of the system for a CPU which has timed out; i.e. has not done any useful work in the
{       recent past.
{     . Reconfiguration of a CPU which is DOWN to an ON state without system interruption.
{
{ DESIGN:
{   This module was developed in response to DAP ARH7896: DISABLE_FAILING_CPU.  It was updated for the
{   feature CPU_REINSTATEMENT (Design Direction A9154).  For DISABLE_FAILING_CPU, the procedures are called
{   in the following manner:
{
{   - DFT processing:
{     If Dedicated Fault Tolerance detects a fatal cpu condition such as a halted CPU, it will inform the
{     system (using the logging interfaces via the SCB and the CST) of the "dead" CPU.  Another "live" CPU
{     will process the entire deconfiguration.
{
{   - DUE processing:
{     If the system detects a Detected Uncorrected Error (DUE), the CPU with the DUE puts itself in a "quiet"
{     state, and another CPU attempts to reconfigure the failing CPU out of the configuration.
{
{   - CPU timeout processing:
{     If a CPU has timed out without DFT noticing this fact, another "live" CPU will eventually discover that
{     the "dead" CPU is no longer updating a timestamp in the "dead" CPU's CST.  A "live" CPU will process the
{     entire deconfiguration.
{
{   - LCU command processing:
{     If the system operator has entered a command to DOWN a CPU element via LCU, the CPU which is targeted
{     will attempt to process part of its own deconfiguration by checking whether or not it is safe to do so.
{     A "live" CPU will finish the deconfiguration processing.
{
{   If a "live" CPU must process the entire deconfiguration (as in DUE, DFT, and Timeout processing), it
{   checks to see if the CPU deconfiguration is safe.  If the deconfiguration can be performed, the "live"
{   CPU changes system tables (such as the CPU state table) to reflect the loss of the CPU.  The live CPU then
{   informs the system job monitor task to finish the deconfiguration in job mode.  If the deconfiguration
{   cannot be performed, the system will perform either a system STEP or a system IDLE, depending on the
{   severity of the CPU failure.
{
{   If a CPU must process the "back-end" of another CPU's deconfiguration (as in LCU processing), the "live"
{   CPU changes system tables (such as the CPU state table) to reflect the loss of the CPU.  The CPU then
{   informs the system job monitor task to finish the deconfiguration in job mode.
{
{   For CPU_REINSTATEMENT, the processing is essentially the reverse of the LCU method of DISABLE_FAILING_CPU.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dst$signal_contents
*copyc dst$system_logging_types
*copyc dst$cpu_attributes
*copyc mtt$monitor_xp_slot_pointers
*copyc osc$monitor_stack_mult
*copyc osc$processor_defined_registers
*copyc osc$purge_map_and_cache
*copyc oss$mainframe_wired_literal
*copyc oss$mainframe_wired
*copyc ost$exchange_package
*copyc ost$monitor_stack
*copyc ost$processor_id
*copyc syc$monitor_request_codes
*copyc syt$monitor_flag
*copyc syt$monitor_status
*copyc tmt$mcr_faults
?? POP ??
*copyc dsp$get_cpu_attributes
*copyc dpp$change_sci_interrupt_port
*copyc dpp$display_error
*copyc i#mtr_disable_traps
*copyc i#program_error
*copyc i#real_memory_address
*copyc mtp$abort_task_with_due
*copyc mtp$cst_p
*copyc mtp$error_stop
*copyc mtp$get_date_time_at_timestamp
*copyc mtp$record_critical_hdw_status
*copyc mtp$spin_cpu
*copyc mtp$step_unstep_system
*copyc mtp$store_informative_message
*copyc osp$alert_keyp_cpu_state_chng
*copyc tmp$send_signal
*copyc tmp$set_monitor_flag
*copyc tmp$switch_task_from_failing_cp
*copyc tmp$update_job_task_cpu_selects
?? EJECT ??
*copyc dpv$lock
*copyc dsv$boot_control_table_p
*copyc dsv$mainframe_type
*if $variable(mmv$test_forced_use_cache_maps, declared) <> 'UNKNOWN'
*copyc mmv$force_use_of_cache_and_maps
*else
{ -------- Declarations for forcing the use of cache and maps omitted at compile time --------
*ifend
*copyc mmv$multiple_caches
*copyc mmv$multiple_page_maps
*copyc mtv$cst0
*copyc mtv$first_cpu_monitor_stack_p
*copyc mtv$request_interlock_table
*copyc mtv$scb
*copyc mtv$step_lock_0
*copyc mtv$step_lock_1
*copyc mtv$step_lock_2
*copyc mtv$unstep_lock_0
*copyc mtv$unstep_lock_1
*copyc mtv$unstep_lock_2
*copyc osv$cpus_logically_on
*copyc osv$cpus_physically_configured
*copyc osv$external_interrupt_selector
*copyc osv$initial_monitor_xp
*copyc osv$multiprocessor_running
*copyc tmv$multiple_cpus_active
*copyc tmv$ptl_lock
*copyc tmv$system_job_monitor_gtid
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    c$cpu_timeout_interval = 30000000, {microseconds}
    c$monitor_stack_frame_length = 20(16);

  TYPE
    t$translate_reason_to_ascii = record
      reason: string (22),
      initiator: string (21),
    recend;

?? EJECT ??

  VAR
    null_sva: 0 .. 0ffffffffffff(16);

  VAR
    mtv$cpu_test_flag: [XREF] boolean,
    mtv$recovery_lock1: [XREF] boolean,
    mtv$recovery_lock3: [XREF] boolean,
    mtv$async_lock: [XREF] boolean,
    mtv$no_stop: [XREF] integer,
    mtv$all_cpus_have_been_started: [XDCL, #GATE] boolean := FALSE,
    mtv$fault_injection_flag: [XREF] boolean,
    mtv$halt_on_cpu_timeout: [XDCL, #GATE] boolean := FALSE,
    mtv$monitor_xp_slot_pointers: [XDCL, #GATE] mtt$monitor_xp_slot_pointers := [NIL, NIL],
    mtv$operator_console_hung: [XDCL] boolean := FALSE,
    mtv$reset_all_cache_now: [XDCL, #GATE] boolean := FALSE,
    mtv$cy2000_sp_recovery: [XDCL, #GATE] boolean := FALSE,
    mtv$failing_task_xcb: [XDCL, #GATE] ^ost$execution_control_block := NIL,
    mtv$failing_ajlo: [XDCL, #GATE] jmt$ajl_ordinal := 0,
    mtv$failing_ijle_p: [XDCL, #GATE] ^jmt$initiated_job_list_entry := NIL,
    mtv$failing_task_identifier: [XDCL, #GATE] ost$global_task_id := [0, 0],
{   mtv$ptl_recovery_info: [XDCL, #GATE] mtt$ptl_recovery_info,

    v$translate_reason_to_ascii: [STATIC, READ, oss$mainframe_wired_literal] array
          [ost$cpu_down_state_reason] of t$translate_reason_to_ascii := [
          { osc$cdsr_null                   } ['                      ', '                     '],
          { osc$cdsr_downed_by_dft          } [' ERROR DETECTED BY DFT', 'Initiated by DFT     '],
          { osc$cdsr_due_threshold_exceeded } ['  TOO MANY DUES IN CPU', 'Initiated by System  '],
          { osc$cdsr_cpu_timeout            } ['  CPU DETECTED TIMEOUT', 'Initiated by System  '],
          { osc$cdsr_downed_by_operator     } ['    DOWNED BY OPERATOR', 'Initiated by Operator'],
          { osc$cdsr_downed_by_system       } ['      DOWNED BY SYSTEM', 'Initiated by System  '],
          { osc$cdsr_downed_by_fault_inject } ['         DOWNED BY SAM', 'Initiated by System  ']],

    v$translate_state_to_ascii: [STATIC, READ, oss$mainframe_wired_literal] array [cmt$element_state] of
          string (5) := ['n  ON', 'n OFF', ' DOWN'];

?? OLDTITLE ??
?? NEWTITLE := 'check_for_fatal_errors', EJECT ??

{ PURPOSE:
{   This procedure checks for errors which would be considered fatal to normal system operations if the
{   processor with the specified id were to meet any of the conditions listed below:
{     . TMV$NEW_PTL_LOCK set
{     . DPV$LOCK set
{     . other locks set internally by procedures which don't set the Request_Table interlock
{     . MTV$REQUEST_INTERLOCK_TABLE lock set itself
{
{   In part, this procedure checks interlock ordinals of monitor requests to see if any locks are set by the
{   processor whose ID is passed into this procedure.  If any lock is set, a fatal error is returned since the
{   system has no knowledge of how to back a CPU out of the processing it was performing when it encountered
{   conditions which cause it to be deconfigured.
{
{   The only reason that locks may be set is that a DUE occurred in monitor mode when such locks can be set.
{   The processor which is executing this code may or may not be the CPU which is being deconfigured, but it
{   cannot be the CPU in which the DUE occurred.

  PROCEDURE check_for_fatal_errors
    (    processor_id: ost$processor_id;
     VAR fatal_error: boolean;
     VAR fatal_error_message: string ( * <= 71));

    VAR
      base_constant: integer,
      cst_p: ^ost$cpu_state_table,
      null_cpu: [STATIC, READ] ost$pva := [0, 0, 0],
      interlock_table_index: 0 .. mtc$maximum_il_table_index,
{     local_status: ost$status,
      message: string (71);

    fatal_error := FALSE;
    fatal_error_message := ' ';
{   local_status.normal := TRUE;

{ Check the monitor request table interlocks and other system table locks.
    cst_p := ^mtv$cst0 [processor_id];
    base_constant := #OFFSET (cst_p);

    { Check the Primary_Task_List (PTL) lock and the system console lock.

    IF tmv$ptl_lock.id = base_constant THEN
*if true
      dpp$display_error ('Cannot recover from PTL Lock');
      fatal_error := TRUE;
      fatal_error_message := 'CPU failure: cannot recover from interlocked table condition';
      RETURN; {----->
*else
      dpp$display_error ('Attempting PTL recovery');
      mtp$recover_ptl (cst_p, local_status);
      IF NOT local_status.normal THEN
        fatal_error := TRUE;
        fatal_error_message := 'CPU failure: cannot recover from interlocked table condition';
        RETURN; {----->
      IFEND;
*ifend
    IFEND;

    IF dpv$lock = base_constant THEN

{ Clear the lock regardless of who has it. I think that the worst that can
{ happen is we scramble the console display.

      dpv$lock := 0;

    IFEND;

    IF (mtv$step_lock_0 = base_constant) OR (mtv$step_lock_1 = base_constant) OR
          (mtv$step_lock_2 = base_constant) OR (mtv$unstep_lock_0 = base_constant) OR
          (mtv$unstep_lock_1 = base_constant) OR (mtv$unstep_lock_2 = base_constant) THEN
{ A CPU failed while the system was trying to step. Since we were going to stop anyway,
{ error stop it here.
      fatal_error := TRUE;
      fatal_error_message := 'CPU failure: damaged system tables with tables interlocked';
      RETURN; {----->
    IFEND;

  PROCEND check_for_fatal_errors;
?? OLDTITLE ??
?? NEWTITLE := 'process_deconfigure_cpu', EJECT ??

{ PURPOSE:
{   This procedure determines whether or not it is permissible to deconfigure a CPU.  Permissible is defined
{   as: "does not generate a system interrupt once the CPU is removed from the configuration".  This procedure
{   checks the following conditions:
{     . The number of CPUs which are logically "on".
{     . Other possibly fatal errors (see CHECK_FOR_FATAL_ERRORS).
{   After determining whether or not it is permissible to deconfigure a CPU, this procedure either steps the
{   system, idles the system, or allows the CPU to be deconfigured.  If the CPU to be deconfigured is
{   executing this procedure, it will be put into a quiescent state and will wait for another CPU to change
{   its state in the Mainframe Reconfiguration Table (MRT).  If the CPU which is executing this code is not
{   the CPU to be deconfigured, then it is executing on behalf of a CPU which may (or may not) have been in
{   quiet state.  It is expected that, by the time execution reaches this point, the CPU which is to be
{   deconfigured is either "dead" (halted) or spinning quietly and waiting for final deconfiguration.

  PROCEDURE [XDCL] process_deconfigure_cpu
    (    cst_p: ^ost$cpu_state_table;
         cpu_state_reason: ost$cpu_down_state_reason);

    TYPE
      t$cpu_deconfiguration_result = (c$cdr_null, c$cdr_system_step, c$cdr_system_idle,
            c$cdr_deconfiguration_ok);

    VAR
      cpu_index: ost$processor_id,
      deconfiguration_result: t$cpu_deconfiguration_result,
      fatal_error: boolean,
      message: string (71),
      old_trap_enable: 0 .. 3,
      running_cst_p: ^ost$cpu_state_table;

    { If the mainframe has only one running CPU at this moment, halt the system immediately.

    IF dsv$mainframe_type <> dsc$mt_2000_mainframe THEN

{ cy2000 mainframes deconfigure after the processor has been turned down.

      IF osv$cpus_logically_on < 2 THEN
        mtp$error_stop ('Deconfigure single CPU');
      IFEND;
    IFEND;


    deconfiguration_result := c$cdr_null;
    check_for_fatal_errors (cst_p^.cst_index, fatal_error, message);
    IF fatal_error THEN
      dpp$display_error (message);
      deconfiguration_result := c$cdr_system_step;
    ELSE
      deconfiguration_result := c$cdr_deconfiguration_ok;
    IFEND;

    IF (cpu_state_reason = osc$cdsr_cpu_timeout) AND mtv$halt_on_cpu_timeout THEN
      mtp$step_unstep_system (syc$ic_software_breakpoint, 'System stepped to catch timed-out CPU');
    IFEND;

    cst_p^.reason_for_current_state := cpu_state_reason;
    IF deconfiguration_result <> c$cdr_deconfiguration_ok THEN
      message := 'VEOS500x- xxxxxxxxxxxxxxxxxxxxxx; CPU could not be deconfigured safely ';
      message (11, 22) := v$translate_reason_to_ascii [mtv$cst0 [cst_p^.cst_index].reason_for_current_state].
            reason;
      IF deconfiguration_result = c$cdr_system_step THEN
        message (8) := '0';
        mtp$record_critical_hdw_status (mtc$scb_hardware_failure_step, mtc$scb_hsa_set, message);
        mtp$step_unstep_system (syc$ic_fatal_hardware_error, message);
      ELSE {deconfiguration_result = c$cdr_system_idle}
        message (8) := '1';
        dpp$display_error ('Fatal CPU condition encountered; system IDLING.');
        mtp$record_critical_hdw_status (mtc$scb_hardware_failure_idle, mtc$scb_hsa_set, message);
      IFEND;
      RETURN; {----->
    IFEND;

{ Flag the fact that for awhile we can't rely on the page and segment
{ maps until the processor situation has been resolved.

    mtv$reset_all_cache_now := TRUE;

    mtp$cst_p (running_cst_p);
    IF cst_p = running_cst_p THEN

      { Traps are disabled to prevent the processor from wandering off and doing anything else.  The system
      { expects the CPU to be in a very tiny loop as it is being deconfigured.

      i#mtr_disable_traps (old_trap_enable);
      reset_purge_times (cst_p);
      cst_p^.pre_processed_for_reconfig := osc$ppfr_processing_complete;
      mtp$spin_cpu;
      dpp$display_error ('MTP$SPIN_CPU returned: PROCESS_DECONFIGURE_CPU');
      WHILE TRUE DO
        i#mtr_disable_traps (old_trap_enable);
        i#program_error;
      WHILEND;

      { The CPU which is going down will never return.  A restarting CPU will begin execution at the procedure
      { MTP$BEGIN in the module MTM$MONITOR_INTERRUPT_HANDLER.

    ELSE

      { Search for the first processor which is still logically ON and use its value of the memory port
      { mask as the value of the port to which external interrupts should be sent from now on.

    /set_port/
      FOR cpu_index := 0 TO (osv$cpus_physically_configured - 1) DO
        IF (cpu_index <> cst_p^.cst_index) AND (mtv$cst0 [cpu_index].processor_state = cmc$on) THEN
          osv$external_interrupt_selector := mtv$cst0 [cpu_index].memory_port_mask;
          dpp$change_sci_interrupt_port;
          EXIT /set_port/; {----->
        IFEND;
      FOREND /set_port/;
      cst_p^.pre_processed_for_reconfig := osc$ppfr_not_processed;
      reconfigure_cpu (cst_p^.cst_index, cmc$on, cmc$down);
    IFEND;

  PROCEND process_deconfigure_cpu;
?? OLDTITLE ??
?? NEWTITLE := 'reconfigure_cpu', EJECT ??

{ PURPOSE:
{   This procedure drives a CPU to its final state (new_state) from its initial state (old state).  This code
{   is executed by a processor which is NOT changing state; the processor which is being changed is already in
{   a state that allows this processing to complete.  This is considered the 'back-end' of CPU state change
{   processing.

  PROCEDURE [XDCL] reconfigure_cpu
    (    processor_id: ost$processor_id;
         old_state: cmt$element_state;
         new_state: cmt$element_state);

    VAR
      buffer_index: ost$processor_id,
      cpu_reconfiguration_message: string (67),
      cst_p: ^ost$cpu_state_table,
      ignore_status: syt$monitor_status,
      ijle_p: ^jmt$initiated_job_list_entry,
      operator_message: string (37),
      signal: dst$signal_contents,
      status: syt$monitor_status;

    { Disable map purge for right now.
    mtv$recovery_lock3 := TRUE;

    IF new_state = cmc$on THEN

      { Update the SCB.

      mtv$scb.cpus.logically_on := mtv$scb.cpus.logically_on + $ost$processor_id_set [processor_id];
      mtv$cst0 [processor_id].dispatching_priority_integer := 0;

      { Disable all but task switch in Mtr
      mtv$recovery_lock1 := TRUE;

      { Keypoints must be turned off if they are currently active on the processor which is handling the CPU
      { state change to ON.

      osp$alert_keyp_cpu_state_chng (#READ_REGISTER (osc$pr_maintenance_id));

    ELSE {new_state = cmc$down}

      { Update the SCB.

      mtv$scb.cpus.hdw_state_change := mtv$scb.cpus.hdw_state_change - $ost$processor_id_set [processor_id];
      mtv$scb.cpus.logically_on := mtv$scb.cpus.logically_on - $ost$processor_id_set [processor_id];
      mtv$cst0 [processor_id].dispatching_priority_integer := UPPERVALUE (integer);

      { Keypoints must be turned off if they are currently active on the processor which is changing state to
      { DOWN or OFF.

      osp$alert_keyp_cpu_state_chng (processor_id);
    IFEND;

    { Update the CPU state table entry for the changed CPU.

    mtv$cst0 [processor_id].processor_state := new_state;

    { Update the boolean which indicates whether any CPU has vector divide capability.

    mtv$scb.vector_simulation_control.all_vector_divides_degraded :=
          (mtv$scb.cpus.logically_on - mtv$scb.vector_simulation_control.vector_divide_degraded) =
          $ost$processor_id_set [];

    { Increment/decrement the number of CPUs which are logically 'on' and which are physically configured.

    IF (new_state = cmc$on) AND (old_state = cmc$down) THEN
      osv$cpus_logically_on := osv$cpus_logically_on + 1;

      tmv$multiple_cpus_active := TRUE;
      reinitialize_cpu_mtr_xp (processor_id);
      mtv$cst0 [processor_id].reason_for_current_state := osc$cdsr_null;

      null_sva := 0;
{     #PURGE_BUFFER (osc$purge_all_cache,null_sva);
{     #PURGE_BUFFER (osc$purge_all_page_seg_map,null_sva);


    ELSEIF (new_state = cmc$down) AND (old_state = cmc$on) THEN

      { Remove the 'downed' CPU selection for all tasks in the system.

      tmp$update_job_task_cpu_selects;

      { The following code attempts to resurrect a task which was executing on a failing CPU.  Check to see if
      { the CPU was being deconfigured because a DUE occurred AND the process was damaged, or the task was
      { processing a system call.  If it was, send a monitor fault and attempt to switch the task to a
      { non-failing processor.  If it wasn't, switch the task to a non-failing processor.

      cst_p := ^mtv$cst0 [processor_id];
      IF cst_p^.xcb_p <> NIL THEN
        IF ((osc$detected_uncorrected_err IN cst_p^.xcb_p^.xp.monitor_condition_register) AND
              NOT (osc$process_not_damaged IN cst_p^.xcb_p^.xp.flags)) OR
              (osc$system_call IN cst_p^.xcb_p^.xp.monitor_condition_register) THEN
          mtp$abort_task_with_due (cst_p, cst_p^.xcb_p);
        IFEND;
        tmp$switch_task_from_failing_cp (cst_p);
      ELSEIF cst_p^.next_ptlo_to_dispatch <> 0 THEN
        tmp$switch_task_from_failing_cp (cst_p);
      IFEND;

      osv$cpus_logically_on := osv$cpus_logically_on - 1;

      IF osv$cpus_logically_on = 1 THEN
        tmv$multiple_cpus_active := FALSE;
      IFEND;

      { Reset the values for the 'cache purged' and/or 'page map purged' times for the deconfigured CPU.
      { This prevents other CPUs from hanging up waiting for the deconfigured CPU to purge its cache and/or
      { page maps.

      reset_purge_times (^mtv$cst0 [processor_id]);

      CASE mtv$cst0 [processor_id].reason_for_current_state OF
      = osc$cdsr_downed_by_dft, osc$cdsr_due_threshold_exceeded, osc$cdsr_cpu_timeout =
        signal.identifier := dsc$deadstart_signal;
        signal.contents.kind := dsc$signal_post_operator_action;
        mtp$get_date_time_at_timestamp (#FREE_RUNNING_CLOCK (0), signal.contents.poa_data.date_time);
        signal.contents.poa_data.kind := dsc$signal_poa_cpu_down_by_sys;
        tmp$send_signal (tmv$system_job_monitor_gtid, signal.signal, ignore_status);
      ELSE
      CASEND;

      { Reset the values representing how cache and/or page maps should be purged.

      IF osv$cpus_logically_on = 1 THEN
        osv$multiprocessor_running := FALSE;
        mmv$multiple_caches := FALSE;
        mmv$multiple_page_maps := FALSE;

*if $variable(mmv$test_forced_use_cache_maps, declared) <> 'UNKNOWN'

        { The following code is benchmark code to force use of cache and/or maps during benchmark runs.

        mmv$multiple_caches := mmv$multiple_caches OR mmv$force_use_of_cache_and_maps;
        mmv$multiple_page_maps := mmv$multiple_page_maps OR mmv$force_use_of_cache_and_maps;

*else
{ -------- Code for forcing the use of cache and maps omitted at compile time --------
*ifend
      IFEND;

    IFEND;

    { Notify the operator of the CPU state change.

    operator_message := 'CP n STATE CHANGED FROM OOOO TO NNNN ';
    operator_message (4) := $CHAR (processor_id + $INTEGER ('0'));
    operator_message (25, 4) := v$translate_state_to_ascii [old_state] (2, 4);
    operator_message (33, 4) := v$translate_state_to_ascii [new_state] (2, 4);
    dpp$display_error (operator_message);
    operator_message := v$translate_reason_to_ascii [mtv$cst0 [processor_id].reason_for_current_state].
          initiator;
    IF operator_message <> ' ' THEN
      dpp$display_error (operator_message);
    IFEND;

    { Should be safe now to allow map purge on new CPU.
    mtv$recovery_lock3 := FALSE;


    { Log a CM1800 engineering statistic detailing that the CPU was reconfigured.

    cpu_reconfiguration_message := 'CPU n was placed in aNNNNN condition';
    cpu_reconfiguration_message (5) := $CHAR (processor_id + $INTEGER ('0'));
    cpu_reconfiguration_message (22, 5) := v$translate_state_to_ascii [new_state];
    IF v$translate_reason_to_ascii [mtv$cst0 [processor_id].reason_for_current_state].reason = ' ' THEN
      cpu_reconfiguration_message (37) := '.';
    ELSE
      cpu_reconfiguration_message (37, 8) := ' due to ';
      cpu_reconfiguration_message (45, 22) := v$translate_reason_to_ascii
            [mtv$cst0 [processor_id].reason_for_current_state].reason;
    IFEND;
    mtp$store_informative_message (cpu_reconfiguration_message);

    { Tell job-mode about the state change.

    mtv$cst0 [processor_id].log_cpu_state_change := TRUE;

{ Enable normal cache management.

    mtv$reset_all_cache_now := FALSE;

    { Set a system flag to log the data and change the MRT from job mode.
    tmp$set_monitor_flag (tmv$system_job_monitor_gtid, syc$mf_cpu_configuration_change, status);
    IF NOT status.normal THEN
      mtp$error_stop ('MT - unable to set MTR flag for job_mode CPU reconfiguration');
    IFEND;

  PROCEND reconfigure_cpu;
?? OLDTITLE ??
?? NEWTITLE := 'reinitialize_cpu_mtr_xp', EJECT ??

{ PURPOSE:
{   This procedure reinitializes the monitor exchange package for a CPU which is about to be reconfigured to
{   an ON state in order that the CPU can execute "cleanly" in monitor when it restarts.  The processor id
{   input parameter specifies the logical processor number of the CPU which is to be restarted.  The deck,
{   sya$constants defines where the first CPU used has the beginning of its stack.  The offset takes into
{   account the fact that the monitor segment table is located right after the monitor exchange package
{   of the first CPU used; all other CPUs use this same monitor segment table.  When reinstating the first
{   CPU used, its exchange package must be reinitialized to have A0 (csf) point to AFTER the monitor segment
{   table.  Otherwise, the moment the first CPU used begins to re-execute, it will bash the segment table
{   entry for mainframe-wired, all the CPUs will page fault for data in mainframe-wired (normally impossible),
{   and the system will halt.


  PROCEDURE reinitialize_cpu_mtr_xp
    (    restarted_cpu_id: ost$processor_id);

    VAR
      rma_slot_1: integer,
      rma_slot_2: integer,
      running_cpu_id: ost$processor_id,
      stack_p: ^ost$monitor_stack;

    { Retrieve all possible locations for the monitor exchange package that this processor could use.

    i#real_memory_address (mtv$monitor_xp_slot_pointers.slot_1_p, rma_slot_1);
    i#real_memory_address (mtv$monitor_xp_slot_pointers.slot_2_p, rma_slot_2);

    { Determine the running CPU id.

    IF restarted_cpu_id = 0 THEN
      running_cpu_id := 1;
    ELSE
      running_cpu_id := 0;
    IFEND;

    { Determine which monitor exchange package location the running CPU is using and use the other
    { monitor exchange package location for the processor being restarted.

    IF mtv$cst0 [running_cpu_id].monitor_mps = rma_slot_1 THEN
      mtv$cst0 [restarted_cpu_id].monitor_mps := rma_slot_2;
      stack_p := mtv$monitor_xp_slot_pointers.slot_2_p;
      stack_p^.xp := osv$initial_monitor_xp;
      stack_p^.xp.a1_current_stack_frame := ^stack_p^.stack;
      stack_p^.xp.a0_dynamic_space_pointer := ^stack_p^.csf;
    ELSE
      mtv$cst0 [restarted_cpu_id].monitor_mps := rma_slot_1;
      stack_p := mtv$monitor_xp_slot_pointers.slot_1_p;
      stack_p^.xp := osv$initial_monitor_xp;
      stack_p^.xp.a1_current_stack_frame := mtv$first_cpu_monitor_stack_p;
      stack_p^.xp.a0_dynamic_space_pointer := #ADDRESS (#RING (mtv$first_cpu_monitor_stack_p),
            #SEGMENT (mtv$first_cpu_monitor_stack_p), #OFFSET (mtv$first_cpu_monitor_stack_p) +
            c$monitor_stack_frame_length);
    IFEND;

    stack_p^.xp.tos_registers [1].pva.ring := #RING (^stack_p^.stack);
    stack_p^.xp.tos_registers [1].pva.seg := #SEGMENT (^stack_p^.stack);
    stack_p^.xp.tos_registers [1].pva.offset := #OFFSET (^stack_p^.stack);
    #SPOIL (stack_p^);

  PROCEND reinitialize_cpu_mtr_xp;

?? OLDTITLE ??
?? NEWTITLE := 'reset_purge_times', EJECT ??

{ PURPOSE:
{   This procedure is necessary to prevent other cpus from hanging up waiting for the deconfigured CPU to
{   purge its cache and/or page maps.  With the use of the value 'very distant future', the CPU is essentially
{   telling all of the other CPUs that it has already purged its cache and/or page maps when it is asked to do
{   so.  If there is not already a 'witty' flag indicating a dead CPU set during multiple CPU cache and/or map
{   purges,  store a very large number.

  PROCEDURE reset_purge_times
    (    cst_p: ^ost$cpu_state_table);

    CONST
      c$very_distant_future = 7fffffffffffffff(16);

    IF cst_p^.time_last_cache_purge < #FREE_RUNNING_CLOCK (0) THEN
      cst_p^.time_last_cache_purge := c$very_distant_future;
    IFEND;
    IF cst_p^.time_last_map_request < #FREE_RUNNING_CLOCK (0) THEN
      cst_p^.time_last_map_request := c$very_distant_future;
    IFEND;

  PROCEND reset_purge_times;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$deconfigure_divide_unit', EJECT ??

{ PURPOSE:
{   This procedure is called to update the status of the vector divide units when DFT deconfigures a divide
{   net due to a hardware failure. Processors (theta) cannot execute vector divide instructions on processors
{   with degraded divide units.

  PROCEDURE [XDCL] mtp$deconfigure_divide_unit
    (    processor_id: ost$processor_id);

    mtv$scb.vector_simulation_control.vector_divide_degraded :=
          mtv$scb.vector_simulation_control.vector_divide_degraded + $ost$processor_id_set [processor_id];
    mtv$scb.vector_simulation_control.all_vector_divides_degraded :=
          (mtv$scb.cpus.logically_on - mtv$scb.vector_simulation_control.vector_divide_degraded) =
          $ost$processor_id_set [];

  PROCEND mtp$deconfigure_divide_unit;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$manage_processor_with_due', EJECT ??

{ PURPOSE:
{   This procedure determines whether or not it is safe to continue with a deconfiguration for a CPU which has
{   encountered a fatal detected uncorrected error (DUE).  If there is more than one CPU which is logically on
{   in the system, the CPU which is executing this code (which has encountered the DUE) will 'wait' by calling
{   MTP$SPIN_CPU and allow the system to attempt to deconfigure it.

  PROCEDURE [XDCL] mtp$manage_processor_with_due
    (    processor_id: ost$processor_id);

    VAR
      cpu_index: ost$processor_id,
      cst_p: ^ost$cpu_state_table,
      old_trap_enable: 0 .. 3;

    IF osv$cpus_logically_on > 1 THEN
      i#mtr_disable_traps (old_trap_enable);
      cst_p := ^mtv$cst0 [processor_id];
      cst_p^.next_processor_state := cmc$down;
      cst_p^.reason_for_current_state := osc$cdsr_due_threshold_exceeded;
      reset_purge_times (cst_p);

{ Clear the async lock just in case this processor has it. Nothing is hurt if
{ the other processor has it.
      dpp$display_error ('SPINNING CPU');

      mtv$async_lock := FALSE;
      mtp$spin_cpu;
      dpp$display_error ('MTP$SPIN_CPU returned: MTP$MANAGE_PROCESSOR_WITH_DUE');
      WHILE TRUE DO
        i#mtr_disable_traps (old_trap_enable);
        i#program_error;
      WHILEND;
    ELSE
      mtp$record_critical_hdw_status (mtc$scb_hardware_failure_step, mtc$scb_hsa_set,
            'VEOS520E- FATAL CPU ERROR: Detected Uncorrected Error (DUE)            ');
      mtp$step_unstep_system (syc$ic_fatal_hardware_error,
            'VEOS520E- FATAL CPU ERROR: Detected Uncorrected Error (DUE)');
    IFEND

  PROCEND mtp$manage_processor_with_due;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$monitor_processor_status', EJECT ??

{ PURPOSE:
{   This procedure monitors CPU states and drives any CPU with a difference between its current state and its
{   next state to the state specified in the cpu_state_table field next_processor_state.  This procedure
{   processes CPUs which change state because of an error or halt detected by Dedicated Fault Tolerance (DFT),
{   a detected uncorrected error (DUE) or timeout detected by the system, or a command invoked through the
{   Logical Configuration Utility.

  PROCEDURE [XDCL] mtp$monitor_processor_status;

    VAR
      cpu_index: ost$processor_id,
      cst_p: ^ost$cpu_state_table,
      running_cst_p: ^ost$cpu_state_table;

    IF NOT mtv$all_cpus_have_been_started THEN
      RETURN; {----->
    IFEND;

    mtp$cst_p (running_cst_p);

    FOR cpu_index := 0 TO (osv$cpus_physically_configured - 1) DO
      cst_p := ^mtv$cst0 [cpu_index];

      { Check for a CPU which is downed by Dedicated Fault Tolerance (DFT).  Only error logging code sets the
      { flag in the SCB indicating a processor with a changed state.

      IF (cst_p^.processor_state = cmc$on) AND (cst_p^.next_processor_state = cmc$down) THEN
        IF (cst_p^.pre_processed_for_reconfig = osc$ppfr_not_processed) OR
              (cpu_index IN mtv$scb.cpus.hdw_state_change) THEN
          cst_p^.previous_processor_state := cst_p^.processor_state;
          process_deconfigure_cpu (cst_p, cst_p^.reason_for_current_state);

        ELSEIF cst_p^.pre_processed_for_reconfig = osc$ppfr_processing_complete THEN

          { The CPU has processed the "front-end" of its own deconfiguration (as a result of an LCU command),
          { and is now in a state where the "back-end" processing can take over.

          process_deconfigure_cpu (cst_p, cst_p^.reason_for_current_state);

        ELSE {cst_p^.pre_processed_for_reconfig = osc$ppfr_processing_in_progress}

          { The processor is voluntarily deconfiguring itself, and the system has caught it in the middle of
          { this processing.  Wait until its preprocessing is complete, and then catch it in the statements
          { above.

        IFEND;

      ELSEIF (cst_p <> running_cst_p) AND (cst_p^.processor_state = cmc$on) AND
            (#FREE_RUNNING_CLOCK (0) > (cst_p^.cpu_alive_flag + c$cpu_timeout_interval)) THEN

        { Check for a timed-out CPU.  This is a CPU which has not updated the CPU_ALIVE flag in its CST in
        { the recent past.

        IF NOT dsv$boot_control_table_p^.flags.cpu_error_process_in_progress OR
              (dsv$boot_control_table_p^.flags.cpu_error_process_in_progress AND
              dsv$boot_control_table_p^.flags.cpu_error_fatal_after_process) THEN
          cst_p^.previous_processor_state := cst_p^.processor_state;
          cst_p^.next_processor_state := cmc$down;
          process_deconfigure_cpu (cst_p, osc$cdsr_cpu_timeout);
        IFEND;

      ELSEIF (cst_p^.processor_state = cmc$down) AND (cst_p^.next_processor_state = cmc$on) THEN
        reconfigure_cpu (cpu_index, cmc$down, cmc$on);
        { Clear the flag which prevents task scheduling on the CPU being turned on.
        mtv$recovery_lock1 := FALSE;

      IFEND;
    FOREND;


  PROCEND mtp$monitor_processor_status;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$process_cpu_state_change', EJECT ??

{ PURPOSE:
{   This procedure is called during the monitor idle loop in mtm$monitor_interrupt_handler.  This is the only
{   way a processor will voluntarily deconfigure itself if there are no hardware problems.  This procedure
{   processes a down request by the Logical_Configuration_Utility (LCU) which is posted in the processor's
{   cpu_state_table (CST).  Only a CPU which is currently "on" will ever execute this code.

  PROCEDURE [XDCL] mtp$process_cpu_state_change
    (    dummy_p: ^cell;
         cst_p: ^ost$cpu_state_table);

    process_deconfigure_cpu (cst_p, cst_p^.reason_for_current_state);

  PROCEND mtp$process_cpu_state_change;
?? OLDTITLE ??
?? NEWTITLE := 'mtp$reissue_request', EJECT ??


  PROCEDURE [XDCL] mtp$reissue_monitor_request
    (    failing_cst: ^ost$cpu_state_table);


*copyc mth$reissue_monitor_request

    VAR
      cst_p: ^ost$cpu_state_table;

    cst_p := failing_cst;
    cst_p^.xcb_p^.xp.p_register.pva.offset := cst_p^.xcb_p^.xp.p_register.pva.offset - 2;

{ Save the xcb address of the task the system was working on at time of failure.

    mtv$failing_task_xcb := cst_p^.xcb_p;

{ Save the task identifier, job identifier and ijl pointer of failing task/job.

    mtv$failing_task_identifier := cst_p^.taskid;
    mtv$failing_ajlo := cst_p^.ajlo;
    mtv$failing_ijle_p := cst_p^.ijle_p;



  PROCEND mtp$reissue_monitor_request;
?? OLDTITLE ??
*if false
?? NEWTITLE := 'recover_ptl', EJECT ??

  PROCEDURE [XDCL] mtp$recover_ptl
    (    cst_p: ^ost$cpu_state_table;
     VAR status: ost$status);


    VAR

      task_id: ost$global_task_id;



    status.normal := TRUE;

    CASE tmv$ptl_lock.class OF
    = mtc$ignore =

      status.normal := TRUE;
{ This value indicates that the ptl activity currently in progress can
{ be discarded with no loss of system, job or task integrity. The result
{ of aborting the process could be a task priority that doesn't get updated
{ or a task statistic that won't get counted, but we can keep the system
{ going which is far more important.

      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;

      dpp$display_error ('Processing mtc$ignore');
{ The following ptl lock classes require more work to clean up. The most
{ common corrective action required will be to resubmit the request that
{ was in progress when the failure occurred. There may be some cleanup
{ work associated as well.

    = mtc$reissue =

{  The cst pointer passed to this procedure is the cst of the failing CPU
      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;
      task_id := cst_p^.taskid;
      mtp$reissue_monitor_request (cst_p);
      dpp$display_error ('Processing reissue');

    = mtc$abandon =
{  This condition requires that we back out of the operation that was in
{  progress when the CPU failed. Ideally, we could save the failing CPU
{  environment and resume it using the backup processor. This may not always
{  be possible.

      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;
      dpp$display_error ('Processing abandon');

    = mtc$fixup_ajl =

{   Don't do anything now - first determine what is required
      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;
      dpp$display_error ('Processing fixup_ajl');

    = mtc$bad_task =
{   Need to throw away the task - after some thought
      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;
      dpp$display_error ('Processing bad_task');

    = mtc$bad_job =
{   Here we need to throw away the executing job - after some thought.
      tmv$ptl_lock.count := 0;
      tmv$ptl_lock.clear := 0;
      dpp$display_error ('Processing bad_job');


    ELSE
      mtp$error_stop ('dont know what else to do ');
    CASEND;

  PROCEND mtp$recover_ptl;
?? OLDTITLE ??
*ifend
?? NEWTITLE := 'mtp$inject_hardware_fault', EJECT ??

{ PURPOSE:
{   This procedure forces a dummy DUE error and attempts to deconfigure the alternate processor.
{   the CPU which is executing this code (which has encountered the DUE) will 'wait' by calling
{   MTP$SPIN_CPU and allow the system to attempt to deconfigure it.

  PROCEDURE [XDCL, #GATE] mtp$inject_hardware_fault
    (    processor_id: ost$processor_id);

    VAR
      cpu_index: ost$processor_id,
      cst_p: ^ost$cpu_state_table,
      old_trap_enable: 0 .. 3,
      running_cst: ^ost$cpu_state_table;

{ Check for the correct processor to deconfigure.

    mtp$cst_p (running_cst);

    IF running_cst^.cst_index <> processor_id THEN
      mtv$fault_injection_flag := TRUE;
      RETURN; {----->
    IFEND;


    IF osv$cpus_logically_on > 1 THEN
      i#mtr_disable_traps (old_trap_enable);
      cst_p := ^mtv$cst0 [processor_id];
      cst_p^.next_processor_state := cmc$down;
      cst_p^.reason_for_current_state := osc$cdsr_downed_by_fault_inject;
      reset_purge_times (cst_p);

{ Clear the async lock just in case this processor has it. Nothing is hurt if
{ the other processor has it.
      dpp$display_error ('INJECT HARDWARE FAULT IS SPINNING CPU');

      mtv$async_lock := FALSE;
      mtp$spin_cpu;
      dpp$display_error ('MTP$SPIN_CPU returned: MTP$INJECT_HARDWARE_FAULT');
      WHILE TRUE DO
        i#mtr_disable_traps (old_trap_enable);
        i#program_error;
      WHILEND;
    ELSE
      mtp$record_critical_hdw_status (mtc$scb_hardware_failure_step, mtc$scb_hsa_set,
            'VEOS520E- FATAL CPU ERROR: hardware fault injection                    ');
      mtp$step_unstep_system (syc$ic_fatal_hardware_error,
            'VEOS520E- FATAL CPU ERROR: hardware fault injection');
    IFEND

  PROCEND mtp$inject_hardware_fault;
MODEND mtm$processor_configuration_mgr;

