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
//! [Writing an OS in Rust]
//!
//! [writing an os in rust]: https://os.phil-opp.com/
//!
//! # Examples
//!
//! Example `_start()` function and the panic handler.
//!
//! ```
//! #![no_std]
//! #![no_main]
//! #![feature(custom_test_frameworks)]
//! #![test_runner(rustos::test_runner)]
//! #![reexport_test_harness_main = "test_main"]
//! extern crate rustos;
//! extern crate x86_64;
//! use core::panic::PanicInfo;
//! use rustos::println;
//!
//! #[no_mangle]
//! pub extern "C" fn _start() -> ! {
//!     println!("Welcome to the real world!");
//!     rustos::init();
//!     use x86_64::registers::control::Cr3;
//!     let (level_4_page_table, _) = Cr3::read();
//!     println!("Level 4 page table at: {:?}", level_4_page_table);
//!     #[cfg(test)]
//!     test_main();
//!     println!("It did not crash!!!");
//!     rustos::hlt_loop();
//! }
//!
//! #[cfg(not(test))]
//! #[panic_handler]
//! fn panic(info: &PanicInfo) -> ! {
//!     println!("{}", info);
//!     rustos::hlt_loop();
//! }
//!
//! #[cfg(test)]
//! #[panic_handler]
//! fn panic(info: &PanicInfo) -> ! {
//!     rustos::test_panic_handler(info)
//! }
//! ```
#![no_std]
#![cfg_attr(test, no_main)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![feature(abi_x86_interrupt)]
extern crate lazy_static;
extern crate spin;
extern crate x86_64;

mod gdt;
mod interrupts;
pub mod serial;
pub mod vga;

use core::panic::PanicInfo;

/// Kernel initialization function.
pub fn init() {
    gdt::init();
    interrupts::init();
}

/// hlt instruction based kernel loop.
pub fn hlt_loop() -> ! {
    loop {
        x86_64::instructions::hlt();
    }
}

/// Qemu exit codes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QemuExitCode {
    /// Success code.
    Success = 0x10,
    /// Failed code.
    Failed = 0x11,
}

/// Qemu exit function.
pub fn exit_qemu(exit_code: QemuExitCode) {
    use x86_64::instructions::port::Port;
    unsafe {
        let mut port = Port::new(0xf4);
        port.write(exit_code as u32);
    }
}

#[cfg(test)]
#[no_mangle]
pub extern "C" fn _start() -> ! {
    init();
    test_main();
    hlt_loop();
}

/// Unit and the integration test runner.
pub fn test_runner(tests: &[&dyn Fn()]) {
    serial_println!("Running {} tests", tests.len());
    for test in tests {
        test();
    }
    exit_qemu(QemuExitCode::Success);
}

/// Unit and the integration test panic handler.
pub fn test_panic_handler(info: &PanicInfo) -> ! {
    serial_println!("[failed]\n");
    serial_println!("Error: {}", info);
    exit_qemu(QemuExitCode::Failed);
    hlt_loop();
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    test_panic_handler(info)
}