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
//! Rust version of the StuBS Kernel
//!
//! The assignments are documented in the [assignments] module.

#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points

// unstable features
#![feature(abi_x86_interrupt)]
#![feature(inline_const)]
#![feature(int_roundings)]
#![feature(lazy_cell)]
#![feature(let_chains)]
#![feature(pointer_is_aligned)]
#![feature(const_mut_refs)]
// unnecessary warnings
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::redundant_pattern_matching)]

use core::arch::global_asm;
use core::panic::PanicInfo;
use core::ptr::addr_of;

#[macro_use]
mod device;

#[macro_use]
mod arch;
mod gdt;
mod interrupts;
mod threading;
mod user;
mod util;

use device::cga::Window;
use device::{KEYBOARD, SERIAL};

#[cfg(feature = "smp")]
use crate::arch::smp;
use crate::arch::{acpi::Acpi, cpu, int};
use crate::device::{DBG, KOUT};
use crate::interrupts::guard::GUARD;
use crate::threading::Thread;

/// Maximal number of cores the OS supports.
#[cfg(feature = "smp")]
const MAX_CPUS: usize = 4;
#[cfg(not(feature = "smp"))]
const MAX_CPUS: usize = 1;

global_asm!(include_str!("arch/i686/start.s"), options(raw));

#[allow(unused)]
extern "C" {
    /// Signature of the multiboot header.
    static MBOOT_SIG: u32;
    /// Pointer to the multiboot header.
    static MBOOT_PTR: *mut ();
    /// Start address of the loaded kernel code.
    static KERNEL_BEGIN: u8;
    /// End address of the loaded kernel code.
    static KERNEL_END: u8;
    /// Function pointer to the start_high function.
    static start_high: u8;
}

/// The stack size for the init and thread stacks.
/// As rust uses the stack for printing, 4K usually isn't enough...
const STACK_SIZE: usize = 16 * 1024;

/// Stacks for each core, used for initialization (set before calling kmain).
///
/// It is also reused for the idle threads.
#[no_mangle]
pub static mut INIT_STACKS: [[u32; STACK_SIZE / 4]; MAX_CPUS] = [[0; STACK_SIZE / 4]; MAX_CPUS];

/// Main function that is called for every core
#[no_mangle]
pub fn kmain() -> ! {
    if cpu::online() == 0 {
        SERIAL.lock().init();
    }

    serial!("core id: {} ({:08b})", cpu::id(), cpu::online());

    // boot processor only
    if cpu::online() == 0 {
        serial!("code: {:x?}", unsafe {
            addr_of!(KERNEL_BEGIN)..addr_of!(KERNEL_END)
        });

        Window::whole().clear();
        DBG.lock().clear();
        KOUT.lock().clear();
        println!("Hello World!");

        // TODO: A1 - Init gdt and tss

        // Load system tables and setup interrupt controllers
        let acpi = Acpi::load().unwrap();
        serial!("global int");
        #[allow(unused_variables)]
        let cpus = interrupts::setup_global(acpi);

        let _old_cid = cpu::id() as u8;
        interrupts::setup_local();

        unsafe { KEYBOARD.init() };

        // Setup threads
        serial!("Start loading apps");

        GUARD.run(|g| {
            let sched = &mut g.scheduler;
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::app_action));
            sched.add(Thread::new(user::keyboard_action));
            sched.add(Thread::new(user::init_action));
        });

        serial!("Finish loading apps");

        #[cfg(feature = "smp")]
        if cpus > 1 {
            serial!("boot cores");
            smp::boot(&int::lapic::LAPIC, _old_cid);
            assert_eq!(cpus as u32, cpu::online().count_ones(), "we lost cores!");
        }
    } else {
        interrupts::setup_local();
    }

    // Enter L1/2, the scheduler leaves this layer
    let mut guarded = GUARD.enter();
    println!(dbg: "{}: int starting", cpu::id());
    int::enable(true);

    serial!("Scheduler starting...");
    guarded.scheduler.schedule();
}

/// This function is called on panic.
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    serial!(force: "{info}");
    println!(dbg: "{info}");
    loop {
        int::enable(false);
        cpu::halt();
    }
}

pub mod assignments {
    pub mod a1_syscalls {
        #![doc = include_str!("../assignments/1-Syscalls.md")]
    }
    pub mod a2_paging {
        #![doc = include_str!("../assignments/2-Paging.md")]
    }
    pub mod a3_ipc_cow {
        #![doc = include_str!("../assignments/3-IPC-COW.md")]
    }
}