?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Fault Tolerance : Inject Hardware Fault' ??
MODULE sym$inject_hardware_fault;

{ PURPOSE:
{   This module contains the procedures to inject various hardware faults on the 960.
{
{ DESIGN:
{   Special microcode is required to run with this utility that actually causes the
{   desired hardware fault.
{
{ NOTE:
{   There is a fault injection utility for injecting errors on the 170 side, new
{   additions made to this module should also be made there if it is applicable.
{   The 170 fault injection utility is in deck DSM$INJHFU_170.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jme$queued_file_conditions
*copyc oss$job_paged_literal
*copyc pmt$program_parameters
*copyc syt$hardware_fault_kind
*copyc syt$rb_inject_hardware_fault
?? POP ??
*copyc clp$evaluate_parameters
*copyc clp$get_parameter_list_text
*copyc i#call_monitor
*copyc jmp$system_job
*copyc osp$fetch_system_constant
*copyc osp$set_status_abnormal
*copyc pmp$execute
*copyc syp$cause_hardware_faults
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    t$hardware_fault_params = RECORD
      kind: syt$hardware_fault_kind,
      mode: (c$job, c$monitor),
      traps_enabled: boolean,
      rma: integer,
    RECEND;

?? OLDTITLE ??
?? NEWTITLE := 'syp$inject_hardware_fault', EJECT ??

{ PURPOSE:
{   This procedure evaluates the inject_hardware_fault command and causes the hardware fault.

  PROCEDURE [XDCL, #GATE] syp$inject_hardware_fault
    (    program_parameters: pmt$program_parameters;
     VAR status: ost$status);

    VAR
      enable_fault_injection_index: integer,
      enable_fault_injection_name: ost$name,
      enable_fault_injection_value: integer,
      hardware_fault_p: ^t$hardware_fault_params,
      inject_hardware_fault_mon_req: syt$rb_inject_hardware_fault,
      known_hardware_fault_kind: boolean,
      program_parameters_seq_p: ^pmt$program_parameters;

    status.normal := TRUE;

    {  Ensure that task is executing within a system job.

    IF NOT jmp$system_job () THEN
      osp$set_status_abnormal ('JM', jme$must_be_system_job, 'syp$inject_hardware_fault', status);
      RETURN;
    IFEND;

    enable_fault_injection_name := 'ENABLE_FAULT_INJECTION';
    osp$fetch_system_constant (enable_fault_injection_name, enable_fault_injection_index,
          enable_fault_injection_value, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF enable_fault_injection_value = 0 THEN
      osp$set_status_abnormal ('SY', 0, 'Enable_fault_injection has not been selected.', status);
      RETURN;
    IFEND;

    program_parameters_seq_p := ^program_parameters;
    RESET program_parameters_seq_p;
    NEXT hardware_fault_p IN program_parameters_seq_p;

    IF hardware_fault_p^.mode = c$job THEN
      IF (hardware_fault_p^.kind = syc$hfk_halt) OR (hardware_fault_p^.kind = syc$hfk_software_error) THEN

        {  Issue the monitor request to clear synchronous bits in monitor mask of this task's exchange
        {  package so that the processor will halt in job mode when the error occurs.

        inject_hardware_fault_mon_req.request_code := syc$inject_hardware_fault;
        inject_hardware_fault_mon_req.hardware_fault_request := syc$hfk_uf_clear_sync_in_mm;
        inject_hardware_fault_mon_req.traps_enabled := TRUE;
        inject_hardware_fault_mon_req.rma := hardware_fault_p^.rma;
        i#call_monitor (#LOC (inject_hardware_fault_mon_req), #SIZE (inject_hardware_fault_mon_req));

        IF NOT inject_hardware_fault_mon_req.status.normal THEN
          osp$set_status_abnormal ('SY', 0,
                'SYP$MTR_INJECT_HARDWARE_FAULT does not recognize request to clear DUE in monitor mask.',
                status);
          RETURN;
        IFEND;
      IFEND;

      syp$cause_hardware_faults (hardware_fault_p^.kind, hardware_fault_p^.rma, known_hardware_fault_kind);
      IF NOT known_hardware_fault_kind THEN
        osp$set_status_abnormal ('SY', 0, 'SYP$CAUSE_HARDWARE_FAULT does not recognize fault kind.', status);
        RETURN;
      IFEND;
    ELSEIF hardware_fault_p^.mode = c$monitor THEN

      {  Issue monitor request to cause hardware fault in monitor mode.

      inject_hardware_fault_mon_req.request_code := syc$inject_hardware_fault;
      inject_hardware_fault_mon_req.hardware_fault_request := hardware_fault_p^.kind;
      inject_hardware_fault_mon_req.traps_enabled := hardware_fault_p^.traps_enabled;
      inject_hardware_fault_mon_req.rma := hardware_fault_p^.rma;
      i#call_monitor (#LOC (inject_hardware_fault_mon_req), #SIZE (inject_hardware_fault_mon_req));

      IF NOT inject_hardware_fault_mon_req.status.normal THEN
        osp$set_status_abnormal ('SY', 0, 'SYP$CAUSE_HARDWARE_FAULT does not recognize fault kind.', status);
        RETURN;
      IFEND;
    ELSE
      osp$set_status_abnormal ('SY', 0, 'Unrecoginized mode, not JOB or MONITOR.', status);
    IFEND;

  PROCEND syp$inject_hardware_fault;
?? OLDTITLE ??
?? NEWTITLE := 'syp$start_injhf_task', EJECT ??

{ PURPOSE:
{   This procedure starts the inject_hardware_fault task.  Inject_hardware_fault is run as a separate task
{   so an error will not be injected into the system monitor.

  PROCEDURE [XDCL] syp$start_injhf_task
    (    parameter_list: clt$parameter_list;
    VAR status: ost$status);

  {  Define parameter_descriptor table (PDT) for the inject fault command.

{ PROCEDURE () inject_hardware_fault, injhf (
{   kind, k: key
{       retry, exchange, trap, halt, pdm_halt, software_error
{     keyend = $required
{   mode, m: key
{       job, monitor
{     keyend = $required
{   traps_enabled, te: boolean = $required
{   rma_of_parity_error, rope: integer 0..0ffffffff(16) = $required
{   status)

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 9] of clt$pdt_parameter_name,
      parameters: array [1 .. 5] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        qualifier: clt$keyword_type_qualifier,
        keyword_specs: array [1 .. 6] of clt$keyword_specification,
      recend,
      type2: record
        header: clt$type_specification_header,
        qualifier: clt$keyword_type_qualifier,
        keyword_specs: array [1 .. 2] of clt$keyword_specification,
      recend,
      type3: record
        header: clt$type_specification_header,
      recend,
      type4: record
        header: clt$type_specification_header,
        qualifier: clt$integer_type_qualifier,
      recend,
      type5: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [89, 4, 25, 15, 25, 1, 703],
    clc$command, 9, 5, 4, 0, 0, 0, 5, ''], [
    ['K                              ',clc$abbreviation_entry, 1],
    ['KIND                           ',clc$nominal_entry, 1],
    ['M                              ',clc$abbreviation_entry, 2],
    ['MODE                           ',clc$nominal_entry, 2],
    ['RMA_OF_PARITY_ERROR            ',clc$nominal_entry, 4],
    ['ROPE                           ',clc$abbreviation_entry, 4],
    ['STATUS                         ',clc$nominal_entry, 5],
    ['TE                             ',clc$abbreviation_entry, 3],
    ['TRAPS_ENABLED                  ',clc$nominal_entry, 3]],
    [
{ PARAMETER 1
    [2, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 229,
  clc$required_parameter, 0, 0],
{ PARAMETER 2
    [4, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 81, clc$required_parameter,
  0, 0],
{ PARAMETER 3
    [9, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3, clc$required_parameter, 0
  , 0],
{ PARAMETER 4
    [5, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 20, clc$required_parameter,
  0, 0],
{ PARAMETER 5
    [7, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$keyword_type], [6], [
    ['EXCHANGE                       ', clc$nominal_entry, clc$normal_usage_entry, 2],
    ['HALT                           ', clc$nominal_entry, clc$normal_usage_entry, 4],
    ['PDM_HALT                       ', clc$nominal_entry, clc$normal_usage_entry, 5],
    ['RETRY                          ', clc$nominal_entry, clc$normal_usage_entry, 1],
    ['SOFTWARE_ERROR                 ', clc$nominal_entry, clc$normal_usage_entry, 6],
    ['TRAP                           ', clc$nominal_entry, clc$normal_usage_entry, 3]]
    ],
{ PARAMETER 2
    [[1, 0, clc$keyword_type], [2], [
    ['JOB                            ', clc$nominal_entry, clc$normal_usage_entry, 1],
    ['MONITOR                        ', clc$nominal_entry, clc$normal_usage_entry, 2]]
    ],
{ PARAMETER 3
    [[1, 0, clc$boolean_type]],
{ PARAMETER 4
    [[1, 0, clc$integer_type], [0, 0ffffffff(16), 10]],
{ PARAMETER 5
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$kind = 1,
      p$mode = 2,
      p$traps_enabled = 3,
      p$rma_of_parity_error = 4,
      p$status = 5;

    VAR
      pvt: array [1 .. 5] of clt$parameter_value;

    TYPE
      t$hardware_fault_description = RECORD
        hardware_fault_kind_name: ost$name,
        hardware_fault_kind: syt$hardware_fault_kind,
      RECEND;

    {  Define table that contains the names of the various kinds of hardware faults that can be injected.
    {  To add a new entry add the hardware fault kind name and the corresponding hardware fault kind index
    {  to the table.  The module, sym$cause_hardware_faults, must also be updated to cause the new fault
    {  condition.  The common deck, syc$hardware_fault_codes, that defines the hardware fault kinds must
    {  also be updated.  The PDT would also have to be updated.

    VAR
      v$hardware_fault_kind_table: [READ, oss$job_paged_literal] ARRAY
            [syc$hfk_retry .. (syc$hfk_uf_null_function - 1)] OF t$hardware_fault_description := [
            ['RETRY                          ', syc$hfk_retry],
            ['EXCHANGE                       ', syc$hfk_exchange],
            ['TRAP                           ', syc$hfk_trap],
            ['HALT                           ', syc$hfk_halt],
            ['PDM_HALT                       ', syc$hfk_pdm_halt],
            ['SOFTWARE_ERROR                 ', syc$hfk_software_error]];

    VAR
      hardware_fault: t$hardware_fault_params,
      hardware_fault_kind: syt$hardware_fault_kind,
      hfkti {hardware fault kind table index} : syt$hardware_fault_kind,
      injhf_params_p: ^pmt$program_parameters,
      known_hardware_fault_kind: boolean,
      program_attributes_p: ^pmt$program_attributes,
      program_description_seq_p: ^pmt$program_description,
      task_status: pmt$task_status,
      taskid: pmt$task_id;

    {  Ensure the that task is executing within a system job.

    status.normal := TRUE;
    IF NOT jmp$system_job () THEN
      osp$set_status_abnormal ('JM', jme$must_be_system_job, 'syp$start_injhf_task', status);
      RETURN;
    IFEND;

    {  Evaluate the inject_hardware_fault command.  Determine kind and mode of fault.  Mode
    {  of fault is either job or monitor.

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    {  Search hardware fault kind description table for valid faults and the corresponding index.

    known_hardware_fault_kind := FALSE;

  /set_hardware_fault_kind/
    FOR hfkti := LOWERBOUND (v$hardware_fault_kind_table) TO UPPERBOUND (v$hardware_fault_kind_table) DO
      IF v$hardware_fault_kind_table [hfkti].hardware_fault_kind_name = pvt [p$kind].value^.keyword_value THEN
        hardware_fault.kind := v$hardware_fault_kind_table [hfkti].hardware_fault_kind;
        known_hardware_fault_kind := TRUE;
        EXIT /set_hardware_fault_kind/;
      IFEND;
    FOREND /set_hardware_fault_kind/;

    IF NOT known_hardware_fault_kind THEN
      osp$set_status_abnormal ('SY', 0, 'Unrecoginized kind of hardware fault.', status);
      RETURN;
    IFEND;

    IF pvt [p$mode].value^.keyword_value = 'JOB' THEN
      hardware_fault.mode := c$job;
    ELSEIF pvt [p$mode].value^.keyword_value = 'MONITOR' THEN
      hardware_fault.mode := c$monitor;
    ELSE
      osp$set_status_abnormal ('SY', 0, 'SYP$CAUSE_HARDWARE_FAULT does not recognize fault kind.', status);
      RETURN;
    IFEND;

    hardware_fault.traps_enabled := pvt [p$traps_enabled].value^.boolean_value.value;

    hardware_fault.rma := pvt [p$rma_of_parity_error].value^.integer_value.value;

    PUSH program_description_seq_p: [[REP 1 OF pmt$program_attributes]];
    RESET program_description_seq_p;
    NEXT program_attributes_p IN program_description_seq_p;
    program_attributes_p^.contents := $pmt$prog_description_contents
          [pmc$starting_proc_specified, pmc$load_map_options_specified, pmc$term_error_level_specified];
    program_attributes_p^.starting_procedure := 'SYP$INJECT_HARDWARE_FAULT';
    program_attributes_p^.load_map_options := $pmt$load_map_options [pmc$no_load_map];
    program_attributes_p^.termination_error_level := pmc$warning_load_errors;

    task_status.status.normal := TRUE;
    pmp$execute (program_description_seq_p^, #SEQ (hardware_fault)^, osc$wait, taskid, task_status, status);
    IF NOT task_status.status.normal THEN
      status := task_status.status;
    IFEND;

  PROCEND syp$start_injhf_task;
MODEND sym$inject_hardware_fault;
