Basic testing groundwork
This commit is contained in:
parent
371ac96fb1
commit
6e9bd7aaf1
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
/target
|
||||
/testing/target
|
||||
Cargo.lock
|
||||
|
|
|
@ -7,6 +7,6 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
conquer-once = "0.2.0"
|
||||
spinning_top = "0.1.0"
|
||||
conquer-once = { version = "0.2.0", default-features = false }
|
||||
spinning_top = { version = "0.1.0", features = ["nightly"] }
|
||||
x86_64 = "0.9.6"
|
||||
|
|
5
testing/.cargo/config
Normal file
5
testing/.cargo/config
Normal file
|
@ -0,0 +1,5 @@
|
|||
[build]
|
||||
target = "x86_64-bare-metal.json"
|
||||
|
||||
[target.'cfg(target_os = "none")']
|
||||
runner = "bootimage runner"
|
19
testing/cargo.toml
Normal file
19
testing/cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "testing"
|
||||
version = "0.1.0"
|
||||
authors = ["Ryan Kennedy <rkennedy9064@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bootloader = "0.8.9"
|
||||
conquer-once = { version = "0.2.0", default-features = false }
|
||||
spinning_top = { version = "0.1.0", features = ["nightly"] }
|
||||
uart_16550 = "0.2.4"
|
||||
x86_64 = "0.9.6"
|
||||
|
||||
[package.metadata.bootimage]
|
||||
test-args = [
|
||||
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",
|
||||
"-display", "none"
|
||||
]
|
||||
test-success-exit-code = 33
|
48
testing/src/gdt.rs
Normal file
48
testing/src/gdt.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use conquer_once::spin::Lazy;
|
||||
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
|
||||
|
||||
static TSS: Lazy<TaskStateSegment> = Lazy::new(|| {
|
||||
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
|
||||
});
|
||||
|
||||
static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| {
|
||||
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,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
55
testing/src/lib.rs
Normal file
55
testing/src/lib.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#![cfg_attr(test, no_main)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![no_std]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[macro_use]
|
||||
pub mod serial;
|
||||
pub mod gdt;
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
test_main();
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
test_panic_handler(info)
|
||||
}
|
33
testing/src/serial.rs
Normal file
33
testing/src/serial.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use conquer_once::spin::Lazy;
|
||||
use spinning_top::Spinlock;
|
||||
use uart_16550::SerialPort;
|
||||
|
||||
pub static SERIAL1: Lazy<Spinlock<SerialPort>> = Lazy::new(|| {
|
||||
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
|
||||
serial_port.init();
|
||||
Spinlock::new(serial_port)
|
||||
});
|
||||
|
||||
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)*));
|
||||
}
|
27
testing/tests/basic_boot.rs
Normal file
27
testing/tests/basic_boot.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![test_runner(testing::test_runner)]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use testing::{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) -> ! {
|
||||
testing::test_panic_handler(info)
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn basic_boot() {
|
||||
serial_print!("basic_boot... ");
|
||||
assert_eq!(0, 0);
|
||||
serial_println!("[ok]");
|
||||
}
|
15
testing/x86_64-bare-metal.json
Normal file
15
testing/x86_64-bare-metal.json
Normal file
|
@ -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 a new issue