1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! The main initialization routine and setup logic of the OS. 
//!
//! The `captain` steers the ship of Theseus, meaning that it contains basic logic 
//! for initializing all of the other crates in the proper order and with
//! the proper flow of data between them.
//!
//! Currently, this is the default `captain` in Theseus, which does the following:
//!
//! * Initializes ACPI and APIC to discover multicore and other hardware configuration,
//! * Sets up interrupt and exception handlers,
//! * Sets up basic device drivers,
//! * Spawns event handling threads,
//! * Initializes the window manager and graphics subsystem,
//! * etc. 
//!
//! At the end, the `captain` enables interrupts to allow the system to schedule in other tasks.

#![no_std]

extern crate alloc;

use log::{error, info};
use memory::{EarlyIdentityMappedPages, MmiRef, PhysicalAddress};
use irq_safety::enable_interrupts;
use stack::Stack;
use no_drop::NoDrop;

#[cfg(target_arch = "x86_64")]
use {
    core::ops::DerefMut,
    kernel_config::memory::KERNEL_STACK_SIZE_IN_PAGES,
};

#[cfg(all(mirror_log_to_vga, target_arch = "x86_64"))]
mod mirror_log_callbacks {
    /// The callback for use in the logger crate to mirror log functions to the early VGA screen.
    pub(crate) fn mirror_to_early_vga(args: core::fmt::Arguments) {
        early_printer::println!("{}", args);
    }

    /// The callback for use in the logger crate to mirror log functions to the
    /// fully-featured terminal-based display.
    pub(crate) fn mirror_to_terminal(args: core::fmt::Arguments) {
        app_io::println!("{}", args);
    }
}

/// Items that must be held until the end of [`init()`] and should be dropped after.
pub struct DropAfterInit {
    pub identity_mappings: NoDrop<EarlyIdentityMappedPages>,
}
impl DropAfterInit {
    fn drop_all(self) {
        drop(self.identity_mappings.into_inner());
    }
}

pub use multicore_bringup::MulticoreBringupInfo;

