?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Memory Manager : Boot' ??
MODULE mmm$boot;

{ PURPOSE:
{   This module contains the boot procedures for memory manager.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc mmc$first_transient_segment
*copyc mme$condition_codes
*copyc mmt$page_frame_table
*copyc mmt$segment_descriptor_table
*copyc mmt$segment_descriptor_table_ex
*copyc pmt$task_id
*copyc osc$purge_map_and_cache
*copyc osd$cybil_structure_definitions
*copyc ost$hardware_subranges
?? POP ??
*copyc dsp$get_data_from_ssr
*copyc mmp$get_max_sdt_pointer
*copyc mmp$get_max_sdt_sdtx_pointer
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$find_executing_task_xcb
*copyc syp$display_deadstart_message
?? EJECT ??
*copyc jmv$jmtr_xcb
*copyc jmv$sdt
*copyc jmv$sdtx
*copyc mmv$free_pages
*copyc mmv$next_free_page
*copyc mmv$pt_length
*copyc mmv$pt_p
*copyc osv$180_memory_limits
*copyc osv$mainframe_wired_heap
*copyc osv$page_size
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??
  VAR
    mmv$default_sdtx_entry: [XDCL] mmt$segment_descriptor_extended,
    mmv$first_transient_seg_index: [XDCL] ost$segment := mmc$first_transient_segment,
    mmv$pft_p: [XDCL, #GATE] ^mmt$page_frame_table := NIL;
?? TITLE := 'mmp$boot_add_sdt_sdtx_entry', EJECT ??

  PROCEDURE [XDCL, #GATE] mmp$boot_add_sdt_sdtx_entry
    (    xsdt_entry: mmt$segment_descriptor;
         xsdtx_entry: mmt$segment_descriptor_extended;
     VAR segment_number {input, output} : ost$segment);

    VAR
      buffer_p: ^cell,
      sdt_entry: mmt$segment_descriptor,
      sdt_p: mmt$max_sdt_p,
      segment_table_length: integer,
      temp_segment_number: ost$segment,
      xcb_p: ^ost$execution_control_block;

    sdt_entry := xsdt_entry;

    pmp$find_executing_task_xcb (xcb_p);
    temp_segment_number := segment_number;

    { Find an available segment number if the caller did not supply one.

    mmp$get_max_sdt_pointer (xcb_p, sdt_p);
    IF segment_number = 0 THEN
      temp_segment_number := mmv$first_transient_seg_index - 1;
      segment_table_length := xcb_p^.xp.segment_table_length;
      REPEAT
        temp_segment_number := temp_segment_number + 1;
        IF temp_segment_number > segment_table_length THEN
          osp$system_error ('Segment table too small', NIL);
        IFEND;
      UNTIL sdt_p^.st [temp_segment_number].ste.vl = osc$vl_invalid_entry;
    IFEND;

    { NOTE:  SINGLE PROCESSOR - CACHE BYPASS IS NOT A PROBLEM.

    sdt_p^.st [temp_segment_number] := sdt_entry;
    buffer_p := NIL;
    #PURGE_BUFFER (osc$purge_all_page_seg_map, buffer_p);
    segment_number := temp_segment_number;

  PROCEND mmp$boot_add_sdt_sdtx_entry;
?? TITLE := 'mmp$create_sdtx_entry', EJECT ??

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

  PROCEND mmp$create_ssr_sdtx;

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

  PROCEDURE [XDCL] mmp$fetch_boot_memory_bounds
    (VAR first_byte_address: integer;
     VAR length: integer);

    VAR
      memory_bounds: dst$ssr_boot_memory_bounds,
      memory_bounds_seq_p: ^SEQ ( * );

    memory_bounds_seq_p := #SEQ (memory_bounds);
    dsp$get_data_from_ssr (dsc$ssr_boot_memory_bounds, memory_bounds_seq_p);

    first_byte_address := (memory_bounds.start_address.r_upper * 10000(8) +
          memory_bounds.start_address.r_lower) * 1000(8);

    length := (memory_bounds.length.r_upper * 10000(8) + memory_bounds.length.r_lower) * 1000(8);

  PROCEND mmp$fetch_boot_memory_bounds;
?? TITLE := 'mmp$initialize', EJECT ??

  PROCEDURE [XDCL] mmp$initialize;

    VAR
      ptl_register: integer,
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      xcb_p: ^ost$execution_control_block;

    { Set up static data constants.

    ptl_register := #READ_REGISTER (osc$pr_page_table_length);
    mmv$pt_p := #ADDRESS (1, 0, 0);
    mmv$pt_length := (ptl_register + 1) * 512;
    osv$page_size := 512 * (128 - #read_register (osc$pr_page_size_mask));

    osv$180_memory_limits.lower := ((osv$180_memory_limits.lower + osv$page_size - 1) DIV osv$page_size)
          * osv$page_size;
    osv$180_memory_limits.upper := (osv$180_memory_limits.upper DIV osv$page_size) * osv$page_size;

{ Set offsets of the sdt and sdtx in the XCB.

    xcb_p := ^jmv$jmtr_xcb;

    sdt_p := #LOC (jmv$sdt);
    xcb_p^.sdt_offset := #OFFSET (sdt_p);

    sdtx_p := #LOC (jmv$sdtx);
    xcb_p^.sdtx_offset := #OFFSET (sdtx_p);

  PROCEND mmp$initialize;
?? TITLE := 'mmp$initialize_boot_pages', EJECT ??

  PROCEDURE [XDCL] mmp$initialize_boot_pages
    (VAR lower_bound: integer;
     VAR upper_bound: integer);

    VAR
      first_byte_address: integer,
      index: integer,
      l: integer,
      length: integer,
      lower_memory_limits: integer,
      msg: string (80),
      page_frame_list_p: ^array [ * ] of 0 .. osc$max_page_frames,
      pft_rma: integer,
      upper_memory_limits: integer,
      touch_pages: 1 .. 2;

    syp$display_deadstart_message ('Initializing Boot Pages ...');
    mmp$fetch_boot_memory_bounds (first_byte_address, length);

    lower_memory_limits := osv$180_memory_limits.lower DIV osv$page_size;
    upper_memory_limits := osv$180_memory_limits.upper DIV osv$page_size;
    ALLOCATE page_frame_list_p: [lower_memory_limits .. upper_memory_limits] IN osv$mainframe_wired_heap^;
    first_byte_address := #READ_REGISTER (osc$pr_page_table_address) + (mmv$pt_length * 8);
    lower_bound := first_byte_address DIV osv$page_size;
    IF length = 0 THEN
      upper_bound := upper_memory_limits;
    ELSE
      upper_bound := (first_byte_address + length) DIV osv$page_size;
    IFEND;

    STRINGREP (msg, l, '  Initializing Page Frame List, Lower= ', lower_memory_limits, ' Upper= ',
          upper_memory_limits, '.');
    syp$display_deadstart_message (msg (1, l));
    FOR index := lower_memory_limits TO upper_memory_limits DO
      page_frame_list_p^ [index] := 0;
    FOREND;

    { The following code is executed twice - once to flush out any page faults generated by the code
    { itself, and a second time to actually initialize the page list.

    syp$display_deadstart_message ('  Initializing Page Table.');
    FOR touch_pages := 1 TO 2 DO

      FOR index := lower_bound TO upper_bound - 1 DO
        page_frame_list_p^ [index] := 1;
      FOREND;

      { Remove in use pages.

      FOR index := 0 TO (mmv$pt_length - 1) DO
        IF mmv$pt_p^ [index].pageid.asid <> 0 THEN
          pft_rma := mmv$pt_p^ [index].rma * 512 DIV osv$page_size;
          IF (pft_rma >= lower_memory_limits) AND (pft_rma <= upper_memory_limits) AND
                (page_frame_list_p^ [pft_rma] <> 0) THEN
            page_frame_list_p^ [pft_rma] := 0;
          IFEND;
        IFEND;
      FOREND;

      { Link free pages together.

      pft_rma := 0;
      FOR index := upper_bound - 1 DOWNTO lower_bound DO
        IF page_frame_list_p^ [index] <> 0 THEN
          page_frame_list_p^ [index] := pft_rma;
          pft_rma := index;
        IFEND;
      FOREND;

    FOREND;

    mmv$next_free_page := pft_rma;
    mmv$free_pages := page_frame_list_p;

  PROCEND mmp$initialize_boot_pages;
?? TITLE := 'mmp$validate_segment_number', EJECT ??

  PROCEDURE [XDCL, #GATE] mmp$validate_segment_number
    (    segment_number: ost$segment;
     VAR sdt_entry_p: ^mmt$segment_descriptor;
     VAR sdtx_entry_p: ^mmt$segment_descriptor_extended;
     VAR status: ost$status);

    VAR
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    pmp$find_executing_task_xcb (xcb_p);

    IF segment_number > xcb_p^.xp.segment_table_length THEN
      osp$set_status_abnormal ('MM', mme$segment_number_too_big, '', status);
      RETURN;
    IFEND;

    mmp$get_max_sdt_sdtx_pointer (xcb_p, sdt_p, sdtx_p);
    sdt_entry_p := ^sdt_p^.st [segment_number];
    sdtx_entry_p := ^sdtx_p^.sdtx_table [segment_number];
    IF sdt_entry_p^.ste.vl = osc$vl_invalid_entry THEN
      osp$set_status_abnormal ('MM', mme$segment_number_not_in_use, '', status);
      RETURN;
    IFEND;

  PROCEND mmp$validate_segment_number;
MODEND mmm$boot;
