From ccd4631e2bf48de003f6ccc6b212e52a68619c3d Mon Sep 17 00:00:00 2001 From: Within Date: Wed, 23 Oct 2019 19:19:02 -0400 Subject: [PATCH] initial commit --- .cargo/config | 5 + .gitignore | 5 + Cargo.lock | 104 ++++++++++++++++++++ Cargo.toml | 44 +++++++++ src/gdt.rs | 46 +++++++++ src/interrupts.rs | 42 +++++++++ src/lib.rs | 64 +++++++++++++ src/main.rs | 35 +++++++ src/serial.rs | 34 +++++++ src/vga_buffer.rs | 204 ++++++++++++++++++++++++++++++++++++++++ tests/basic_boot.rs | 27 ++++++ tests/should_panic.rs | 25 +++++ tests/stack_overflow.rs | 60 ++++++++++++ x86_64-xe_os.json | 15 +++ 14 files changed, 710 insertions(+) create mode 100644 .cargo/config create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/gdt.rs create mode 100644 src/interrupts.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/serial.rs create mode 100644 src/vga_buffer.rs create mode 100644 tests/basic_boot.rs create mode 100644 tests/should_panic.rs create mode 100644 tests/stack_overflow.rs create mode 100644 x86_64-xe_os.json diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..d8bfeab --- /dev/null +++ b/.cargo/config @@ -0,0 +1,5 @@ +[build] +target = "x86_64-xe_os.json" + +[target.'cfg(target_os = "none")'] +runner = "bootimage runner" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7948021 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.idea +*.log +tmp/ +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..16179b1 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2803c32 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "xe_os" +version = "0.1.0" +authors = ["Christine Dodrill "] +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 diff --git a/src/gdt.rs b/src/gdt.rs new file mode 100644 index 0000000..0b3940f --- /dev/null +++ b/src/gdt.rs @@ -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, +} diff --git a/src/interrupts.rs b/src/interrupts.rs new file mode 100644 index 0000000..5c0e15b --- /dev/null +++ b/src/interrupts.rs @@ -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]"); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..08f7e37 --- /dev/null +++ b/src/lib.rs @@ -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); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d3435eb --- /dev/null +++ b/src/main.rs @@ -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); +} diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..e8073d9 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,34 @@ +use uart_16550::SerialPort; +use spin::Mutex; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref SERIAL1: Mutex = { + 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)*)); +} diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs new file mode 100644 index 0000000..183929e --- /dev/null +++ b/src/vga_buffer.rs @@ -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 = 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; 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]"); +} diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs new file mode 100644 index 0000000..726488e --- /dev/null +++ b/tests/basic_boot.rs @@ -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]"); +} diff --git a/tests/should_panic.rs b/tests/should_panic.rs new file mode 100644 index 0000000..763316a --- /dev/null +++ b/tests/should_panic.rs @@ -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 {} +} diff --git a/tests/stack_overflow.rs b/tests/stack_overflow.rs new file mode 100644 index 0000000..9f73f0f --- /dev/null +++ b/tests/stack_overflow.rs @@ -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(); +} diff --git a/x86_64-xe_os.json b/x86_64-xe_os.json new file mode 100644 index 0000000..c1c29f9 --- /dev/null +++ b/x86_64-xe_os.json @@ -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" + } \ No newline at end of file