*copyc osd$default_pragmats
?? NEWTITLE := 'NOS/VE Device Management' ??
?? NEWTITLE := '  Module Header' ??
MODULE dmm$flaw_management;
?? RIGHT := 110 ??

{
{  PURPOSE:
{
{    This module contains the code which is needed in ring one to assist in
{    the management of flaws on devices maintained by device management.
{
?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cmt$element_definition
*copyc cmt$product_identification
*copyc dmt$active_volume_table_index
*copyc dmt$avt_search_key
*copyc dmt$device_allocation_unit
*copyc dmt$error_condition_codes
*copyc dmt$flaw_dau_definition
*copyc dmt$flaw_duplication
*copyc dmt$log_flaw_init_data
*copyc dmt$ms_device_allocation_table
*copyc dmt$physical_device_attributes
*copyc dmt$sc_flaw_command
*copyc gft$system_file_identifier
*copyc iot$cylinder
*copyc iot$logical_unit
*copyc ost$status
*copyc rmt$recorded_vsn
?? POP ??
*copyc cmp$pc_get_logical_unit
*copyc dmp$close_file
*copyc dmp$get_logical_unit_number
*copyc dmp$get_physical_attributes
*copyc dmp$open_dat
*copyc dsp$log_sys_msg_help
*copyc osp$clear_mainframe_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc dmv$active_volume_table
*copyc dmv$p_sc_flaw_commands
*copyc gfv$null_sfid
*copyc osv$mainframe_pageable_heap
*copyc cml$ms_media_flaw_change
?? OLDTITLE ??
?? NEWTITLE := '  dmp$construct_sc_dau_list', EJECT ??

  PROCEDURE [XDCL] dmp$construct_sc_dau_list (
        rvsn: rmt$recorded_vsn;
        scan_only: boolean;
    VAR p_sc_dau_list: ^array [1 .. *] of dmt$log_flaw_init_data;
    VAR applicable_flaw_count: integer;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure will compare each entry in the array of system core flaw commands
{     with the rvsn given in it's paramater.  If there is an entry with the same rvsn,
{     the physical address will be converted to a range of dau addresses and added to
{     p_sc_dau_list array.  If the input parameter scan_only is true, nothing else will
{     be done.  If false, each matching entry in dmv$p_sc_flaw_commands will be marked as
{     processed.  If all entries in the array are set in the dat, a FREE command will be
{     issued for the pointer.

    VAR
      dau_adrs: dmt$dau_address,
      end_dau_adrs: dmt$dau_address,
      index: integer,
      p_phys_adrs: ^dmt$physical_flaw_address,
      release_sc_flaw_commands: boolean,
      sec_specified: boolean,
      trk_specified: boolean;

    applicable_flaw_count := 0;
    status.normal := TRUE;
    IF dmv$p_sc_flaw_commands = NIL THEN
      RETURN;
    IFEND;

    release_sc_flaw_commands := NOT scan_only;

    FOR index := LOWERBOUND (dmv$p_sc_flaw_commands^) TO UPPERBOUND (dmv$p_sc_flaw_commands^) DO
      IF (dmv$p_sc_flaw_commands^ [index].rvsn = rvsn) AND
            (dmv$p_sc_flaw_commands^ [index].flaw_processed = FALSE) THEN
        IF NOT scan_only THEN
          dmv$p_sc_flaw_commands^ [index].flaw_processed := TRUE;
        IFEND;

        p_phys_adrs := ^dmv$p_sc_flaw_commands^ [index].phys_adrs;
        sec_specified := dmv$p_sc_flaw_commands^ [index].sec_specified;
        trk_specified := dmv$p_sc_flaw_commands^ [index].trk_specified;
        dmp$convert_to_dau_address (rvsn, p_phys_adrs, trk_specified, sec_specified,
            dau_adrs, end_dau_adrs, status);
        IF status.normal THEN
          applicable_flaw_count := applicable_flaw_count + 1;
          p_sc_dau_list^ [applicable_flaw_count].recorded_vsn := rvsn;
          p_sc_dau_list^ [applicable_flaw_count].first_dau := dau_adrs;
          p_sc_dau_list^ [applicable_flaw_count].last_dau := end_dau_adrs;
          p_sc_dau_list^ [applicable_flaw_count].operation_code := dmc$oc_flaw_define;
          p_sc_dau_list^ [applicable_flaw_count].initiator_code := dmc$ic_operator_initiated;
        IFEND;
      IFEND;
    FOREND;

    FOR index := LOWERBOUND (dmv$p_sc_flaw_commands^) TO UPPERBOUND (dmv$p_sc_flaw_commands^) DO
      IF dmv$p_sc_flaw_commands^ [index].flaw_processed = FALSE THEN
        release_sc_flaw_commands := FALSE;
      IFEND;
    FOREND;

    IF release_sc_flaw_commands THEN
      FREE dmv$p_sc_flaw_commands in osv$mainframe_pageable_heap^;
    IFEND;

  PROCEND dmp$construct_sc_dau_list;
?? TITLE := '  dmp$convert_to_dau_address', EJECT ??
  PROCEDURE [XDCL, #GATE] dmp$convert_to_dau_address (recorded_vsn: rmt$recorded_vsn;
        p_phys_adrs: ^dmt$physical_flaw_address;
        trk_specified: boolean;
        sec_specified: boolean;
    VAR dau_address: dmt$dau_address;
    VAR end_dau_address: dmt$dau_address;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure will convert the LCU cylinder, track, and sector operator entry
{     into a range of DAU addresses.

    VAR
      element_def: ^cmt$element_definition,
      end_sector_address: integer,
      lun: iot$logical_unit,
      p_physical_attributes: ^dmt$physical_device_attributes,
      product_id: cmt$product_identification,
      sector_address: integer,
      unused_sector_count: integer;

    end_sector_address := 0;
    status.normal := TRUE;

    dmp$get_logical_unit_number (recorded_vsn, lun, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH element_def;

    cmp$pc_get_logical_unit (lun, element_def, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    product_id := element_def^.product_id;

    PUSH p_physical_attributes: [1 .. 5];
    p_physical_attributes^[1].keyword := dmc$maus_per_cylinder;
    p_physical_attributes^[2].keyword := dmc$sectors_per_mau;
    p_physical_attributes^[3].keyword := dmc$sectors_per_track;
    p_physical_attributes^[4].keyword := dmc$maus_per_dau;
    p_physical_attributes^[5].keyword := dmc$cylinders_per_device;

    dmp$get_physical_attributes (product_id, p_physical_attributes, status);

{ Check the parameters given in the flaw command to see if they are in the physical range
{ of the device.

    IF p_phys_adrs^.cylinder >= p_physical_attributes^[5].cylinders_per_device THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$cylinder_limit_exceeded,
        'dmp$convert_to_dau_address', status);
      RETURN;
    ELSEIF trk_specified AND
        (p_phys_adrs^.track * p_physical_attributes^[3].sectors_per_track DIV
            p_physical_attributes^[2].sectors_per_mau >= p_physical_attributes^[1].maus_per_cylinder) THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$track_limit_exceeded,
        'dmp$convert_to_dau_address', status);
      RETURN;
    ELSEIF sec_specified AND (p_phys_adrs^.sector >= p_physical_attributes^[3].sectors_per_track) THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$sector_limit_exceeded,
        'dmp$convert_to_dau_address', status);
      RETURN;

{ If the last track in a cylinder is selected, check to insure the space is used.
{ On some devices, the number of sectors per DAU does not come out evenly.  The
{ remaining sectors are not used as a DAU can not cross cylinder boundaries.

    ELSEIF sec_specified AND ((p_phys_adrs^.track + 1) * p_physical_attributes^[3].sectors_per_track DIV
            p_physical_attributes^[2].sectors_per_mau >= p_physical_attributes^[1].maus_per_cylinder) THEN

      IF (p_phys_adrs^.sector >= p_physical_attributes^[3].sectors_per_track - (((p_phys_adrs^.track + 1) *
          p_physical_attributes^[3].sectors_per_track) - (p_physical_attributes^[2].sectors_per_mau *
               p_physical_attributes^[1].maus_per_cylinder))) THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unaddressable_sector,
            'dmp$convert_to_dau_address', status);
        RETURN;
      IFEND;

    IFEND;

{ If the last track on a cylinder is to be flawed, save the number of un-used sectors in the cylinder, if any,
{ so the number of sectors to be flawed can be adjusted later.

    IF ((p_phys_adrs^.track + 1) * p_physical_attributes^[3].sectors_per_track DIV
            p_physical_attributes^[2].sectors_per_mau >= p_physical_attributes^[1].maus_per_cylinder) THEN
      unused_sector_count := (((p_phys_adrs^.track + 1) * p_physical_attributes^[3].sectors_per_track) -
         (p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[1].maus_per_cylinder));
    ELSE
      unused_sector_count := 0;
    IFEND;

{ Since all parameters are in range, calculate the DAUs to be flawed.

    sector_address := p_phys_adrs^.cylinder * p_physical_attributes^[2].sectors_per_mau *
       p_physical_attributes^[1].maus_per_cylinder;
    IF trk_specified THEN
      sector_address := sector_address + (p_phys_adrs^.track * p_physical_attributes^[3].sectors_per_track);
      IF sec_specified THEN
        sector_address := sector_address + p_phys_adrs^.sector;
      ELSE
        end_sector_address := sector_address + (p_physical_attributes^[3].sectors_per_track) - 1 -
           unused_sector_count;
      IFEND;
    ELSE
      end_sector_address := sector_address + (p_physical_attributes^[2].sectors_per_mau *
         p_physical_attributes^[1].maus_per_cylinder) - 1;
    IFEND;

    dau_address := sector_address DIV (p_physical_attributes^[2].sectors_per_mau *
       p_physical_attributes^[4].maus_per_dau);

    IF end_sector_address = 0 THEN
      end_dau_address := dau_address;
    ELSE
      end_dau_address := end_sector_address DIV (p_physical_attributes^[2].sectors_per_mau *
         p_physical_attributes^[4].maus_per_dau);
    IFEND;
  PROCEND dmp$convert_to_dau_address;
?? TITLE := '  dmp$process_sc_flaw_commands', EJECT ??

{ PURPOSE:
{   This procedure will call a procedure to construct the flaw list, set the update lock,
{   open the dat, and call a procedure to process the flaws.  The lock will then be removed
{   and the DAT closed.

  PROCEDURE [XDCL] dmp$process_sc_flaw_commands
    (    avt_index: dmt$active_volume_table_index;
         dat_sfid: gft$system_file_identifier;
         recorded_vsn: rmt$recorded_vsn;
     VAR status: ost$status);

    VAR
      applicable_flaw_count: integer,
      p_dat: ^dmt$ms_device_allocation_table,
      p_sc_dau_list: ^array [1 .. * ] of dmt$log_flaw_init_data;

    status.normal := TRUE;
    IF dmv$p_sc_flaw_commands = NIL THEN
      RETURN; {----->
    IFEND;

    PUSH p_sc_dau_list: [1 .. UPPERBOUND (dmv$p_sc_flaw_commands^)];
    dmp$construct_sc_dau_list (recorded_vsn, FALSE, p_sc_dau_list, applicable_flaw_count, status);
    IF applicable_flaw_count <= 0 THEN
      RETURN; {----->
    IFEND;

    dmp$open_dat (dat_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_write_extend, mmc$as_sequential, p_dat,
          status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    osp$set_mainframe_sig_lock (dmv$active_volume_table.table_p^ [avt_index].mass_storage.update_lock);
    dmp$record_sc_flaw (applicable_flaw_count, p_dat, p_sc_dau_list);
    osp$clear_mainframe_sig_lock (dmv$active_volume_table.table_p^ [avt_index].mass_storage.update_lock);

    dmp$close_file (p_dat, status);

  PROCEND dmp$process_sc_flaw_commands;
?? TITLE := '  dmp$record_sc_flaw', EJECT ??
  PROCEDURE [XDCL] dmp$record_sc_flaw (applicable_flaw_count: integer;
        p_dat: ^dmt$ms_device_allocation_table;
        p_sc_dau_list: ^array [1 .. *] of dmt$log_flaw_init_data);

{   PURPOSE:
{     This procedure will change the DAU status in the DAT to reflect flaws
{     and issue a flaw statistic reporting the change to the engineering log.

    VAR
      dau_index: integer,
      index: integer,
      first_dau: dmt$dau_address,
      last_dau: dmt$dau_address,
      flaw_logging_data: dmt$log_flaw_init_data,
      p_dat_entry: ^dmt$ms_device_allocation_unit,
      p_log_data: ^SEQ (*),
      previous_status: dmt$dau_status;

    /process_flaw_list/
    FOR index := LOWERBOUND (p_sc_dau_list^) TO applicable_flaw_count DO

      first_dau := p_sc_dau_list^ [index].first_dau;
      last_dau := p_sc_dau_list^ [index].last_dau;
      p_dat_entry := ^p_dat^.body [first_dau];
      previous_status := p_dat_entry^.dau_status;

{ If only one DAU is to be altered, then check the DAU's status before changing.

      IF first_dau = last_dau THEN
        IF (previous_status = dmc$dau_software_flawed) OR (previous_status = dmc$dau_ass_to_mf_swr_flawed)
               OR (previous_status = dmc$dau_ass_to_file_swr_flawed) OR
               (previous_status = dmc$dau_hardware_flawed) THEN
          CYCLE /process_flaw_list/; {Do not issue statistic to the engineering log}
        IFEND;
      IFEND;

{ Change the DAU status to flawed.

      FOR dau_index := first_dau TO last_dau DO

        p_dat_entry := ^p_dat^.body [dau_index];
        CASE p_dat_entry^.dau_status OF

        = dmc$dau_usable =
          p_dat_entry^.dau_status := dmc$dau_software_flawed;
          p_dat^.header.available := p_dat^.header.available - 1;

        = dmc$dau_assigned_to_mainframe =
          p_dat_entry^.dau_status := dmc$dau_ass_to_mf_swr_flawed;

        = dmc$dau_assigned_to_file =
          p_dat_entry^.dau_status := dmc$dau_ass_to_file_swr_flawed;

        = dmc$dau_hardware_flawed, dmc$dau_software_flawed, dmc$dau_ass_to_mf_swr_flawed,
            dmc$dau_ass_to_file_swr_flawed =

        ELSE
          ;
        CASEND;
      FOREND;

{ Issue statistics about flaw to the engineering log.

      p_log_data := #SEQ (p_sc_dau_list^ [index]);
      dsp$log_sys_msg_help (cml$ms_media_flaw_change, p_log_data);
    FOREND /process_flaw_list/;

  PROCEND dmp$record_sc_flaw;
?? TITLE := '  dmp$store_sc_flaw_command', EJECT ??
  PROCEDURE [XDCL, #GATE] dmp$store_sc_flaw_command (
        p_sc_flaw: ^dmt$sc_flaw_command);

{   PURPOSE:
{     This procedure will add the new system core flaw command to the existing array
{     of commands, if one is present, or create an array of one entry if it has not
{     yet been created.

    VAR
      flaw_index: integer,
      free_ptr: ^array [1 .. *] of dmt$sc_flaw_command,
      new_count: integer,
      p_new_sc_flaws: ^array [1 .. *] of dmt$sc_flaw_command,
      present_count: integer;

    IF dmv$p_sc_flaw_commands <> NIL THEN
      present_count := UPPERBOUND (dmv$p_sc_flaw_commands^);
      new_count := present_count + 1;

      ALLOCATE p_new_sc_flaws: [1 .. new_count] IN osv$mainframe_pageable_heap^;

      FOR flaw_index := 1 TO present_count DO
        p_new_sc_flaws^ [flaw_index] := dmv$p_sc_flaw_commands^ [flaw_index];
      FOREND;

      p_new_sc_flaws^ [new_count] := p_sc_flaw^;

      free_ptr := dmv$p_sc_flaw_commands;
      dmv$p_sc_flaw_commands := p_new_sc_flaws;
      FREE free_ptr IN osv$mainframe_pageable_heap^;
    ELSE
      ALLOCATE dmv$p_sc_flaw_commands: [1 .. 1] IN osv$mainframe_pageable_heap^;

      dmv$p_sc_flaw_commands^ [1] := p_sc_flaw^;
    IFEND;
  PROCEND dmp$store_sc_flaw_command;
MODEND dmm$flaw_management;