/// Initialize the Captain, which is the main crate that "steers the ship" of Theseus. 
///
/// This does the rest of the initialization procedures so that the OS 
/// can continue running and do actual useful work.
///
/// # Arguments
/// * `kernel_mmi_ref`: a reference to the kernel's memory management info.
/// * `identity_mapped_pages`: the memory containing the identity-mapped content,
///    which must not be dropped until all APs are finished booting.
/// * `bsp_initial_stack`: the stack currently in use for running this code,
///    which must not be dropped for the entire execution of the initial bootstrap task.
/// * `multicore_info`: information needed to bring up secondary CPUs.
/// * `rsdp_address`: the physical address of the RSDP (an ACPI table pointer),
///    if available and provided by the bootloader.
#[cfg_attr(target_arch = "aarch64", allow(unreachable_code, unused_variables))]
pub fn init(
    kernel_mmi_ref: MmiRef,
    bsp_initial_stack: NoDrop<Stack>,
    drop_after_init: DropAfterInit,
    multicore_info: MulticoreBringupInfo,
    rsdp_address: Option<PhysicalAddress>,
) -> Result<(), &'static str> {
    #[cfg(all(mirror_log_to_vga, target_arch = "x86_64"))] {
        // Enable early mirroring of logger output to VGA buffer (for real hardware)
        logger::set_log_mirror_function(mirror_log_callbacks::mirror_to_early_vga);
    }

    // calculate TSC period and initialize it
    // not strictly necessary, but more accurate if we do it early on before interrupts, multicore, and multitasking
    #[cfg(target_arch = "x86_64")]
    if let Some(period) = tsc::get_tsc_period() {
        time::register_clock_source::<tsc::Tsc>(period);
    } else {
        log::warn!("Couldn't get TSC period");
    }

    // Initialize early devices, which currently only includes ACPI (x86-specific).
    #[cfg(target_arch = "x86_64")]
    device_manager::early_init(rsdp_address, kernel_mmi_ref.lock().deref_mut())?;

    // Initialize local and system-wide interrupt controllers.
    // TODO: move this into `interrupts::init()`.
    interrupt_controller::init(&kernel_mmi_ref)?;
    
    // Initialize other arch-specific interrupt stuff, e.g., basic interrupt handlers.
    // arch-gate: the IDT & special stacks are x86_64 specific
    #[cfg(target_arch = "x86_64")]
    let idt = {
        let (double_fault_stack, privilege_stack) = {
            let mut kernel_mmi = kernel_mmi_ref.lock();
            (
                stack::alloc_stack(KERNEL_STACK_SIZE_IN_PAGES, &mut kernel_mmi.page_table)
                    .ok_or("could not allocate double fault stack")?,
                stack::alloc_stack(1, &mut kernel_mmi.page_table)
                    .ok_or("could not allocate privilege stack")?,
            )
        };
        interrupts::init(double_fault_stack.top_unusable(), privilege_stack.top_unusable())?
    };

    #[cfg(target_arch = "aarch64")] {
        interrupts::init()?;
        irq_safety::enable_fast_interrupts();

        // register BSP CpuId
        cpu::register_cpu(true)?;
    }
    
    // get BSP's CPU ID
    let bsp_id = cpu::bootstrap_cpu().ok_or("captain::init(): couldn't get ID of bootstrap CPU!")?;
    cls_allocator::reload_current_cpu();

    // Initialize the scheduler and create the initial `Task`,
    // which is bootstrapped from this current execution context.
    scheduler::init()?;
    let bootstrap_task = spawn::init(kernel_mmi_ref.clone(), bsp_id, bsp_initial_stack)?;
    info!("Created initial bootstrap task: {:?}", bootstrap_task);

    // after we've initialized the task subsystem, we can use better exception handlers
    // arch-gate: aarch64 simply logs exceptions and crash; porting exceptions_full
    // hasn't been done yet
    #[cfg(target_arch = "x86_64")]
    exceptions_full::init(idt);
    
    // boot up the other cores (APs)
    let ap_count = multicore_bringup::handle_ap_cores(
        &kernel_mmi_ref,
        multicore_info,
    )?;

    let cpu_count = ap_count + 1;
    info!("Finished booting all {} AP cores; {} total CPUs are running.", ap_count, cpu_count);
    info!("Proceeding with system initialization, please wait...");

    // arch-gate: no framebuffer support on aarch64 at the moment
    #[cfg(all(mirror_log_to_vga, target_arch = "x86_64"))] {
        // Currently, handling the AP cores also siwtches the graphics mode
        // (from text mode VGA to a graphical framebuffer).
        // Thus, we can now use enable the function that mirrors logger output to the terminal.
        logger::set_log_mirror_function(mirror_log_callbacks::mirror_to_terminal);
    }

    // Now that other CPUs are fully booted, init TLB shootdowns,
    // which rely on Local APICs to broadcast an IPI to all running CPUs.
    tlb_shootdown::init();
    
    // Initialize the per-core heaps.
    // arch-gate: no multicore support on aarch64 at the moment
    #[cfg(target_arch = "x86_64")] {
        multiple_heaps::switch_to_multiple_heaps()?;
        info!("Initialized per-core heaps");
    }

    // Initialize the window manager, and also the PAT, if available.
    // The PAT supports write-combining caching of graphics video memory for better performance
    // and must be initialized explicitly on every CPU, 
    // but it is not a fatal error if it doesn't exist.
    #[cfg(target_arch = "x86_64")]
    if page_attribute_table::init().is_err() {
        error!("This CPU does not support the Page Attribute Table");
    }

    // arch-gate: no windowing/input support on aarch64 at the moment
    #[cfg(target_arch = "x86_64")]
    match window_manager::init() {
        Ok((key_producer, mouse_producer)) => {
            device_manager::init(key_producer, mouse_producer)?;
        },
        Err(error) => {
            error!("Failed to init window manager (expected if using nographic): {error}");
        }
    }

    #[cfg(target_arch = "aarch64")]
    device_manager::init()?;

    task_fs::init()?;

    // create a SIMD personality
    #[cfg(simd_personality)] {
        #[cfg(simd_personality_sse)]
        let simd_ext = task::SimdExt::SSE;
        #[cfg(simd_personality_avx)]
        let simd_ext = task::SimdExt::AVX;
        log::warn!("SIMD_PERSONALITY FEATURE ENABLED, creating a new personality with {:?}!", simd_ext);
        spawn::new_task_builder(simd_personality::setup_simd_personality, simd_ext)
            .name(alloc::format!("setup_simd_personality_{:?}", simd_ext))
            .spawn()?;
    }

    // Now that key subsystems are initialized, we can:
    // 1. Drop the items that needed to be held through initialization,
    drop_after_init.drop_all();

    // 2. Spawn various system tasks/daemons,
    console::start_connection_detection()?;

    // 3. Start the first application(s).
    first_application::start()?;

    info!("captain::init(): initialization done! Spawning an idle task on BSP core {} and enabling interrupts...", bsp_id);
    // The following final initialization steps are important, and order matters:
    // 1. Drop any other local stack variables that still exist.
    drop(kernel_mmi_ref);
    // 2. Cleanup bootstrap tasks, which handles this one and all other APs' bootstrap tasks.
    spawn::cleanup_bootstrap_tasks(cpu_count)?;
    // 3. "Finish" this bootstrap task, indicating it has exited and no longer needs to run.
    bootstrap_task.finish();
    // 4. Enable interrupts such that other tasks can be scheduled in.
    enable_interrupts();
    // ****************************************************
    // NOTE: nothing below here is guaranteed to run again!
    // ****************************************************

    scheduler::schedule();
    loop { 
        error!("BUG: captain::init(): captain's bootstrap task was rescheduled after being dead!");
    }
}