?? RIGHT := 110 ??
?? TITLE := 'MMM$SYSTEM_IMAGE_RECOVERY' ??
MODULE mmm$system_image_recovery;

{ PURPOSE:
{   This module contains the memory manager routines for system recovery.

?? NEWTITLE := '  External Declarations Referenced by this module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc gfc$constants
*copyc gft$file_desc_entry_p
*copyc gft$system_file_identifier
*copyc mme$condition_codes
*copyc mmt$ast_index
*copyc mmt$image_page_description
*copyc mmt$old_modified_bits
*copyc mmt$page_frame_index
*copyc osc$asid_ei
*copyc osd$cybil_structure_definitions
*copyc osd$virtual_address
*copyc oss$job_paged_literal
*copyc ost$page_size
*copyc ost$page_table
*copyc ost$status
?? EJECT ??
?? POP ??
*copyc dsp$get_nve_image_description
*copyc i#build_adaptable_array_ptr
*copyc osp$set_status_condition
*copyc osp$append_status_integer
*copyc mmv$ast_p

*copyc syp$process_deadstart_status
?? OLDTITLE ??
?? NEWTITLE := '  Global Declarations Referenced by this module', EJECT ??

  TYPE
    asid_asti_operand = 0 .. 10000(16),
    page_validations = (page_null_validation, page_doesnt_need_to_be_written, page_needs_to_be_written);

  VAR
    a_divisor: 0 .. 10000(16),
    a_mult: 0 .. 10000(16),
    bits: [READ, STATIC, oss$job_paged_literal] array [0 .. 15] of 0 .. 15 := [0, 8, 4, 12, 2, 10, 6, 14, 1,
          9, 5, 13, 3, 11, 7, 15],
    image_ast_p: ^mmt$active_segment_table := NIL,
    image_iht_p: ^mmt$old_modified_bits := NIL,
    image_offset: ost$segment_offset,
    image_page_size: ost$page_size,
    image_pft_p: ^mmt$page_frame_table := NIL,
    image_segment_number: ost$segment,
    mmv$a_divisor: [XREF] 0 .. 10000(16),
    mmv$a_mult: [XREF] 0 .. 10000(16),
    rcv_mfw_segment: ost$segment,
    recovery_load_offset: ost$segment_offset;

?? OLDTITLE ??
?? NEWTITLE := '  [INLINE] image_asid_to_image_asti', EJECT ??

  FUNCTION [INLINE] image_asid_to_image_asti
    (    xasid: ost$asid): mmt$ast_index;

    VAR
      asid: ost$asid;

    asid := xasid;
    asid := (asid DIV a_mult) + ((asid MOD a_mult) * a_divisor);
    image_asid_to_image_asti := (bits [asid MOD 16] * 4096) + (bits [(asid DIV 16) MOD 16] * 256) +
          (bits [(asid DIV 256) MOD 16] * 16) + bits [(asid DIV 4096) MOD 16];

  FUNCEND image_asid_to_image_asti;

?? OLDTITLE ??
?? NEWTITLE := '  [INLINE] image_asti_to_image_asid', EJECT ??

  FUNCTION [INLINE] image_asti_to_image_asid
    (    xasti: mmt$ast_index): ost$asid;

    VAR
      asti: mmt$ast_index,
      asid: ost$asid;

    asti := xasti;
    asid := (bits [asti MOD 16] * 4096) + (bits [(asti DIV 16) MOD 16] * 256) +
          (bits [(asti DIV 256) MOD 16] * 16) + bits [(asti DIV 4096) MOD 16];
    image_asti_to_image_asid := (asid DIV a_divisor) + ((asid MOD a_divisor) * a_mult);

  FUNCEND image_asti_to_image_asid;
?? OLDTITLE ??
?? NEWTITLE := '  [XDCL, #GATE] mmp$fetch_image_page_count', EJECT ??

*copy mmh$fetch_image_page_count

  PROCEDURE [XDCL, #GATE] mmp$fetch_image_page_count
    (VAR image_page_count: 0 .. osc$max_page_frames);

    VAR
      image_ast_pp: ^^mmt$active_segment_table,
      image_description: dst$nve_image_descriptor,
      operand_p: ^asid_asti_operand,
      ptr_to_adaptable_array: ^^cell;


    IF image_pft_p = NIL THEN
      dsp$get_nve_image_description (image_description);
      IF (image_description.nve_image = NIL) OR (image_description.rcv_mainframe_wired_segment = NIL) THEN
        image_page_count := 0;
        RETURN;
      IFEND;

      image_segment_number := #SEGMENT (image_description.nve_image);
      image_page_size := image_description.rcv_page_size;
      recovery_load_offset := image_description.rcv_load_offset;
      image_offset := #OFFSET (image_description.nve_image);
      image_pft_p := image_description.rcv_page_frame_tbl_p;
      image_iht_p := image_description.rcv_hash_tbl_p;
      rcv_mfw_segment := #SEGMENT (image_description.rcv_mainframe_wired_segment);

      image_ast_pp := #ADDRESS (1, rcv_mfw_segment, #OFFSET (^mmv$ast_p));
      image_ast_p := image_ast_pp^;
      ptr_to_adaptable_array := #LOC (image_ast_p);
      i#build_adaptable_array_ptr (1, rcv_mfw_segment, #OFFSET (image_ast_p), #SIZE (image_ast_p^), LOWERBOUND
            (image_ast_p^), #SIZE (image_ast_p^ [LOWERBOUND (image_ast_p^)]), #LOC (ptr_to_adaptable_array^));

      operand_p := #ADDRESS (1, rcv_mfw_segment, #OFFSET (^mmv$a_mult));
      a_mult := operand_p^;
      operand_p := #ADDRESS (1, rcv_mfw_segment, #OFFSET (^mmv$a_divisor));
      a_divisor := operand_p^;
    IFEND;

    image_page_count := (UPPERBOUND (image_pft_p^) - LOWERBOUND (image_pft_p^)) + 1;

  PROCEND mmp$fetch_image_page_count;
?? OLDTITLE ??
?? NEWTITLE := '  [XDCL, #GATE] mmp$fetch_pvas_of_image_pages', EJECT ??

*copy mmh$fetch_pvas_of_image_pages

  PROCEDURE [XDCL, #GATE] mmp$fetch_pvas_of_image_pages
    (    old_fde_p: gft$file_desc_entry_p;
         desc_p: {OUTPUT/dereferenced} ^mmt$image_page_description;
     VAR status: ost$status);

?? NEWTITLE := '    determine_page_status', EJECT ??

    PROCEDURE [INLINE] determine_page_status
      (    image_pft_entry_p: ^mmt$page_frame_table_entry;
           image_aste_p: ^mmt$active_segment_table_entry;
           old_fde_p: gft$file_desc_entry_p;
       VAR page_status: page_validations);


      page_status := page_doesnt_need_to_be_written;

{ The page is not valid for a write from the image to disk if it is not modified and it is not locked for IO.

      IF NOT image_iht_p^ [image_pft_entry_p^.pti] AND (image_pft_entry_p^.active_io_count = 0) THEN
        RETURN;
      IFEND;

{ The page is not valid for a write from the image to disk if the page lock indicates the page doesn't contain
{ data (page is being read from disk or server allocation is occurring) or the user has locked the page to
{ prevent IO.

      IF (image_pft_entry_p^.locked_page = mmc$lp_aging_lock) OR
            (image_pft_entry_p^.locked_page = mmc$lp_server_allocate_lock) OR
            (image_pft_entry_p^.locked_page = mmc$lp_page_in_lock) THEN
        RETURN;
      IFEND;

{ The page is not valid for a write from the image to disk if the segment is locked (MMP$LOCK_SEGMENT)
{ UNLESS the page is still being written from a previous MMP$UNLOCK_SEGMENT with write_protection.

      IF old_fde_p^.segment_lock.locked_for_write AND (image_pft_entry_p^.locked_page <>
            mmc$lp_write_protected_lock) THEN
        RETURN;
      IFEND;

      page_status := page_needs_to_be_written;

    PROCEND determine_page_status;
?? OLDTITLE, EJECT ??

    VAR
      asid: ost$asid,
      asti: mmt$ast_index,
      fast_search_mode: boolean,
      image_aste_p: ^mmt$active_segment_table_entry,
      image_pft_entry_p: ^mmt$page_frame_table_entry,
      image_pva: ^cell,
      image_sfid: gft$system_file_identifier,
      page_descriptor_array_index: mmt$page_frame_index,
      page_status: page_validations,
      pft_page_count: 0 .. osc$max_page_frames,
      pfti: integer;


    status.normal := TRUE;

    page_descriptor_array_index := LOWERBOUND (desc_p^.page_desc);
    desc_p^.pagesize := image_page_size;

    asti := old_fde_p^.asti;
    IF asti > UPPERBOUND (image_ast_p^) THEN
      osp$set_status_condition (mme$computed_asti_out_of_range, status);
      osp$append_status_integer (osc$status_parameter_delimiter, asti, 16, TRUE, status);
      osp$append_status_integer (osc$status_parameter_delimiter, UPPERBOUND (image_ast_p^), 16, TRUE, status);
      RETURN; {----->
    IFEND;

    image_aste_p := ^image_ast_p^ [asti];
    image_sfid.file_entry_index := ((#OFFSET (old_fde_p)) - gfc$fde_table_base) DIV gfc$fde_size;
    IF NOT image_aste_p^.in_use OR (image_aste_p^.sfid.file_entry_index <>
        image_sfid.file_entry_index) THEN
      desc_p^.valid_desc_count := 0;
      RETURN;
    IFEND;

    asid := image_asti_to_image_asid (asti);
    pfti := image_aste_p^.pft_link.fwd;
    fast_search_mode := TRUE;

    WHILE TRUE DO
      pft_page_count := 0;

      WHILE pfti <> 0 DO
        page_status := page_null_validation;
        IF (pfti >= LOWERBOUND (image_pft_p^)) AND (pfti <= UPPERBOUND (image_pft_p^)) AND
              (#OFFSET (image_pft_p^ [pfti].aste_p) = #OFFSET (image_aste_p)) AND
              (image_pft_p^ [pfti].sva.asid = asid) AND (pft_page_count <= UPPERBOUND (desc_p^.page_desc))
              THEN

{ Check if the page is really valid.  It is not valid if it is in the process of being read from mass storage.
{ Therefore, the following code should return only valid pages.

          pft_page_count := pft_page_count + 1;
          image_pva := #ADDRESS (1, image_segment_number, (pfti * image_page_size - recovery_load_offset) +
                image_offset);
          image_pft_entry_p := ^image_pft_p^ [pfti];
          determine_page_status (image_pft_entry_p, image_aste_p, old_fde_p, page_status);
          IF page_status = page_needs_to_be_written THEN
            desc_p^.page_desc [page_descriptor_array_index].image_pva := image_pva;
            desc_p^.page_desc [page_descriptor_array_index].file_offset := image_pft_entry_p^.sva.offset;
            page_descriptor_array_index := page_descriptor_array_index + 1;
          IFEND;
        ELSE
          fast_search_mode := FALSE;
        IFEND;

        IF fast_search_mode THEN
          pfti := image_pft_entry_p^.segment_link.fwd;
          IF page_status = page_null_validation THEN
            pfti := 0;
          IFEND;
        ELSE {NOT fast_search_mode; doesn't matter what the page validation was}
          pfti := pfti + 1;
          IF pfti > UPPERBOUND (image_pft_p^) THEN
            pfti := 0;
          IFEND;
        IFEND;
      WHILEND;

      IF NOT fast_search_mode OR (pft_page_count = image_aste_p^.pages_in_memory) THEN
        desc_p^.valid_desc_count := page_descriptor_array_index - LOWERBOUND (desc_p^.page_desc);
        RETURN;
      IFEND;

{ If the recovery reaches this point, the assumption is that the image Active Segment Table is not intact for
{ the ASID in question.  Unfortunately (for performance' sake), we must go through the entire page frame table
{ to obtain the appropriate pages in this manner.

      page_descriptor_array_index := LOWERBOUND (desc_p^.page_desc);
      pfti := LOWERBOUND (image_pft_p^);
      fast_search_mode := FALSE;
    WHILEND;

  PROCEND mmp$fetch_pvas_of_image_pages;
?? OLDTITLE, OLDTITLE ??
MODEND mmm$system_image_recovery;
