commit
ccd4631e2b
14 changed files with 710 additions and 0 deletions
@ -0,0 +1,5 @@
|
||||
[build] |
||||
target = "x86_64-xe_os.json" |
||||
|
||||
[target.'cfg(target_os = "none")'] |
||||
runner = "bootimage runner" |
@ -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" |
||||
} |
Loading…
Reference in new issue