commit
ccd4631e2b
14 changed files with 710 additions and 0 deletions
-
5.cargo/config
-
5.gitignore
-
104Cargo.lock
-
44Cargo.toml
-
46src/gdt.rs
-
42src/interrupts.rs
-
64src/lib.rs
-
35src/main.rs
-
34src/serial.rs
-
204src/vga_buffer.rs
-
27tests/basic_boot.rs
-
25tests/should_panic.rs
-
60tests/stack_overflow.rs
-
15x86_64-xe_os.json
@ -0,0 +1,5 @@ |
|||
[build] |
|||
target = "x86_64-xe_os.json" |
|||
|
|||
[target.'cfg(target_os = "none")'] |
|||
runner = "bootimage runner" |
@ -0,0 +1,5 @@ |
|||
.DS_Store |
|||
.idea |
|||
*.log |
|||
tmp/ |
|||
target/ |
@ -0,0 +1,104 @@ |
|||
# This file is automatically @generated by Cargo. |
|||
# It is not intended for manual editing. |
|||
[[package]] |
|||
name = "array-init" |
|||
version = "0.0.4" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
dependencies = [ |
|||
"nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "bit_field" |
|||
version = "0.9.0" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "bitflags" |
|||
version = "1.2.1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "bootloader" |
|||
version = "0.8.2" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "cast" |
|||
version = "0.2.2" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "lazy_static" |
|||
version = "1.4.0" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
dependencies = [ |
|||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "nodrop" |
|||
version = "0.1.14" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "spin" |
|||
version = "0.5.2" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "uart_16550" |
|||
version = "0.2.1" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
dependencies = [ |
|||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"x86_64 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "ux" |
|||
version = "0.1.3" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "volatile" |
|||
version = "0.2.6" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
|
|||
[[package]] |
|||
name = "x86_64" |
|||
version = "0.7.5" |
|||
source = "registry+https://github.com/rust-lang/crates.io-index" |
|||
dependencies = [ |
|||
"array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
] |
|||
|
|||
[[package]] |
|||
name = "xe_os" |
|||
version = "0.1.0" |
|||
dependencies = [ |
|||
"bootloader 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"uart_16550 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
"x86_64 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", |
|||
] |
|||
|
|||
[metadata] |
|||
"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" |
|||
"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" |
|||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" |
|||
"checksum bootloader 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abfbe6cdea6367860818facc8e4a184f003cb83d7d004acaaf57baebf1949da0" |
|||
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" |
|||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" |
|||
"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" |
|||
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" |
|||
"checksum uart_16550 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "803ea8cb602dbb32c1a657a866d2dd79fe7dbeab0fb2ac667cb4dcc7de12a58b" |
|||
"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" |
|||
"checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" |
|||
"checksum x86_64 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "997837f3913aac8f67164683258756d712376849906c8d609f844084bc03ef84" |
@ -0,0 +1,44 @@ |
|||
[package] |
|||
name = "xe_os" |
|||
version = "0.1.0" |
|||
authors = ["Christine Dodrill <[email protected]>"] |
|||
edition = "2018" |
|||
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
|||
|
|||
[dependencies] |
|||
bootloader = "0.8.0" |
|||
volatile = "0.2.6" |
|||
spin = "0.5.2" |
|||
x86_64 = "0.7.5" |
|||
uart_16550 = "0.2.0" |
|||
|
|||
[dependencies.lazy_static] |
|||
version = "1.0" |
|||
features = ["spin_no_std"] |
|||
|
|||
[profile.dev] |
|||
panic = "abort" |
|||
|
|||
[profile.release] |
|||
panic = "abort" |
|||
|
|||
[package.metadata.bootimage] |
|||
run-args = [ |
|||
"-display", "curses" |
|||
] |
|||
test-args = [ |
|||
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", |
|||
"-display", "none", |
|||
"-serial", "stdio" |
|||
] |
|||
test-success-exit-code = 33 |
|||
test-timeout = 300 |
|||
|
|||
[[test]] |
|||
name = "should_panic" |
|||
harness = false |
|||
|
|||
[[test]] |
|||
name = "stack_overflow" |
|||
harness = false |
@ -0,0 +1,46 @@ |
|||
use x86_64::VirtAddr;
|
|||
use x86_64::structures::tss::TaskStateSegment;
|
|||
use lazy_static::lazy_static;
|
|||
use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor, SegmentSelector};
|
|||
|
|||
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
|
|||
|
|||
lazy_static! {
|
|||
static ref TSS: TaskStateSegment = {
|
|||
let mut tss = TaskStateSegment::new();
|
|||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
|
|||
const STACK_SIZE: usize = 4096;
|
|||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
|||
|
|||
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
|
|||
let stack_end = stack_start + STACK_SIZE;
|
|||
stack_end
|
|||
};
|
|||
tss
|
|||
};
|
|||
}
|
|||
|
|||
pub fn init() {
|
|||
use x86_64::instructions::segmentation::set_cs;
|
|||
use x86_64::instructions::tables::load_tss;
|
|||
|
|||
GDT.0.load();
|
|||
unsafe {
|
|||
set_cs(GDT.1.code_selector);
|
|||
load_tss(GDT.1.tss_selector);
|
|||
}
|
|||
}
|
|||
|
|||
lazy_static! {
|
|||
static ref GDT: (GlobalDescriptorTable, Selectors) = {
|
|||
let mut gdt = GlobalDescriptorTable::new();
|
|||
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
|
|||
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
|
|||
(gdt, Selectors { code_selector, tss_selector })
|
|||
};
|
|||
}
|
|||
|
|||
struct Selectors {
|
|||
code_selector: SegmentSelector,
|
|||
tss_selector: SegmentSelector,
|
|||
}
|
@ -0,0 +1,42 @@ |
|||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
|||
use crate::{gdt, println};
|
|||
use lazy_static::lazy_static;
|
|||
|
|||
lazy_static! {
|
|||
static ref IDT: InterruptDescriptorTable = {
|
|||
let mut idt = InterruptDescriptorTable::new();
|
|||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
|||
unsafe {
|
|||
idt.double_fault.set_handler_fn(double_fault_handler)
|
|||
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
|
|||
}
|
|||
idt
|
|||
};
|
|||
}
|
|||
|
|||
pub fn init_idt() {
|
|||
IDT.load();
|
|||
}
|
|||
|
|||
extern "x86-interrupt" fn breakpoint_handler(
|
|||
stack_frame: &mut InterruptStackFrame)
|
|||
{
|
|||
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
|||
}
|
|||
|
|||
extern "x86-interrupt" fn double_fault_handler(
|
|||
stack_frame: &mut InterruptStackFrame, _error_code: u64)
|
|||
{
|
|||
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
use crate::{serial_print, serial_println};
|
|||
|
|||
#[test_case]
|
|||
fn test_breakpoint_exception() {
|
|||
serial_print!("test_breakpoint_exception...");
|
|||
// invoke a breakpoint exception
|
|||
x86_64::instructions::interrupts::int3();
|
|||
serial_println!("[ok]");
|
|||
}
|
@ -0,0 +1,64 @@ |
|||
#![no_std]
|
|||
#![cfg_attr(test, no_main)]
|
|||
#![feature(abi_x86_interrupt)]
|
|||
#![feature(custom_test_frameworks)]
|
|||
#![test_runner(crate::test_runner)]
|
|||
#![reexport_test_harness_main = "test_main"]
|
|||
|
|||
pub mod gdt;
|
|||
pub mod serial;
|
|||
pub mod interrupts;
|
|||
pub mod vga_buffer;
|
|||
|
|||
use core::panic::PanicInfo;
|
|||
|
|||
pub fn init() {
|
|||
gdt::init();
|
|||
interrupts::init_idt();
|
|||
}
|
|||
|
|||
pub fn test_runner(tests: &[&dyn Fn()]) {
|
|||
serial_println!("Running {} tests", tests.len());
|
|||
for test in tests {
|
|||
test();
|
|||
}
|
|||
exit_qemu(QemuExitCode::Success);
|
|||
}
|
|||
|
|||
pub fn test_panic_handler(info: &PanicInfo) -> ! {
|
|||
serial_println!("[failed]\n");
|
|||
serial_println!("Error: {}\n", info);
|
|||
exit_qemu(QemuExitCode::Failed);
|
|||
loop {}
|
|||
}
|
|||
|
|||
/// Entry point for `cargo xtest`
|
|||
#[cfg(test)]
|
|||
#[no_mangle]
|
|||
pub extern "C" fn _start() -> ! {
|
|||
init();
|
|||
test_main();
|
|||
loop {}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
#[panic_handler]
|
|||
fn panic(info: &PanicInfo) -> ! {
|
|||
test_panic_handler(info)
|
|||
}
|
|||
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|||
#[repr(u32)]
|
|||
pub enum QemuExitCode {
|
|||
Success = 0x10,
|
|||
Failed = 0x11,
|
|||
}
|
|||
|
|||
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);
|
|||
}
|
|||
}
|
@ -0,0 +1,35 @@ |
|||
#![no_std]
|
|||
#![no_main]
|
|||
#![feature(custom_test_frameworks)]
|
|||
#![test_runner(xe_os::test_runner)]
|
|||
#![reexport_test_harness_main = "test_main"]
|
|||
|
|||
use core::panic::PanicInfo;
|
|||
use xe_os::println;
|
|||
|
|||
#[no_mangle]
|
|||
pub extern "C" fn _start() -> ! {
|
|||
xe_os::init();
|
|||
println!("Hello World{}", "!");
|
|||
|
|||
#[cfg(test)]
|
|||
test_main();
|
|||
|
|||
println!("It did not crash!");
|
|||
loop {}
|
|||
}
|
|||
|
|||
// our existing panic handler
|
|||
#[cfg(not(test))] // new attribute
|
|||
#[panic_handler]
|
|||
fn panic(info: &PanicInfo) -> ! {
|
|||
println!("{}", info);
|
|||
loop {}
|
|||
}
|
|||
|
|||
// our panic handler in test mode
|
|||
#[cfg(test)]
|
|||
#[panic_handler]
|
|||
fn panic(info: &PanicInfo) -> ! {
|
|||
xe_os::test_panic_handler(info);
|
|||
}
|
@ -0,0 +1,34 @@ |
|||
use uart_16550::SerialPort;
|
|||
use spin::Mutex;
|
|||
use lazy_static::lazy_static;
|
|||
|
|||
lazy_static! {
|
|||
pub static ref SERIAL1: Mutex<SerialPort> = {
|
|||
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
|
|||
serial_port.init();
|
|||
Mutex::new(serial_port)
|
|||
};
|
|||
}
|
|||
|
|||
#[doc(hidden)]
|
|||
pub fn _print(args: ::core::fmt::Arguments) {
|
|||
use core::fmt::Write;
|
|||
SERIAL1.lock().write_fmt(args).expect("Printing to serial failed");
|
|||
}
|
|||
|
|||
/// Prints to the host through the serial interface.
|
|||
#[macro_export]
|
|||
macro_rules! serial_print {
|
|||
($($arg:tt)*) => {
|
|||
$crate::serial::_print(format_args!($($arg)*));
|
|||
};
|
|||
}
|
|||
|
|||
/// Prints to the host through the serial interface, appending a newline.
|
|||
#[macro_export]
|
|||
macro_rules! serial_println {
|
|||
() => ($crate::serial_print!("\n"));
|
|||
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
|
|||
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
|
|||
concat!($fmt, "\n"), $($arg)*));
|
|||
}
|
@ -0,0 +1,204 @@ |
|||
use core::fmt;
|
|||
use lazy_static::lazy_static;
|
|||
use spin::Mutex;
|
|||
use volatile::Volatile;
|
|||
|
|||
lazy_static! {
|
|||
/// A global `Writer` instance that can be used for printing to the VGA text buffer.
|
|||
///
|
|||
/// Used by the `print!` and `println!` macros.
|
|||
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
|
|||
column_position: 0,
|
|||
color_code: ColorCode::new(Color::Yellow, Color::Black),
|
|||
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
|
|||
});
|
|||
}
|
|||
|
|||
/// The standard color palette in VGA text mode.
|
|||
#[allow(dead_code)]
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|||
#[repr(u8)]
|
|||
pub enum Color {
|
|||
Black = 0,
|
|||
Blue = 1,
|
|||
Green = 2,
|
|||
Cyan = 3,
|
|||
Red = 4,
|
|||
Magenta = 5,
|
|||
Brown = 6,
|
|||
LightGray = 7,
|
|||
DarkGray = 8,
|
|||
LightBlue = 9,
|
|||
LightGreen = 10,
|
|||
LightCyan = 11,
|
|||
LightRed = 12,
|
|||
Pink = 13,
|
|||
Yellow = 14,
|
|||
White = 15,
|
|||
}
|
|||
|
|||
/// A combination of a foreground and a background color.
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|||
#[repr(transparent)]
|
|||
struct ColorCode(u8);
|
|||
|
|||
impl ColorCode {
|
|||
/// Create a new `ColorCode` with the given foreground and background colors.
|
|||
fn new(foreground: Color, background: Color) -> ColorCode {
|
|||
ColorCode((background as u8) << 4 | (foreground as u8))
|
|||
}
|
|||
}
|
|||
|
|||
/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`.
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|||
#[repr(C)]
|
|||
struct ScreenChar {
|
|||
ascii_character: u8,
|
|||
color_code: ColorCode,
|
|||
}
|
|||
|
|||
/// The height of the text buffer (normally 25 lines).
|
|||
const BUFFER_HEIGHT: usize = 25;
|
|||
/// The width of the text buffer (normally 80 columns).
|
|||
const BUFFER_WIDTH: usize = 80;
|
|||
|
|||
/// A structure representing the VGA text buffer.
|
|||
#[repr(transparent)]
|
|||
struct Buffer {
|
|||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
|||
}
|
|||
|
|||
/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`.
|
|||
///
|
|||
/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the
|
|||
/// `core::fmt::Write` trait.
|
|||
pub struct Writer {
|
|||
column_position: usize,
|
|||
color_code: ColorCode,
|
|||
buffer: &'static mut Buffer,
|
|||
}
|
|||
|
|||
impl Writer {
|
|||
/// Writes an ASCII byte to the buffer.
|
|||
///
|
|||
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character.
|
|||
pub fn write_byte(&mut self, byte: u8) {
|
|||
match byte {
|
|||
b'\n' => self.new_line(),
|
|||
byte => {
|
|||
if self.column_position >= BUFFER_WIDTH {
|
|||
self.new_line();
|
|||
}
|
|||
|
|||
let row = BUFFER_HEIGHT - 1;
|
|||
let col = self.column_position;
|
|||
|
|||
let color_code = self.color_code;
|
|||
self.buffer.chars[row][col].write(ScreenChar {
|
|||
ascii_character: byte,
|
|||
color_code,
|
|||
});
|
|||
self.column_position += 1;
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
/// Writes the given ASCII string to the buffer.
|
|||
///
|
|||
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
|
|||
/// support strings with non-ASCII characters, since they can't be printed in the VGA text
|
|||
/// mode.
|
|||
fn write_string(&mut self, s: &str) {
|
|||
for byte in s.bytes() {
|
|||
match byte {
|
|||
// printable ASCII byte or newline
|
|||
0x20..=0x7e | b'\n' => self.write_byte(byte),
|
|||
// not part of printable ASCII range
|
|||
_ => self.write_byte(0xfe),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
/// Shifts all lines one line up and clears the last row.
|
|||
fn new_line(&mut self) {
|
|||
for row in 1..BUFFER_HEIGHT {
|
|||
for col in 0..BUFFER_WIDTH {
|
|||
let character = self.buffer.chars[row][col].read();
|
|||
self.buffer.chars[row - 1][col].write(character);
|
|||
}
|
|||
}
|
|||
self.clear_row(BUFFER_HEIGHT - 1);
|
|||
self.column_position = 0;
|
|||
}
|
|||
|
|||
/// Clears a row by overwriting it with blank characters.
|
|||
fn clear_row(&mut self, row: usize) {
|
|||
let blank = ScreenChar {
|
|||
ascii_character: b' ',
|
|||
color_code: self.color_code,
|
|||
};
|
|||
for col in 0..BUFFER_WIDTH {
|
|||
self.buffer.chars[row][col].write(blank);
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Write for Writer {
|
|||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|||
self.write_string(s);
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
/// Like the `print!` macro in the standard library, but prints to the VGA text buffer.
|
|||
#[macro_export]
|
|||
macro_rules! print {
|
|||
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
|
|||
}
|
|||
|
|||
/// Like the `println!` macro in the standard library, but prints to the VGA text buffer.
|
|||
#[macro_export]
|
|||
macro_rules! println {
|
|||
() => ($crate::print!("\n"));
|
|||
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
|||
}
|
|||
|
|||
/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance.
|
|||
#[doc(hidden)]
|
|||
pub fn _print(args: fmt::Arguments) {
|
|||
use core::fmt::Write;
|
|||
WRITER.lock().write_fmt(args).unwrap();
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
use crate::{serial_print, serial_println};
|
|||
|
|||
#[test_case]
|
|||
fn test_println_simple() {
|
|||
serial_print!("test_println... ");
|
|||
println!("test_println_simple output");
|
|||
serial_println!("[ok]");
|
|||
}
|
|||
|
|||
#[test_case]
|
|||
fn test_println_many() {
|
|||
serial_print!("test_println_many... ");
|
|||
for _ in 0..200 {
|
|||
println!("test_println_many output");
|
|||
}
|
|||
serial_println!("[ok]");
|
|||
}
|
|||
|
|||
#[test_case]
|
|||
fn test_println_output() {
|
|||
serial_print!("test_println_output... ");
|
|||
|
|||
let s = "Some test string that fits on a single line";
|
|||
println!("{}", s);
|
|||
for (i, c) in s.chars().enumerate() {
|
|||
let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
|
|||
assert_eq!(char::from(screen_char.ascii_character), c);
|
|||
}
|
|||
|
|||
serial_println!("[ok]");
|
|||
}
|
@ -0,0 +1,27 @@ |
|||
#![no_std]
|
|||
#![no_main]
|
|||
#![feature(custom_test_frameworks)]
|
|||
#![test_runner(xe_os::test_runner)]
|
|||
#![reexport_test_harness_main = "test_main"]
|
|||
|
|||
use core::panic::PanicInfo;
|
|||
use xe_os::{println, serial_print, serial_println};
|
|||
|
|||
#[no_mangle] // don't mangle the name of this function
|
|||
pub extern "C" fn _start() -> ! {
|
|||
test_main();
|
|||
|
|||
loop {}
|
|||
}
|
|||
|
|||
#[panic_handler]
|
|||
fn panic(info: &PanicInfo) -> ! {
|
|||
xe_os::test_panic_handler(info);
|
|||
}
|
|||
|
|||
#[test_case]
|
|||
fn test_println() {
|
|||
serial_print!("test_println... ");
|
|||
println!("test_println output");
|
|||
serial_println!("[ok]");
|
|||
}
|
@ -0,0 +1,25 @@ |
|||
#![no_std]
|
|||
#![no_main]
|
|||
|
|||
use core::panic::PanicInfo;
|
|||
use xe_os::{exit_qemu, serial_print, serial_println, QemuExitCode};
|
|||
|
|||
#[no_mangle]
|
|||
pub extern "C" fn _start() -> ! {
|
|||
should_fail();
|
|||
serial_println!("[test did not panic]");
|
|||
exit_qemu(QemuExitCode::Failed);
|
|||
loop{}
|
|||
}
|
|||
|
|||
fn should_fail() {
|
|||
serial_print!("should_fail... ");
|
|||
assert_eq!(0, 1);
|
|||
}
|
|||
|
|||
#[panic_handler]
|
|||
fn panic(_info: &PanicInfo) -> ! {
|
|||
serial_println!("[ok]");
|
|||
exit_qemu(QemuExitCode::Success);
|
|||
loop {}
|
|||
}
|
@ -0,0 +1,60 @@ |
|||
#![no_std]
|
|||
#![no_main]
|
|||
#![feature(abi_x86_interrupt)]
|
|||
|
|||
use core::panic::PanicInfo;
|
|||
use xe_os::serial_print;
|
|||
|
|||
#[no_mangle]
|
|||
pub extern "C" fn _start() -> ! {
|
|||
serial_print!("stack_overflow... ");
|
|||
|
|||
xe_os::gdt::init();
|
|||
init_test_idt();
|
|||
|
|||
stack_overflow();
|
|||
|
|||
panic!("Execution continued after stack overflow");
|
|||
}
|
|||
|
|||
#[allow(unconditional_recursion)]
|
|||
fn stack_overflow() {
|
|||
stack_overflow();
|
|||
}
|
|||
|
|||
#[panic_handler]
|
|||
fn panic(info: &PanicInfo) -> ! {
|
|||
xe_os::test_panic_handler(info)
|
|||
}
|
|||
|
|||
use lazy_static::lazy_static;
|
|||
use x86_64::structures::idt::InterruptDescriptorTable;
|
|||
|
|||
lazy_static! {
|
|||
static ref TEST_IDT: InterruptDescriptorTable = {
|
|||
let mut idt = InterruptDescriptorTable::new();
|
|||
unsafe {
|
|||
idt.double_fault
|
|||
.set_handler_fn(test_double_fault_handler)
|
|||
.set_stack_index(xe_os::gdt::DOUBLE_FAULT_IST_INDEX);
|
|||
}
|
|||
|
|||
idt
|
|||
};
|
|||
}
|
|||
|
|||
use xe_os::{exit_qemu, QemuExitCode, serial_println};
|
|||
use x86_64::structures::idt::InterruptStackFrame;
|
|||
|
|||
extern "x86-interrupt" fn test_double_fault_handler(
|
|||
_stack_frame: &mut InterruptStackFrame,
|
|||
_error_code: u64,
|
|||
) {
|
|||
serial_println!("[ok]");
|
|||
exit_qemu(QemuExitCode::Success);
|
|||
loop {}
|
|||
}
|
|||
|
|||
pub fn init_test_idt() {
|
|||
TEST_IDT.load();
|
|||
}
|
@ -0,0 +1,15 @@ |
|||
{ |
|||
"llvm-target": "x86_64-unknown-none", |
|||
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", |
|||
"arch": "x86_64", |
|||
"target-endian": "little", |
|||
"target-pointer-width": "64", |
|||
"target-c-int-width": "32", |
|||
"os": "none", |
|||
"executables": true, |
|||
"linker-flavor": "ld.lld", |
|||
"linker": "rust-lld", |
|||
"panic-strategy": "abort", |
|||
"disable-redzone": true, |
|||
"features": "-mmx,-sse,+soft-float" |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue