?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Operating System : Boot' ??
MODULE osm$boot;

{ PURPOSE:
{   This module contains the starting boot procedure.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dmt$allocation_size
*copyc dst$rb_logging_request
*copyc dst$rb_system_deadstart_status
*copyc iot$io_request
*copyc jmc$special_dispatch_priorities
*copyc mmt$segment_descriptor_table
*copyc mmt$segment_descriptor_table_ex
*copyc osc$processor_defined_registers
*copyc osc$purge_map_and_cache
*copyc oss$mainframe_pageable
*copyc ost$boot_update_page_table
*copyc ost$deadstart_phase
*copyc ost$processor_id_set
*copyc ost$recover_system_set_phase
*copyc rmt$recorded_vsn
*copyc stt$set_name
*copyc syt$perf_keypoints_enabled
?? POP ??
*copyc cmp$configure_deadstart_device
*copyc cmp$de_configure_ds_device
*copyc cmp$vcmb_menu_manager
*copyc cmp$write_os_status
*copyc dpp$configure_system_console
*copyc dsp$boot_deadstart_loader
*copyc dsp$build_mainframe_information
*copyc dsp$fetch_mau_list
*copyc dsp$get_entry_from_ssr
*copyc dsp$get_ssr_data_rma
*copyc dsp$initialize_sys_msg_buffer
*copyc dsp$load_additional_dft
*copyc dsp$make_ssr_segment
*copyc dsp$retrieve_device_address
*copyc dsp$save_boot_data_pointer
*copyc dsp$save_sys_status_current_ds
*copyc dsp$save_sys_status_ds_file
*copyc dsp$setup_170_request_interlock
*copyc dsp$setup_load_ppu_interlocks
*copyc dsp$store_entry_in_ssr
*copyc i#call_monitor
*copyc iop$mass_storage_io
*copyc mmp$fetch_boot_memory_bounds
*copyc mmp$get_max_sdt_pointer
*copyc mmp$get_sdt_entry_p
*copyc mmp$initialize
*copyc mmp$initialize_boot_pages
*copyc osp$initialize_date_time
*copyc osp$initialize_signature_lock
*copyc osp$reset_heap
*copyc osp$system_error
*copyc pmp$find_executing_task_xcb
*copyc syp$check_system_level
*copyc syp$determine_mainframe_type
*copyc syp$display_deadstart_message
*copyc syp$prepare_deadstart_display
*copyc syp$process_deadstart_status
*copyc syp$trace_deadstart_message
?? EJECT ??
*copyc cmv$system_device_data
*copyc dsv$system_deadstart_status_p
*copyc jmv$jcb
*copyc jmv$jmtr_xcb
*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
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    dmv$retain_system_device_flaws: [XDCL] boolean := TRUE,
    dmv$system_device_recorded_vsn: [XDCL, #GATE] rmt$recorded_vsn := 'VSN006',
    osv$deadstart_phase: [XDCL, #GATE] ost$deadstart_phase := osc$normal_deadstart,
    osv$default_pit: [XDCL] integer := 7fffffff(16), {default value for PIT}
    osv$job_fixed_heap: [XREF] ^ost$heap,
    osv$job_pageable_heap: [XDCL, #GATE, oss$mainframe_pageable] ^ost$heap,
    osv$mainframe_pageable_heap: [XDCL, #GATE, oss$mainframe_pageable] ^ost$heap,
    osv$mainframe_wired_cb_heap: [XDCL, #GATE, oss$mainframe_pageable] ^ost$heap,
    osv$mph_length: [XDCL] integer, {! * * * kludge until ost$heap is a heap}
    osv$recover_system_set_phase: [XDCL, #GATE] ost$recover_system_set_phase := osc$recovery_not_required,
    osv$spi_response_processor: [XDCL, STATIC, #GATE, oss$mainframe_pageable] iot$response_processor := NIL,
    osv$system_device_cylinder_size: [XDCL] integer := 0,
    osv$task_private_heap: [XDCL, #GATE, oss$mainframe_pageable] ^ost$heap,
    stv$system_set_name: [XDCL, #GATE] stt$set_name,
    syv$enable_heap_trace: [XDCL, #GATE] boolean := TRUE,
    syv$perf_keypoints_enabled: [XDCL, #GATE] syt$perf_keypoints_enabled :=
          [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE],
    syv$verify_heap_linkage: [XDCL, #GATE] boolean := FALSE;
?? OLDTITLE ??
?? NEWTITLE := 'jmp$job_monitor_xcb', EJECT ??

  FUNCTION [XDCL, #GATE] jmp$job_monitor_xcb: ^ost$execution_control_block;

    jmp$job_monitor_xcb := ^jmv$jmtr_xcb;

  FUNCEND jmp$job_monitor_xcb;
?? OLDTITLE ??
?? NEWTITLE := 'add_free_memory', EJECT ??

  PROCEDURE add_free_memory
    (VAR lower_memory_bound: integer;
     VAR upper_memory_bound: integer);

    VAR
      lower_memory_limits: integer,
      memory_limits_size: integer,
      message: string (80),
      message_length: integer,
      pft_rma: integer,
      temp_memory_limit: integer;

    lower_memory_limits := osv$180_memory_limits.lower DIV osv$page_size;
    memory_limits_size := osv$180_memory_limits.deadstart_upper DIV osv$page_size;
    IF (lower_memory_limits >= lower_memory_bound) AND (memory_limits_size <= upper_memory_bound) THEN
      RETURN;
    IFEND;

    STRINGREP (message, message_length, ' Adding memory: ', lower_memory_limits: #(16),
          ' - ', memory_limits_size: #(16));
    syp$trace_deadstart_message (message (1, message_length));

    { Find the current last free page.

    pft_rma := mmv$next_free_page;
    WHILE mmv$free_pages^ [pft_rma] <> 0 DO
      pft_rma := mmv$free_pages^ [pft_rma];
    WHILEND;

    IF upper_memory_bound < memory_limits_size THEN
      temp_memory_limit := upper_memory_bound;
      WHILE temp_memory_limit < memory_limits_size DO
        mmv$free_pages^ [pft_rma] := temp_memory_limit;
        pft_rma := temp_memory_limit;
        temp_memory_limit := temp_memory_limit + 1;
      WHILEND;
      upper_memory_bound := memory_limits_size;
    IFEND;

    IF lower_memory_limits < lower_memory_bound THEN
      temp_memory_limit := lower_memory_limits;
      WHILE temp_memory_limit < lower_memory_bound DO
        mmv$free_pages^ [pft_rma] := temp_memory_limit;
        pft_rma := temp_memory_limit;
        temp_memory_limit := temp_memory_limit + 1;
      WHILEND;
      lower_memory_bound := lower_memory_limits;
    IFEND;

  PROCEND add_free_memory;
?? OLDTITLE ??
?? NEWTITLE := 'write_image_file', EJECT ??

{ PURPOSE:
{   This procedure writes the memory image to the image file to recover the previously running system.  The
{   boot is always loaded right after the page table.  Before it is loaded SCI copies that memory to what is
{   referred to as the 'hole'.  The 'hole' is memory that is used by NOS/VE that is not needed for recovery,
{   the monitor segment and job fixed of the system job.  The image is written beginning at memory lower
{   bounds.  The image length is obtained from the SSR.  When copying memory to the image file the part of
{   memory where the boot is running is copied from the area of memory defined by memory bounds in the SSR,
{   thus the image file reflect memory before the boot was loaded except for memory that is not needed for
{   recovery.  A value in the SSR to determine if an image file should be written.  If it determines that one
{   should be written it writes the image file to the system device.  The majority of this procedure is
{   traversed regardless of whether an image file is to be written.  This is done so that the memory needed
{   to write the image file is accounted for during normal deadstarts so that a large enough space is
{   reserved to deadstart the boot during recovery deadstarts.
{
{ NOTE:
{   In the future it would be good to begin the image file write right after the page table.  If the image
{   length exceeds available memory start at lower bound of memory.  The current method could lead to a
{   problem when loading monitor segment and reserving memory for job fixed of system job which must be in
{   contiguous memory.  Large page tables move the load address of the boot up but may not increase the image
{   length.  It would also make it easier to understand.

  PROCEDURE write_image_file
    (VAR lower_memory_bound: integer;
     VAR upper_memory_bound: integer;
     VAR status: ost$status);

    CONST
      c$mega_bytes = 1024 * 1024;

    VAR
      bytes_per_mau: integer,
      completion_status_p: ^iot$completion_status,
      copy_lower_bound: integer,
      copy_upper_bound: integer,
      current_copy_address: integer,
      current_mau: dmt$mau_count,
      current_rma: integer,
      device_address: dmt$ms_logical_device_address,
      first_block_transfer_size: integer,
      first_partial_block_exists: boolean,
      image_length: dst$ssr_entry,
      image_offset: dst$ssr_entry,
      image_state: dst$ssr_entry,
      image_table_size: integer,
      index: integer,
      last_rma: integer,
      length: integer,
      mau_count: dmt$mau_count,
      mau_list_p: ^dmt$mau_address_list,
      memory_image_sva: ost$system_virtual_address,
      message: string(80),
      message_length: integer,
      only_perform_allocates: boolean,
      page_count: integer,
      pva_p: ^cell,
      rma_list_p: ^ARRAY [1 .. *] OF integer,
      rma_p: ^ARRAY [ * ] OF cell,
      ste_p: ^mmt$segment_descriptor,
      ssr_data_rma: integer,
      ssr_data_size: integer,
      sva_count: integer,
      sva_found: boolean,
      sva_index: integer,
      transfer_length: dmt$maus_per_transfer,
      transfer_size: integer,
      transfer_size_used: integer,
      update: ost$boot_update_page_table,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;

    { Set up the limits of where the boot is running, during the copy this memory is actually copied from
    { area defined by memory bounds in the SSR.  The boot is always loaded right after the page table.  The
    { page table is copied to the image file but it is not used.

    mmp$fetch_boot_memory_bounds (current_copy_address, length);
    copy_lower_bound := #READ_REGISTER (osc$pr_page_table_address) + (mmv$pt_length * 8);
    copy_upper_bound := copy_lower_bound + length;

    { Set the first byte address of memory bounds, this is where the memory is that the boot was loaded over.

    dsp$get_entry_from_ssr (dsc$ssr_image_length, image_length);
    last_rma := osv$180_memory_limits.lower + image_length.whole_slot;
    IF (last_rma > osv$180_memory_limits.upper) OR (image_length.whole_slot = 0) THEN
      osv$180_memory_limits.deadstart_upper := osv$180_memory_limits.upper;
    ELSE
      osv$180_memory_limits.deadstart_upper := last_rma;
    IFEND;

    { Configure the disk to write the image file.

    cmp$configure_deadstart_device (cmc$sdt_disk_device, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Determine the last RMA from the SSR length.

    current_rma := osv$180_memory_limits.lower;
    dsp$get_ssr_data_rma (dsc$ssr_total_length, ssr_data_rma, ssr_data_size);
    ssr_data_rma := ssr_data_rma + ssr_data_size + osv$page_size - 1;
    IF ssr_data_rma < last_rma THEN
      last_rma := ssr_data_rma DIV osv$page_size * osv$page_size;
    IFEND;

    dsp$get_entry_from_ssr (dsc$ssr_image_offset, image_offset);
    image_table_size := image_offset.whole_slot;
    dsp$get_entry_from_ssr (dsc$ssr_image_state, image_state);

    STRINGREP (message, message_length, '  Memory Limits: Upper= ', osv$180_memory_limits.upper DIV
          c$mega_bytes, ' MBytes, Deadstart: ', osv$180_memory_limits.deadstart_upper DIV c$mega_bytes,
          ' MBytes.');
    syp$display_deadstart_message (message (1, message_length));

    { Fetch the MAU list for the image file.

    only_perform_allocates := (image_state.whole_slot > 1);
    dsp$fetch_mau_list (only_perform_allocates, dmc$dlf_image_entry, mau_list_p, mau_count,
          transfer_size, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH completion_status_p;
    PUSH rma_list_p: [1 .. (osv$system_device_cylinder_size DIV osv$page_size) + 2];

    { Have monitor add entries to the page table for this memory.  Round up to page boundary

    ALLOCATE rma_p: [1 .. ((UPPERBOUND (rma_list_p^) + 2) * osv$page_size)] IN osv$mainframe_wired_heap^;
    pva_p := #ADDRESS (#RING (rma_p), #SEGMENT (rma_p),
          (((#OFFSET (rma_p) + osv$page_size - 1) DIV osv$page_size) * osv$page_size));
    update.reqcode := 11;
    update.pva := pva_p;
    update.length := UPPERBOUND (rma_list_p^) * osv$page_size;
    i#call_monitor (#LOC (update), #SIZE (update));
    #PURGE_BUFFER (osc$purge_all_page_seg_map, pva_p);

    dsp$save_sys_status_current_ds (dsc$ssr_sds_sdas_continuation);

    { Check the image state to see if the image file needs to be written.

    IF (image_state.whole_slot <= 1) AND (image_length.whole_slot <> 0) THEN
      syp$display_deadstart_message ('Writing the image file ...');
      dsp$retrieve_device_address (transfer_size, device_address);
      device_address.write_translation := TRUE;
      device_address.au_was_previously_written := TRUE;
      device_address.maus_per_allocation_unit := device_address.transfer_length;
      transfer_length := device_address.transfer_length;
      device_address.preset_value := 0;

      current_mau := (image_table_size DIV transfer_size) + 1;
      bytes_per_mau := transfer_size DIV device_address.transfer_length;
      first_block_transfer_size := transfer_size - (image_table_size MOD transfer_size);

      { The first image file block is special when written.  It starts in the middle of transfer unit due to
      { the image tables that precede it.

      first_partial_block_exists := (first_block_transfer_size < transfer_size);

      WHILE current_rma < last_rma DO
        IF first_partial_block_exists THEN
          page_count := first_block_transfer_size DIV osv$page_size;
          device_address.transfer_mau_offset := (image_table_size MOD transfer_size) DIV bytes_per_mau;
          device_address.transfer_length := first_block_transfer_size DIV bytes_per_mau;
          transfer_size_used := first_block_transfer_size;
        ELSE
          page_count := transfer_size DIV osv$page_size;
          device_address.transfer_mau_offset := 0;
          device_address.transfer_length := transfer_length;
          transfer_size_used := transfer_size;
        IFEND;
        device_address.allocation_unit_mau_address := mau_list_p^ [current_mau];

        FOR index := 1 TO page_count DO
          IF first_partial_block_exists OR (current_rma < last_rma) THEN
            IF (current_rma >= copy_upper_bound) OR (current_rma < copy_lower_bound) THEN
              rma_list_p^ [index] := current_rma;
            ELSE

              { Copying memory where the boot is running, use memory bounds instead.

              rma_list_p^ [index] := current_copy_address;
              current_copy_address := current_copy_address + osv$page_size;
            IFEND;
            current_rma := current_rma + osv$page_size;
          ELSE

            { Special case end of memory that is not on a transfer unit boundary, just write the last
            { memory page for those pages.

            rma_list_p^ [index] := current_rma - osv$page_size;
            transfer_size_used := transfer_size_used - osv$page_size;
          IFEND;
        FOREND;

        { Change the RMA's in the page table entries to point to the image file.

        pmp$find_executing_task_xcb (xcb_p);
        ste_p := mmp$get_sdt_entry_p (xcb_p, #SEGMENT (pva_p));
        memory_image_sva.asid := ste_p^.ste.asid;
        memory_image_sva.offset := #OFFSET (pva_p);
        FOR index := 1 TO page_count DO
          IF (rma_list_p^ [index] MOD osv$page_size) <> 0 THEN
            osp$system_error ('RMA not on page boundary', #LOC (rma_list_p^));
          IFEND;
          #HASH_SVA (memory_image_sva, sva_index, sva_count, sva_found);
          IF NOT sva_found THEN
            osp$system_error ('Convert rma - pte not found', NIL);
          IFEND;
          mmv$pt_p^ [sva_index].rma := rma_list_p^ [index] DIV 512;
          memory_image_sva.offset := memory_image_sva.offset + osv$page_size;
        FOREND;
        #PURGE_BUFFER (osc$purge_all_page_seg_map, pva_p);

        STRINGREP (message, message_length, ' Image: ', rma_list_p^ [1]: #(16),
              rma_list_p^ [page_count]: #(16), current_mau, mau_count, mau_list_p^ [current_mau],
              transfer_size_used);
        syp$trace_deadstart_message (message (1, message_length));
        iop$mass_storage_io (pva_p, transfer_size_used, ioc$write_mass_storage, device_address,
              TRUE, completion_status_p, status);
        IF NOT status.normal THEN
          osp$system_error ('Cannot write image file', ^status);
        IFEND;
        current_mau := current_mau + 1;
        first_partial_block_exists := FALSE;
      WHILEND;

      { Change the image state to reflect the fact that the image file has been written.

      osv$180_memory_limits.upper := last_rma;
      image_state.whole_slot := 2;
      dsp$store_entry_in_ssr (dsc$ssr_image_state, dsc$ssr_whole_slot, image_state);

      { Change the current deadstart type in the System Deadstart Status data to note that an image file
      { has been written.

      dsp$save_sys_status_current_ds (dsc$ssr_sds_sdas_with_image);

    IFEND;

    { Free the pages so that they are not counted in the needed memory.  The memory was never used as
    { the page table entries were changed to point elsewhere.

    pmp$find_executing_task_xcb (xcb_p);
    ste_p := mmp$get_sdt_entry_p (xcb_p, #SEGMENT (pva_p));
    memory_image_sva.asid := ste_p^.ste.asid;
    memory_image_sva.offset := #OFFSET (pva_p);
    FOR index := 1 TO UPPERBOUND (rma_list_p^) DO
      #HASH_SVA (memory_image_sva, sva_index, sva_count, sva_found);
      IF NOT sva_found THEN
        osp$system_error ('Convert rma - pte not found', NIL);
      IFEND;
      mmv$pt_p^ [sva_index].v := FALSE;
      mmv$pt_p^ [sva_index].pageid.asid := 0;
      mmv$pt_p^ [sva_index].pageid.pagenum := 0;
      mmv$pt_p^ [sva_index].rma := 0;
      memory_image_sva.offset := memory_image_sva.offset + osv$page_size;
    FOREND;
    #PURGE_BUFFER (osc$purge_all_page_seg_map, pva_p);
    FREE rma_p IN osv$mainframe_wired_heap^;

    cmp$de_configure_ds_device (status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    add_free_memory (lower_memory_bound, upper_memory_bound);

    FREE mau_list_p IN osv$mainframe_wired_heap^;

  PROCEND write_image_file;
?? OLDTITLE ??
?? NEWTITLE := 'osp$initialize', EJECT ??

{ PURPOSE:
{   This program is the starting procedure for the boot OS code.

  PROGRAM [XDCL] osp$initialize;

    VAR
      dft_rb: dst$rb_logging_request,
      lower_memory_bound: integer,
      rb: dst$rb_system_deadstart_status,
      sdt_p: mmt$max_sdt_p,
      status: ost$status,
      upper_memory_bound: integer,
      xcb_p: ^ost$execution_control_block;

    mmp$initialize;

    { Setup the job monitor exchange package.

    xcb_p := jmp$job_monitor_xcb ();
    mmp$get_max_sdt_pointer (xcb_p, sdt_p);
    sdt_p^.st [osc$segnum_job_fixed_heap] := sdt_p^.st [0a(16)];
    #PURGE_BUFFER (osc$purge_all_page_seg_map, xcb_p);
    xcb_p^.dispatching_priority := jmc$priority_system_job;
    xcb_p^.pit_count := 7fffffff(16);
    xcb_p^.iocb_p := NIL;
    xcb_p^.processor_selections := - $ost$processor_id_set [];
    xcb_p^.requested_processor_selections := $ost$processor_id_set [];
    xcb_p^.global_task_id.index := 1;
    xcb_p^.global_task_id.seqno := 1;
    xcb_p^.task_kind := osc$tk_nosve_task;

    jmv$jcb.cptime_next_age_working_set := 200000;
    jmv$jcb.max_working_set_size := 16000;
    jmv$jcb.signal_interval := 0ffffffff(16);

    { At this point memory manager is initialized to the point where a page fault can be
    { processed to extend existing segments (within 1mb).

    osp$reset_heap (osv$mainframe_wired_heap, 3fffffff(16), TRUE, 1);
    osv$mainframe_pageable_heap := osv$mainframe_wired_heap;
    osv$mainframe_wired_cb_heap := osv$mainframe_wired_heap;
    osv$job_pageable_heap := osv$mainframe_wired_heap;
    osv$job_fixed_heap := osv$mainframe_wired_heap;
    osv$task_private_heap := osv$mainframe_wired_heap;

    syp$determine_mainframe_type;

    { Set up the variables to the DFT block.

    dft_rb.reqcode := syc$rc_logging_request;
    dft_rb.action := dsc$rla_dft_setup_variables;
    i#call_monitor (#LOC (dft_rb), #SIZE (dft_rb));

    { Preparing the deadstart displays must be done very early to prepare the displays for output.
    { Anything done before this procedure will not be able to write to the screen.

    dpp$configure_system_console;
    syp$prepare_deadstart_display;
    dsp$make_ssr_segment;
    dsp$build_mainframe_information;

    { Initialize the system deadstart status in the SSR.

    ALLOCATE dsv$system_deadstart_status_p IN osv$mainframe_wired_heap^;
    dsp$save_boot_data_pointer (dsc$boot_system_ds_status, #SEQ (dsv$system_deadstart_status_p^));
    rb.reqcode := syc$rc_system_deadstart_status;
    rb.status.normal := TRUE;
    rb.action := dsc$rb_sds_initialize_data;
    rb.data_p := NIL;
    i#call_monitor (#LOC (rb), #SIZE (rb));

    { Initialize the signature lock variables used by the deadstart area.

    dsp$setup_load_ppu_interlocks;
    dsp$setup_170_request_interlock;

    { Initialize the buffer used to hold messages from monitor destined for the engineering log.

    dsp$initialize_sys_msg_buffer;

    osp$initialize_date_time (status);
    IF NOT status.normal THEN
      syp$process_deadstart_status ('Error:  Initialize date and time.', TRUE, status);
    IFEND;

    syp$display_deadstart_message ('Virtual CPU bootstrap initiated ...');

    dsp$load_additional_dft (status);
    IF NOT status.normal THEN
      syp$process_deadstart_status ('Unable to load secondary DFT.', TRUE, status);
    IFEND;

    { Chech the system level number in the SSR for compatability with system core.

    syp$check_system_level;

    { Initialize the boot pages.

    mmp$initialize_boot_pages (lower_memory_bound, upper_memory_bound);

    { Allow configuration of the deadstart and system device(s).

    cmp$vcmb_menu_manager;

    REPEAT
      write_image_file (lower_memory_bound, upper_memory_bound, status);
      IF NOT status.normal THEN
        cmp$write_os_status (' ', status);
        cmp$vcmb_menu_manager;
      IFEND;
    UNTIL status.normal;

    { Store the source of the deadstart file in the System Deadstart Status data.  If the tape is specified
    { then the source is the tape drive.

    IF cmv$system_device_data [cmc$sdt_tape_device].specified THEN
      dsp$save_sys_status_ds_file (dsc$ssr_sds_source_tape);
    ELSE
      dsp$save_sys_status_ds_file (dsc$ssr_sds_source_disk);
    IFEND;

    dsp$boot_deadstart_loader;

  PROCEND osp$initialize;
?? OLDTITLE ??
MODEND osm$boot;
