?? RIGHT := 110, LEFT := 1 ??
MODULE mmm$deadstart_initialization;

?? SKIP := 3 ??
{
{  PURPOSE:
{     This module contains procedures used during deadstart to initialize
{     memory manager tables.
{

?? PUSH (LISTEXT := ON) ??
*copyc gfv$null_sfid
*copyc dst$rb_system_deadstart_status
*copyc jst$swap_file_descriptor
*copyc mlt$ant_entry
*copyc osd$cybil_structure_definitions
*copyc osv$170_os_type
*copyc osv$mainframe_wired_heap
*copyc osv$mainframe_wired_cb_heap
*copyc osv$mainframe_pageable_heap
*copyc jmv$ijl_p
*copyc jmv$system_ijl_ordinal
*copyc jmv$jcb
*copyc mmt$attribute_keyword
*copyc mmv$mf_wired_asid_p
*copyc mmv$pages_to_dump_p
*copyc mmt$mainframe_wired_asid
*copyc mmt$manage_memory_utility
*copyc mmc$first_transient_segment
*copyc mmt$rb_segment_request
*copyc mmt$segment_descriptor_table
*copyc mmt$segment_descriptor_table_ex
*copyc osd$virtual_address
*copyc ost$page_table
*copyc osv$enable_hyperchannel
*copyc ost$segment_descriptor
*copyc osc$processor_defined_registers
*copyc ost$execution_control_block
?? POP ??
?? SKIP := 3 ??
{External procedures used by this module.

*copyc osp$reset_heap
*copyc osp$reset_heap_ext
*copyc dsp$allocate_continuous_memory
*copyc dsp$fetch_boot_data
*copyc dsp$get_flaw_map
*copyc gfp$assign_fde
*copyc gfp$get_fde_p
*copyc i#call_monitor
*copyc jmp$get_ijle_p
*copyc mmf$include_pages_in_dump
*copyc mmp$assign_mass_storage
*copyc mmp$convert_ps_transfer_size
*copyc mmp$free_pages
*copyc mmp$get_max_sdt_pointer
*copyc mmp$get_max_sdt_sdtx_pointer
*copyc mmp$get_sdt_entry_p
*copyc mmp$get_sdtx_entry_p
*copyc mmp$issue_ring1_segment_request
*copyc mmp$set_segment_access_rights
*copyc osp$fatal_system_error
*copyc osp$system_error
*copyc pmp$find_executing_task_xcb
*copyc pmp$zero_out_table
?? SKIP := 3 ??
*copyc dsv$ssr_size
*copyc jmv$sdt
*copyc jmv$sdtx
*copyc jmv$jmtr_xcb
*copyc jsv$swapped_page_entry_size
*copyc mmv$big_segment
*copyc mmv$maximum_180_memory
*copyc mmv$tables_initialized
*copyc mmv$total_page_frames
*copyc mmv$pt_length
*copyc mmv$time_to_call_mem_mgr
*copyc mmv$pfti_array_p
*copyc mmv$reassignable_page_frames
*copyc mmv$image_file
*copyc mmv$ast_p
*copyc mmv$continue_bit_count_p
*copyc mmv$gpql
*copyc mmv$manage_memory_utility
*copyc mmv$pages_per_new_page_fault
*copyc mmv$periodic_call_interval
*copyc mmv$shared_queue_age_interval
*copyc mmv$pft_p
*copyc mmv$pt_p
*copyc mmv$default_sdtx_entry
*copyc mtv$mx_segments
*copyc osv$cpus_physically_configured
*copyc osv$180_memory_limits
*copyc osv$page_size
*copyc dfv$server_wired_heap
*copyc nav$network_wired_heap
*copyc nav$network_paged_heap

{  Define variable that will contain the SDT index for the first transient segment.

  VAR
    mmv$first_transient_seg_index: [XDCL] ost$segment := mmc$first_transient_segment;

  VAR
    mlv$shared_segment: [XREF] mlt$shared_segment;

  VAR
    osv$np_heap_min_frag_alloc_size: [XDCL] integer := 0c(16);

?? TITLE := 'ASID CONVERSION FUNCTIONS' ??
?? EJECT ??
*copyc mmp$asid_functions
?? TITLE := 'MMP$ADD_GLOBAL_TEMPLATE_SEGMENT', EJECT ??
{
{ This procedure is used during deadstart to add job template segments to the address space
{ of the system job monitor.
{


  PROCEDURE [XDCL] mmp$add_global_template_segment
    (    sdt_entry: mmt$segment_descriptor;
         sdtx_entry: mmt$segment_descriptor_extended;
         segnum: ost$segment;
     VAR status: ost$status);

    VAR
      fde_p: gft$file_desc_entry_p,
      page_streaming_transfer_size: 0 .. 15,
      ste_p: ^mmt$segment_descriptor,
      stxe_p: ^mmt$segment_descriptor_extended,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;

    pmp$find_executing_task_xcb (xcb_p);
    IF xcb_p <> ^jmv$jmtr_xcb THEN
      osp$system_error ('MM - incorrect call to mmp$add_global_template_segment', NIL);
    IFEND;

    mmp$convert_ps_transfer_size (16384, page_streaming_transfer_size); {convert TS to pages in a power of 2
    ste_p := mmp$get_sdt_entry_p (xcb_p, segnum);
    stxe_p := mmp$get_sdtx_entry_p (xcb_p, segnum);

    ste_p^ := sdt_entry;
    stxe_p^ := sdtx_entry;
    stxe_p^.stream.transfer_size := page_streaming_transfer_size;

    IF stxe_p^.sfid.residence = gfc$tr_null_residence THEN
      assign_fde (gfc$tr_job, 0, segnum, stxe_p^.sfid, fde_p);
    IFEND;

    gfp$get_fde_p (stxe_p^.sfid, fde_p);
    fde_p^.flags.global_template_file := TRUE;

  PROCEND mmp$add_global_template_segment;
?? TITLE := 'MMP$WRITE_ALL_SEGMENTS_TO_DISK', EJECT ??

  PROCEDURE [XDCL, #GATE] mmp$write_all_segments_to_disk
    (VAR status: ost$status);


    VAR
      xcb_p: ^ost$execution_control_block,
      rb: mmt$rb_ring1_segment_request,
      segnum: ost$segment,
      st_p: mmt$max_sdt_p;

    rb.reqcode := syc$rc_ring1_segment_request;
    rb.request := mmc$sr1_flush_seg_segnum;
    rb.wait_for_io_complete := FALSE;
    pmp$find_executing_task_xcb (xcb_p);
    mmp$get_max_sdt_pointer (xcb_p, st_p);
    FOR segnum := 1 TO #READ_REGISTER (osc$pr_segment_table_length) DO
      IF (st_p^.st [segnum].ste.vl <> osc$vl_invalid_entry) THEN
        mmp$assign_mass_storage (segnum, gfv$null_sfid, 0, status);
        IF status.normal AND (segnum <> xcb_p^.xp.tos_registers [1].pva.seg) THEN
          rb.segnum := segnum;
          mmp$issue_ring1_segment_request (rb);
        IFEND;
      IFEND;
    FOREND;
    status.normal := TRUE;

  PROCEND mmp$write_all_segments_to_disk;
?? TITLE := 'MMP$INITIALIZE', EJECT ??

{-------------------------------------------------------------------------------------------------------
{Name:
{  mmp$initialize
{Purpose:
{    This routine is the first procedure in the module mmm$deadstart_initialization to be called during
{    deadstart.  Later deadstart will make separate calls to mmp$assign_device_shared_segs,
{    mmp$pft_initialize, and to mmp$write_all_segments_to_disk.
{Input:
{    none
{Output:
{    The static data constants are initialized and the SDTX will be initialized via
{    a call to the procedure mmp$sdtx_initialization.
{Error Codes:
{    none
{Notes:
{    - The system heap must be initialized before calling this routine
{--------------------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mmp$initialize;

    VAR
      pti: integer,
      i: integer,
      index_ma: mmt$mmu_memory_attributes;

{Set up static data constants.

    i := #READ_REGISTER (osc$pr_page_table_length);
    mmv$pt_length := (i + 1) * 512;
    i := i MOD 100(16);
    mmv$a_divisor := 256 DIV (i + 1);
    mmv$a_mult := 10000(16) DIV mmv$a_divisor;
    mmv$pt_p := #ADDRESS (1, 0, 0);

{ Set all continue bits in the page table. Continue bits are not managed by the page
{ fault processor used before AST/PFT initialization is complete. The continue bits will
{ be cleaned up during PFT initialization.

    FOR pti := 0 TO mmv$pt_length - 1 DO
      mmv$pt_p^ [pti].c := TRUE;
    FOREND;


{  Copy the current values of mmv$gpql and other mmv$ variables managed by the Manage Memory Utility into
{  the default copies so that the MMU will have available the original values when it is requested to
{  reset values to their default.

    FOR index_ma := LOWERBOUND (mmv$manage_memory_utility.ma) TO UPPERBOUND (mmv$manage_memory_utility.ma) DO
      IF mmv$manage_memory_utility.ma [index_ma].value_type = mmc$mmu_mvt_integer THEN
        mmv$manage_memory_utility.ma [index_ma].default := mmv$manage_memory_utility.ma [index_ma].integer_p^;
      ELSE { mmc$mmu_mvt_byte }
        mmv$manage_memory_utility.ma [index_ma].default := mmv$manage_memory_utility.ma [index_ma].byte_p^;
      IFEND;
    FOREND;

    mmv$manage_memory_utility.gpql := mmv$gpql;


{ Initialize fields in the SDTX.

    mmp$sdtx_initialization;

  PROCEND mmp$initialize;
?? TITLE := 'MMP$PFT_INITIALIZE' ??
?? EJECT ??

{--------------------------------------------------------------------------------------------------------
{Name:
{  mmp$pft_initialize
{Purpose:
{  By the time this procedure is called the other procedures in mmm$deadstart_initialization have
{  already been executed.  This routine initializes the PFT, PQL and the defaults for Manage_Memory
{Input:
{Output:
{  none
{Error Code:
{  none
{--------------------------------------------------------------------------------------------------------

  PROCEDURE [XDCL] mmp$pft_initialize;

    TYPE
      asidq_type = record
        asid: ost$asid,
        queue_id: mmt$page_frame_queue_id,
        qcb_p: ^mmt$page_queue_list_entry,
        aste_p: ^mmt$active_segment_table_entry,
        fde_p: gft$file_desc_entry_p,
      recend;

    VAR
      asid: ost$asid,
      asid1: ost$asid,
      asid2: ost$asid,
      asid3: ost$asid,
      asid_seq_p: ^SEQ ( * ),
      asid_size: integer,
      asidq: array [0 .. 40] of asidq_type,
      asidq_p: ^asidq_type,
      aste_p: ^mmt$active_segment_table_entry,
      asti: mmt$ast_index,
      boot_asids: dst$boot_asids,
      boot_asids_seq_p: ^SEQ ( * ),
      cbc_seq_p: ^SEQ ( * ),
      count: 1 .. 32,
      dummy_p: ^cell,
      dummy_reference: cell,
      fde_p: gft$file_desc_entry_p,
      first_image_pfti: 0 .. 0ffffffff(16),
      flaw_map_p: ^array [1 .. *] of mmt$page_frame_index,
      found: boolean,
      fwd_link: integer,
      i: integer,
      ijle_p: ^jmt$initiated_job_list_entry,
      ipti: integer,
      j: integer,
      last_asid: ost$asid,
      last_link_p: ^mmt$link,
      next_asidq_index: integer,
      pft_p: ^mmt$page_frame_table_entry,
      pft_seq_p: ^SEQ ( * ),
      pft_size: integer,
      pfti: mmt$page_frame_index,
      pftimax: integer,
      pftimin: integer,
      pt_p: ^ost$page_table,
      ptd_seq_p: ^SEQ ( * ),
      ptd_size: integer,
      pte_p: ^ost$page_table_entry,
      pti: ost$page_table_index,
      qcb_p: ^mmt$page_queue_list_entry,
      queue_id: mmt$page_frame_queue_id,
      rb: mmt$rb_ring1_segment_request,
      rb_sds: dst$rb_system_deadstart_status,
      residence: gft$table_residence,
      ssr_size: integer,
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      sva: ost$system_virtual_address,
      sdt_entry: mmt$segment_descriptor,
      sdte: [STATIC] mmt$segment_descriptor := [[osc$vl_cache_bypass, osc$non_executable,
            osc$read_uncontrolled, osc$write_uncontrolled, 1, 1, 0, * ], 0, 0],
      sdtx_entry: mmt$segment_descriptor_extended,
      status: ost$status;


{ Set up maximum amount of memory that 180 will use. This is for DEBUG purposes only and so
{ Display_System_Attribute can be used to verify the amount of memory being used.

    mmv$maximum_180_memory := (mmv$maximum_180_memory DIV osv$page_size) * osv$page_size;
    IF (osv$180_memory_limits.upper - osv$180_memory_limits.lower) > mmv$maximum_180_memory THEN
      osv$180_memory_limits.upper := osv$180_memory_limits.lower + mmv$maximum_180_memory;
    IFEND;
    mmv$maximum_180_memory := osv$180_memory_limits.upper - osv$180_memory_limits.lower;

{ Set maximum number of ASIDs based on memory size and allocate the AST.

    IF mtv$mx_segments = 0 THEN
      mtv$mx_segments := ((osv$180_memory_limits.upper - osv$180_memory_limits.lower) DIV osv$page_size) +
            300;
      IF mtv$mx_segments > 0FFFE(16) THEN
        mtv$mx_segments := 0FFFE(16);
      IFEND;
    IFEND;
    mmv$number_free_astes := mtv$mx_segments;
    ALLOCATE mmv$ast_p: [0 .. (mtv$mx_segments)] IN osv$mainframe_wired_heap^;

{ Allocate the PFT.

    i := osv$180_memory_limits.lower DIV osv$page_size;
    IF i = 0 THEN
      i := 1;
    IFEND;
    pft_size := (osv$180_memory_limits.upper DIV osv$page_size - i) * (#SIZE (mmt$page_frame_table_entry));
    dsp$allocate_continuous_memory (osv$mainframe_wired_heap, pft_size, pft_seq_p);
    RESET pft_seq_p;
    NEXT mmv$pft_p: [i .. (osv$180_memory_limits.upper DIV osv$page_size) - 1] IN pft_seq_p;

    pftimin := LOWERBOUND (mmv$pft_p^);
    pftimax := UPPERBOUND (mmv$pft_p^);
    mmv$total_page_frames := pftimax - pftimin;

{ Get the flaw map now before the pft is built so that it will be accounted for.

    dsp$get_flaw_map (flaw_map_p);

{ Allocate the critical dump page bit table.

    ssr_size := ((dsv$ssr_size + osv$page_size - 1) DIV osv$page_size) * osv$page_size;
    ptd_size := (((osv$180_memory_limits.upper + ssr_size) DIV osv$page_size) + 64) DIV 8;
    dsp$allocate_continuous_memory (osv$mainframe_wired_heap, ptd_size, ptd_seq_p);
    RESET ptd_seq_p;
    NEXT mmv$pages_to_dump_p: [0 .. (ptd_size * 8) - 1] IN ptd_seq_p;

    dsp$allocate_continuous_memory (osv$mainframe_wired_heap, mmv$pt_length, cbc_seq_p);
    RESET cbc_seq_p;
    NEXT mmv$continue_bit_count_p: [0 .. mmv$pt_length - 1] IN cbc_seq_p;

    ALLOCATE mmv$pfti_array_p: [0 .. (osv$180_memory_limits.upper DIV osv$page_size) - i] IN
          osv$mainframe_wired_heap^;

{Zero out the tables allocated.}

    pmp$zero_out_table (#LOC (mmv$ast_p^), #SIZE (mmv$ast_p^));
    pmp$zero_out_table (#LOC (mmv$pft_p^), #SIZE (mmv$pft_p^));
    pmp$zero_out_table (#LOC (mmv$pages_to_dump_p^), #SIZE (mmv$pages_to_dump_p^));
    pmp$zero_out_table (#LOC (mmv$continue_bit_count_p^), #SIZE (mmv$continue_bit_count_p^));
    pmp$zero_out_table (#LOC (mmv$pfti_array_p^), #SIZE (mmv$pfti_array_p^));

{  Set cache bypass if multiprocessing enabled.

    mmp$get_max_sdt_sdtx_pointer (^jmv$jmtr_xcb, sdt_p, sdtx_p);

    IF osv$cpus_physically_configured > 1 THEN
      sdt_p^.st [osc$segnum_mainframe_paged].ste.vl := osc$vl_cache_bypass;
      sdt_p^.st [#SEGMENT (#LOC (nav$network_paged_heap^))].ste.vl := osc$vl_cache_bypass;
      sdt_p^.st [#SEGMENT (^mlv$shared_segment)].ste.vl := osc$vl_cache_bypass;
    ELSE

{  Make mainframe wired a cache segment

      rb.reqcode := syc$rc_ring1_segment_request;
      rb.request := mmc$sr1_make_mfw_cache;
      rb.wait_for_io_complete := FALSE;
      sdt_p^.st [#SEGMENT (osv$mainframe_wired_heap)].ste.vl := osc$vl_regular_segment;
      mmp$issue_ring1_segment_request (rb);
    IFEND;


    jmp$get_ijle_p (jmv$system_ijl_ordinal, ijle_p);
    ijle_p^.job_fixed_asid := sdt_p^.st [osc$segnum_job_fixed_heap].ste.asid;


{ Set up the table used to locate mainframe wired if the system crashes while ASID
{ reassignment on mainframe wired is active. If the system crashes while this is
{ happening, both old and new ASIDs must be located in the page table.

    asid_size := #SIZE (mmt$mainframe_wired_asid);
    dsp$allocate_continuous_memory (osv$mainframe_wired_heap, asid_size, asid_seq_p);
    RESET asid_seq_p;
    NEXT mmv$mf_wired_asid_p IN asid_seq_p;
    mmv$mf_wired_asid_p^.current := sdt_p^.st [osc$segnum_mainframe_wired].ste.asid;
    mmv$mf_wired_asid_p^.new := 0;


{ Free the pages used by the boot. The easiest way to do this at this point in deadstart
{ is to delete the page table entries used by the boot.

    boot_asids_seq_p := #SEQ (boot_asids);
    dsp$fetch_boot_data (dsc$boot_asids, boot_asids_seq_p);
    sdt_p^.st [osc$segnum_job_pageable_heap].ste.vl := osc$vl_invalid_entry;

    pt_p := mmv$pt_p;
    asid1 := boot_asids.code_data;
    asid2 := boot_asids.job_stack;
    asid3 := boot_asids.mtr_stack;
    FOR pti := 0 TO mmv$pt_length - 1 DO
      asid := pt_p^ [pti].pageid.asid;
      IF (asid = asid1) OR (asid = asid2) OR (asid = asid3) THEN
        pt_p^ [pti].v := FALSE;
        pt_p^ [pti].pageid.asid := 0;
      IFEND;
    FOREND;


{ Search the segment table of the job monitor. For each valid entry, create an AST
{ and FDE entry that describes the segment.

    next_asidq_index := 0;

  /scan_sdt/
    FOR i := 0 TO jmv$jmtr_xcb.xp.segment_table_length DO
      IF sdt_p^.st [i].ste.vl = osc$vl_invalid_entry THEN
        CYCLE /scan_sdt/
      IFEND;

      asidq_p := ^asidq [next_asidq_index];

      IF mmc$sa_wired IN sdtx_p^.sdtx_table [i].software_attribute_set THEN
        queue_id := mmc$pq_wired;
      ELSEIF mmc$sa_fixed IN sdtx_p^.sdtx_table [i].software_attribute_set THEN
        queue_id := mmc$pq_job_fixed;
      ELSEIF sdtx_p^.sdtx_table [i].open_validating_ring_number = 0 THEN
        queue_id := mmc$pq_shared_task_service;
      ELSE
        queue_id := mmc$pq_job_working_set;
      IFEND;

      asidq_p^.asid := sdt_p^.st [i].ste.asid;
      asidq_p^.queue_id := queue_id;
      mmp$asti (asidq_p^.asid, asti);
      sdt_p^.st [i].asti := asti;

      IF asti <= UPPERBOUND (mmv$ast_p^) THEN
        asidq_p^.aste_p := ^mmv$ast_p^ [asti];
        IF NOT asidq_p^.aste_p^.in_use THEN
          asidq_p^.aste_p^.in_use := TRUE;
          IF queue_id < mmc$pq_job_base THEN
            residence := gfc$tr_system;
          ELSE
            residence := gfc$tr_job;
          IFEND;
          assign_fde (residence, asti, i, asidq_p^.aste_p^.sfid, asidq_p^.fde_p);
          sdtx_p^.sdtx_table [i].sfid := asidq_p^.aste_p^.sfid;
          IF (queue_id = mmc$pq_job_working_set) OR (queue_id = mmc$pq_shared_task_service) THEN
            sdtx_p^.sdtx_table [i].assign_active := mmc$assign_active_escaped;
          IFEND;
          asidq_p^.aste_p^.queue_id := asidq_p^.queue_id;
          asidq_p^.aste_p^.ijl_ordinal := jmv$system_ijl_ordinal;
          IF mmc$sa_stack IN sdtx_p^.sdtx_table [i].software_attribute_set THEN
            asidq_p^.fde_p^.stack_for_ring := sdt_p^.st [i].ste.r1;
          IFEND;

          asidq_p^.aste_p^.include_pages_in_dump := mmf$include_pages_in_dump (i, asidq_p^.fde_p,
                ^sdt_p^.st [i]);
          mmv$number_free_astes := mmv$number_free_astes - 1;
        IFEND;

      ELSE
        asidq_p^.aste_p := NIL;
        asidq_p^.fde_p := NIL;
      IFEND;

      IF queue_id < mmc$pq_job_base THEN
        asidq_p^.qcb_p := ^mmv$gpql [queue_id].pqle;
      ELSE
        asidq_p^.qcb_p := ^ijle_p^.job_page_queue_list [queue_id];
      IFEND;
      next_asidq_index := next_asidq_index + 1;

    FOREND /scan_sdt/;


{ Search thru the page table. Initialize the PFT entry for each page found and
{ link the page to the correct page queue.

    last_asid := 0;

  /scan_page_table/
    FOR pti := mmv$pt_length - 1 DOWNTO 0 DO
      asid := mmv$pt_p^ [pti].pageid.asid;
      IF asid = 0 THEN
        CYCLE /scan_page_table/
      IFEND;
      pte_p := ^mmv$pt_p^ [pti];

{ Find the ASIDQ table entry for the segment. Usually page table entries for the same segment
{ are clustered together - skip the ASIDQ search if new entry is the same as the previous
{ entry. If the entry is not found, the ASID must belong to a segment that is accessible in
{ monitor mode ONLY.

      IF asid <> last_asid THEN
        j := 0;

        WHILE (j < next_asidq_index) AND (asidq [j].asid <> asid) DO
          j := j + 1;
        WHILEND;

        IF j = next_asidq_index THEN
          asidq_p := ^asidq [j];
          mmp$asti (asid, asti);
          IF asti <= UPPERBOUND (mmv$ast_p^) THEN
            asidq_p^.aste_p := ^mmv$ast_p^ [asti];
            IF NOT asidq_p^.aste_p^.in_use THEN
              assign_fde (gfc$tr_system, asti, 0, asidq_p^.aste_p^.sfid, asidq_p^.fde_p);
              asidq_p^.fde_p^.file_kind := gfc$fk_monitor_only_unnamed;
              asidq_p^.aste_p^.in_use := TRUE;
              asidq_p^.aste_p^.queue_id := mmc$pq_wired;
              asidq_p^.aste_p^.ijl_ordinal := jmv$system_ijl_ordinal;
              IF asid <> 0ffff(16) THEN
                asidq_p^.aste_p^.include_pages_in_dump := TRUE;
              IFEND;
              mmv$number_free_astes := mmv$number_free_astes - 1;
            IFEND;
          ELSE
            asidq_p^.aste_p := NIL;
            asidq_p^.fde_p := NIL;
          IFEND;
          asidq_p^.asid := asid;
          asidq_p^.qcb_p := ^mmv$gpql [mmc$pq_wired].pqle;
          asidq_p^.queue_id := mmc$pq_wired;
          next_asidq_index := next_asidq_index + 1;
        IFEND;

        asidq_p := ^asidq [j];
        aste_p := asidq_p^.aste_p;
        queue_id := asidq_p^.queue_id;
        qcb_p := asidq_p^.qcb_p;
        last_asid := asid;
        fde_p := asidq_p^.fde_p;
      IFEND;

      sva.asid := asid;
      sva.offset := pte_p^.pageid.pagenum * 512;
      #HASH_SVA (sva, ipti, count, found);
      FOR i := 2 TO count DO
        ipti := ipti - 1;
        IF ipti < 0 THEN
          ipti := mmv$pt_length - 1;
        IFEND;
        mmv$continue_bit_count_p^ [ipti] := mmv$continue_bit_count_p^ [ipti] + 1;
      FOREND;

      IF aste_p <> NIL THEN
        aste_p^.pages_in_memory := aste_p^.pages_in_memory + 1;
      IFEND;

      pfti := (pte_p^.rma * 512) DIV osv$page_size;

      IF (pfti < pftimin) OR (pfti > pftimax) OR (pfti = pftimin) AND (asid = 0FFFF(16)) THEN
        IF (pfti >= pftimin) AND (pfti <= UPPERBOUND (mmv$pages_to_dump_p^)) THEN
          IF asid <> 0FFFF(16) THEN     { NOT NOS }
            mmv$pages_to_dump_p^ [pfti] := TRUE;
          IFEND;
        IFEND;
        CYCLE /scan_page_table/
      IFEND;

      pft_p := ^mmv$pft_p^ [pfti];
      pft_p^.pti := pti;
      pft_p^.sva := sva;
      pft_p^.queue_id := queue_id;
      pft_p^.aste_p := aste_p;
      pft_p^.age := 1;
      pft_p^.ijl_ordinal := jmv$system_ijl_ordinal;
      pft_p^.locked_page := mmc$lp_not_locked;
      pft_p^.link.fwd := qcb_p^.link.fwd;
      IF (fde_p <> NIL) AND (fde_p^.eoi_byte_address < sva.offset + osv$page_size) THEN
        fde_p^.eoi_byte_address := sva.offset + osv$page_size;
      IFEND;
      IF pft_p^.aste_p <> NIL THEN
        link_page_to_segment_ds (pfti, pft_p, aste_p);
        mmv$pages_to_dump_p^ [pfti] := aste_p^.include_pages_in_dump
      ELSE
        mmv$pages_to_dump_p^ [pfti] := TRUE;
      IFEND;
      fwd_link := qcb_p^.link.fwd;
      IF fwd_link <> 0 THEN
        mmv$pft_p^ [fwd_link].link.bkw := pfti;
      IFEND;
      qcb_p^.link.fwd := pfti;
      qcb_p^.count := qcb_p^.count + 1;
      IF qcb_p^.link.bkw = 0 THEN
        qcb_p^.link.bkw := pfti;
      IFEND;

    FOREND /scan_page_table/;

{ Mark any flawed pages.  Pages that are already in use will be moved to the flaw queue
{ when they are linked to the free or avail queue.  Link the flawed pages into the flaw
{ queue.  Pages are only flawed on the CY2000 machines.

    IF flaw_map_p <> NIL THEN

    /flaw_pages/
      FOR i := LOWERBOUND (flaw_map_p^) TO UPPERBOUND (flaw_map_p^) DO
        IF flaw_map_p^ [i] = 0 THEN
          EXIT /flaw_pages/;
        ELSEIF (flaw_map_p^ [i] < LOWERBOUND (mmv$pft_p^)) OR
              (flaw_map_p^ [i] > UPPERBOUND (mmv$pft_p^)) THEN
          CYCLE /flaw_pages/;
        IFEND;
        pft_p := ^mmv$pft_p^ [flaw_map_p^ [i]];
        pft_p^.flawed := TRUE;
      FOREND /flaw_pages/;

      FREE flaw_map_p IN osv$mainframe_wired_heap^;
    IFEND;


    first_image_pfti := osv$180_memory_limits.deadstart_upper DIV osv$page_size;
    qcb_p := ^mmv$gpql [mmc$pq_free].pqle;
    last_link_p := ^qcb_p^.link;

  /scan_pft/
    FOR pfti := pftimin TO pftimax DO
      IF pfti >= first_image_pfti THEN
        IF mmv$pft_p^ [pfti].age <> 0 THEN
          osp$fatal_system_error (' Not enough memory to deadstart- PFT INIT', NIL);
        IFEND;

{ These pages will be linked to the free queue when memory is committed.

        mmv$pft_p^ [pfti].link.fwd := 0;
        mmv$pft_p^ [pfti].link.bkw := 0;
        mmv$pft_p^ [pfti].queue_id := mmc$pq_free;

      ELSEIF mmv$pft_p^ [pfti].age <> 0 THEN
        CYCLE /scan_pft/

      ELSEIF mmv$pft_p^ [pfti].flawed THEN
        mmv$pft_p^ [pfti].link.fwd := mmv$gpql [mmc$pq_flawed].pqle.link.fwd;
        mmv$pft_p^ [pfti].link.bkw := 0;
        mmv$pft_p^ [pfti].queue_id := mmc$pq_flawed;
        IF mmv$gpql [mmc$pq_flawed].pqle.link.fwd = 0 THEN
          mmv$gpql [mmc$pq_flawed].pqle.link.bkw := pfti;
        ELSE
          mmv$pft_p^ [mmv$gpql [mmc$pq_flawed].pqle.link.fwd].link.bkw := pfti;
        IFEND;
        mmv$gpql [mmc$pq_flawed].pqle.link.fwd := pfti;
        mmv$gpql [mmc$pq_flawed].pqle.count := mmv$gpql [mmc$pq_flawed].pqle.count + 1;

      ELSE
        last_link_p^.bkw := pfti;
        last_link_p := ^mmv$pft_p^ [pfti].link;
        last_link_p^.fwd := qcb_p^.link.fwd;
        qcb_p^.link.fwd := pfti;
        qcb_p^.count := qcb_p^.count + 1;
        mmv$pft_p^ [pfti].queue_id := mmc$pq_free;

      IFEND;
      mmv$pft_p^ [pfti].aste_p := NIL;
      mmv$pft_p^ [pfti].segment_link.fwd := 0;
      mmv$pft_p^ [pfti].segment_link.bkw := 0;
    FOREND /scan_pft/;

{ Set the count of the number of reassignable page frames.

    mmv$reassignable_page_frames.now := qcb_p^.count;

{ Store ASID in all unused AST entries.

    FOR i := 1 TO mtv$mx_segments DO
      IF NOT mmv$ast_p^ [i].in_use THEN
        mmp$asid (i, mmv$ast_p^ [i].asid);
      IFEND;
    FOREND;

    FOR pti := 0 TO mmv$pt_length - 1 DO
      mmv$pt_p^ [pti].c := mmv$continue_bit_count_p^ [pti] > 0;
    FOREND;

    mmv$time_to_call_mem_mgr := #FREE_RUNNING_CLOCK (0) + 5000000;

    jsv$swapped_page_entry_size := #SIZE (jst$swapped_page_descriptor);

{ The number 16384 is arbitrary and only must be less than or equal to the
{ minimum allocation unit size.

    mmv$pages_per_new_page_fault := 16384 DIV osv$page_size;
    IF mmv$pages_per_new_page_fault > 4 THEN
      mmv$pages_per_new_page_fault := 4;
    IFEND;

    mmv$tables_initialized := TRUE;

{ Set the bit in the critical page map so that physical page zero will
{ get dumped.  This is where the boot control tables for cy2000 lives
{ and where sometimes hardware erroneous deposits an exchange package.
{ Do this only if standalone! In dual state, NOS has access to physical
{ page zero.  *RUN can't dump any pages that are referenced by NOS.

    IF osv$170_os_type = osc$ot7_none THEN
      mmv$pages_to_dump_p^ [0] := TRUE;
    IFEND;

{ Set up pointer to flag SCI that a critical page dump is now available.

    rb_sds.reqcode := syc$rc_system_deadstart_status;
    rb_sds.action := dsc$rb_sds_set_cpt_pointer;
    rb_sds.data_p := NIL;
    i#call_monitor (#LOC (rb_sds), #SIZE (rb_sds));

{ The following code is to support the hyperchannel project.

    IF osv$enable_hyperchannel THEN
      sdt_entry := sdte;
      sdt_entry.ste.r1 := 6;
      sdt_entry.ste.r2 := 6;
      sdt_entry.ste.asid := 0;
      sdt_entry.ste.vl := osc$vl_cache_bypass;
      sdtx_entry := mmv$default_sdtx_entry;
      sdtx_entry.software_attribute_set := sdtx_entry.software_attribute_set +
            $mmt$software_attribute_set [mmc$sa_wired];
      sdtx_entry.open_validating_ring_number := 0;
      sdtx_entry.inheritance := mmc$si_share_segment;
      assign_fde (gfc$tr_system, 0, osc$segment_for_hyperchannel, sdtx_entry.sfid, fde_p);
      fde_p^.file_limit := 989680(16);
      fde_p^.last_segment_number := osc$segment_for_hyperchannel;
      sdt_p^.st [osc$segment_for_hyperchannel] := sdt_entry;
      sdtx_p^.sdtx_table [osc$segment_for_hyperchannel] := sdtx_entry;

{ Force an  ASID to be assigned so aste will contain jmv$system_ijl_ordinal.
       dummy_p := #ADDRESS (1, osc$segment_for_hyperchannel, 10);
       dummy_reference := dummy_p^;
    IFEND;

  PROCEND mmp$pft_initialize;

?? TITLE := 'assign_fde', EJECT ??
{ Purpose:
{   This procedure is called from mmp$pft_initialize to assign and initialize an FDE
{   entry for a segment.

  PROCEDURE assign_fde
    (    residence: gft$table_residence;
         asti: mmt$ast_index;
         segnum: ost$segment;
     VAR sfid: gft$system_file_identifier;
     VAR fde_p: gft$file_desc_entry_p);

    gfp$assign_fde (residence, 0, sfid, fde_p);
    IF sfid.residence <> gfc$tr_system THEN
      fde_p^.file_kind := gfc$fk_unnamed_file;
      fde_p^.open_count := 1;
      fde_p^.attach_count := 1;
    ELSE
      fde_p^.flags.global_template_file := TRUE;
      fde_p^.queue_status := gfc$qs_global_shared;
      fde_p^.attach_count := 0fff(16);
      fde_p^.open_count := 0fff(16);
    IFEND;
    fde_p^.asti := asti;
    fde_p^.last_segment_number := segnum;
    fde_p^.global_task_id := jmv$jmtr_xcb.global_task_id;
    fde_p^.global_task_id := jmv$jmtr_xcb.global_task_id;
    fde_p^.file_hash := segnum;
    sfid.file_hash := segnum;

  PROCEND assign_fde;
?? TITLE := 'mmp$create_ssr_sdtx', EJECT ??
{ Purpose:
{   This procedure is called during deadstart to create a SDTX entry for the SSR.
{

  PROCEDURE [XDCL] mmp$create_ssr_sdtx
    (VAR sdt_entry: mmt$segment_descriptor;
     VAR sdtx_entry: mmt$segment_descriptor_extended);

    VAR
      fde_p: gft$file_desc_entry_p,
      sfid: gft$system_file_identifier;

    sdtx_entry := mmv$default_sdtx_entry;
    sdtx_entry.software_attribute_set := $mmt$software_attribute_set [mmc$sa_wired];
    sdtx_entry.inheritance := mmc$si_none;

    gfp$assign_fde (gfc$tr_system, 0, sfid, fde_p);
    fde_p^.file_kind := gfc$fk_monitor_only_unnamed;
    fde_p^.queue_status := gfc$qs_global_shared;
    fde_p^.attach_count := 0fff(16);
    fde_p^.open_count := 0fff(16);

    mmp$asti (sdt_entry.ste.asid, fde_p^.asti);
    sdt_entry.asti := fde_p^.asti;
    fde_p^.global_task_id := jmv$jmtr_xcb.global_task_id;
    fde_p^.global_task_id := jmv$jmtr_xcb.global_task_id;
    fde_p^.file_hash := 0;

  PROCEND mmp$create_ssr_sdtx;

?? TITLE := 'link_page_to_segment_ds', EJECT ??
{ Purpose:
{   This procedure is called from mmp$pft_initialize to insert a page frame into the
{   thread which links all pages of a segment that are in memory.  There must be NO OTHER CALLERS
{   of this procedure, or the integrity of the links will be destroyed.

  PROCEDURE [INLINE] link_page_to_segment_ds
    (    pfti: mmt$page_frame_index;
         pfte_p: ^mmt$page_frame_table_entry;
         aste_p: ^mmt$active_segment_table_entry);

{ Debug code

    IF (pfte_p^.segment_link.fwd <> 0) AND (pfte_p^.segment_link.bkw <> 0) THEN
      osp$system_error ('LINK PAGE TO SEGMENT ERROR.', NIL);
    IFEND;

{ End debug code

    IF aste_p^.pft_link.fwd = 0 THEN
      aste_p^.pft_link.fwd := pfti;
      aste_p^.pft_link.bkw := pfti;
    ELSE
      mmv$pft_p^ [aste_p^.pft_link.bkw].segment_link.fwd := pfti;
      pfte_p^.segment_link.bkw := aste_p^.pft_link.bkw;
      aste_p^.pft_link.bkw := pfti;
    IFEND;

  PROCEND link_page_to_segment_ds;

?? TITLE := 'mmp$sdtx_initialization', EJECT ??

  PROCEDURE [XDCL] mmp$sdtx_initialization;

*copyc mmh$sdtx_initialization

    VAR
      sdt_p: mmt$max_sdt_p,
      sdtx_entry: mmt$segment_descriptor_extended,
      sdtx_p: mmt$max_sdtx_p,
      xcb_p: ^ost$execution_control_block,
      segnum: ost$segment,
      tos_array_index: ost$ring,
      rb: mmt$rb_ring1_segment_request;


{  Set pointer to SDTX in XCB.

    xcb_p := ^jmv$jmtr_xcb;
    xcb_p^.sdt_offset := #OFFSET (^jmv$sdt);
    xcb_p^.sdtx_offset := #OFFSET (^jmv$sdtx);

    mmp$get_max_sdt_sdtx_pointer (xcb_p, sdt_p, sdtx_p);


{ Initialize the SDTX entry for each valid SDT entry. By default each segment is assumed
{ to be pageable shared system template segments. Exceptions to this rule must be accounted for
{ by specifically changing attributes later in this proc.

    sdtx_entry := mmv$default_sdtx_entry;
    sdtx_entry.inheritance := mmc$si_share_segment;
    sdtx_entry.open_validating_ring_number := 0;

    FOR segnum := xcb_p^.xp.segment_table_length DOWNTO 0 DO
      IF sdt_p^.st [segnum].ste.vl <> osc$vl_invalid_entry THEN
        sdtx_p^.sdtx_table [segnum] := sdtx_entry;
        mmp$set_segment_access_rights (sdt_p^.st [segnum], sdtx_p^.sdtx_table [segnum]);
      IFEND;
    FOREND;

{ Set the page table to be read only.

    sdtx_p^.sdtx_table [osc$segnum_page_table].access_rights := mmc$sar_read;


{ Set software attributes for special segments.

    sdtx_p^.sdtx_table [osc$segnum_page_table].software_attribute_set :=
          $mmt$software_attribute_set [mmc$sa_wired];
    sdtx_p^.sdtx_table [#SEGMENT (#LOC (osv$mainframe_wired_heap^))].software_attribute_set :=
          $mmt$software_attribute_set [mmc$sa_wired];
    sdtx_p^.sdtx_table [#SEGMENT (#LOC (osv$mainframe_wired_cb_heap^))].
          software_attribute_set := $mmt$software_attribute_set [mmc$sa_wired];
    sdtx_p^.sdtx_table [#SEGMENT (#LOC (nav$network_wired_heap^))].software_attribute_set :=
          $mmt$software_attribute_set [mmc$sa_wired];
    sdtx_p^.sdtx_table [osc$segnum_job_fixed_heap].software_attribute_set :=
          $mmt$software_attribute_set [mmc$sa_fixed];

    sdtx_p^.sdtx_table [osc$segnum_job_fixed_heap].open_validating_ring_number := 1;


{ The file server wired heap is shared with the network wired heap.
{ The file server is the ending portion of this heap.

    dfv$server_wired_heap := #ADDRESS (#RING (nav$network_wired_heap), #SEGMENT (nav$network_wired_heap),
          nac$network_heap_size);
    osp$reset_heap (dfv$server_wired_heap, 3fffffff(16) - nac$network_heap_size, TRUE, 2);
    osp$reset_heap (nav$network_wired_heap, nac$network_heap_size, TRUE, nac$heap_algorithm);
    osp$reset_heap_ext (nav$network_paged_heap, nac$network_heap_size, TRUE, nac$heap_algorithm,
          osv$np_heap_min_frag_alloc_size, osv$np_heap_min_frag_alloc_size, osv$np_heap_min_frag_alloc_size);


{  For ring 1, 2, and 3 stack segments, set software attributes.

    FOR tos_array_index := 1 TO 3 DO
      segnum := xcb_p^.xp.tos_registers [tos_array_index].pva.seg;
      IF sdt_p^.st [segnum].ste.vl <> osc$vl_invalid_entry THEN
        sdtx_p^.sdtx_table [segnum].software_attribute_set := $mmt$software_attribute_set [mmc$sa_stack];
        sdtx_p^.sdtx_table [segnum].open_validating_ring_number := 1;
        sdtx_p^.sdtx_table [segnum].inheritance := mmc$si_new_segment;
      IFEND;
    FOREND;

  PROCEND mmp$sdtx_initialization;

?? TITLE := 'MMP$COMMIT_MEMORY' ??
?? EJECT ??

  PROCEDURE [XDCL] mmp$commit_memory;

    VAR
      rb: mmt$rb_ring1_segment_request;

*copyc mmh$commit_memory

    rb.reqcode := syc$rc_ring1_segment_request;
    rb.request := mmc$sr1_commit_memory;

    mmp$issue_ring1_segment_request (rb);

  PROCEND mmp$commit_memory;
?? TITLE := 'MMP$FREE_IMAGE_PAGES' ??
?? EJECT ??

  PROCEDURE [XDCL] mmp$free_image_pages;

    VAR
      rb: mmt$rb_ring1_segment_request;

*copyc mmh$free_image_pages

    rb.reqcode := syc$rc_ring1_segment_request;
    rb.request := mmc$sr1_free_image_pages;

    mmp$issue_ring1_segment_request (rb);

  PROCEND mmp$free_image_pages;
?? TITLE := 'MMP$DEFINE_IMAGE_FILE' ??
?? EJECT ??

  PROCEDURE [XDCL] mmp$define_image_file
    (    sfid: gft$system_file_identifier;
         length: 0 .. 0ffffffff(16));

*copyc mmh$define_image_file

    mmv$image_file.active := TRUE;
    mmv$image_file.sfid := sfid;
    mmv$image_file.file_offset := length;

  PROCEND mmp$define_image_file;
MODEND mmm$deadstart_initialization;
