Basic testing groundwork

This commit is contained in:
Ryan Kennedy 2020-03-12 13:34:48 -05:00
parent 371ac96fb1
commit 6e9bd7aaf1
9 changed files with 205 additions and 2 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target
/testing/target
Cargo.lock

View file

@ -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
View 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
View 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
View 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
View 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
View 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)*));
}

View 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]");
}

View 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"
}