Skip to content

Commit b5f7697

Browse files
committed
freestanding rust binary, VGA, set up QEMU with serial port and isa-debug-exit, custom test framework
0 parents  commit b5f7697

File tree

11 files changed

+483
-0
lines changed

11 files changed

+483
-0
lines changed

.cargo/config.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[build]
2+
target = "bare-metal-target.json"
3+
4+
[unstable]
5+
build-std-features = ["compiler-builtins-mem"]
6+
build-std = ["core", "compiler_builtins"]
7+
8+
[target.'cfg(target_os = "none")']
9+
runner = "bootimage runner"
10+
11+
# isso aqui é para compilar um binário freestading para o sistema host
12+
# é melhor compilar para um target bare metal já que precisamos fazer outras coisas além disso, como inicializar a stack.
13+
14+
# não linka com a rotina de startup padrão do runtime C
15+
16+
# [target.'cfg(target_os = "linux")']
17+
# rustflags = ["-C", "link-arg=-nostartfiles"]
18+
19+
# [target.'cfg(target_os = "windows")']
20+
# rustflags = ["-C", "link-args=/ENTRY:_start /SUBSYSTEM:console"]
21+
# espera a entrada dependendo do subsystem utilizado. ao especificar /ENTRY precisamos especificar o subsistema tbm que pode ser por ex WINDOWS ou CONSOLE.
22+
23+
# [target.'cfg(target_os = "macos")']
24+
# rustflags = ["-C", "link-args=-e __start -static -nostartfiles"]
25+
26+
# nostartfiles é para não linkar com o crt0 (c runtime zero).
27+
# _ adicional no nome do entrypoint é pq no mac as funções começam com _.
28+
# -static é para linkar um binário estático e não linçar com a lib libSystem.

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
on:
2+
push:
3+
branches:
4+
- main
5+
workflow_dispatch:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Check out the code
13+
uses: actions/checkout@v2
14+
- name: Install QEMU
15+
run: |
16+
sudo apt-get update
17+
sudo apt-get install qemu qemu-system-x86 qemu-utils
18+
- name: Set up Rust
19+
uses: actions-rs/toolchain@v1
20+
with:
21+
toolchain: nightly
22+
- name: Install dependencies
23+
run: |
24+
cargo build
25+
- name: Run tests
26+
run: cargo test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"rust-analyzer.check.overrideCommand": [
3+
"./cargo",
4+
"xcheck",
5+
"--json-output"
6+
]
7+
}

Cargo.lock

Lines changed: 101 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[package]
2+
name = "osdev_rust"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bootloader = "0.9"
8+
volatile = "0.2.6"
9+
spin = "0.5.2"
10+
x86_64 = "0.14.2"
11+
uart_16550 = "0.2.0"
12+
13+
[dependencies.lazy_static]
14+
version = "1.0"
15+
features = ["spin_no_std"]
16+
17+
# [profile.dev]
18+
# panic = "abort"
19+
20+
[profile.release]
21+
panic = "abort"
22+
23+
[package.metadata.bootimage]
24+
test-args = [
25+
"-device",
26+
"isa-debug-exit,iobase=0xf4,iosize=0x04",
27+
"-serial",
28+
"stdio",
29+
]
30+
# (0x10 << 1) | 1 = 32 | 1 = 33
31+
test-success-exit-code = 33
32+
test-timeout = 60 # 1 minute in seconds

bare-metal-target.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"llvm-target": "x86_64-unknown-none",
3+
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
4+
"arch": "x86_64",
5+
"target-endian": "little",
6+
"target-pointer-width": "64",
7+
"target-c-int-width": "32",
8+
"os": "none",
9+
"executables": true,
10+
"linker-flavor": "ld.lld",
11+
"linker": "rust-lld",
12+
"panic-strategy": "abort",
13+
"disable-redzone": true,
14+
"features": "-mmx,-sse,+soft-float",
15+
"rustc-abi": "x86-softfloat"
16+
}

rust-toolchain

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nightly

src/main.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#![feature(custom_test_frameworks)]
2+
#![test_runner(crate::test_runner)]
3+
#![reexport_test_harness_main = "test_main"]
4+
#![no_std]
5+
#![no_main]
6+
7+
mod serial;
8+
mod vga_buffer;
9+
10+
use core::panic::PanicInfo;
11+
12+
#[no_mangle]
13+
pub extern "C" fn _start() -> ! {
14+
println!("Hello World!");
15+
16+
#[cfg(test)]
17+
test_main();
18+
19+
loop {}
20+
}
21+
22+
#[cfg(not(test))]
23+
#[panic_handler]
24+
fn panic(info: &PanicInfo) -> ! {
25+
println!("{}", info);
26+
27+
loop {}
28+
}
29+
30+
#[cfg(test)]
31+
#[panic_handler]
32+
fn panic(info: &PanicInfo) -> ! {
33+
serial_println!("[failed]\n");
34+
serial_println!("Error: {}\n", info);
35+
exit_qemu(QemuExitCode::Failed);
36+
loop {}
37+
}
38+
39+
pub trait Testable {
40+
fn run(&self);
41+
}
42+
43+
impl<T> Testable for T
44+
where
45+
T: Fn(),
46+
{
47+
fn run(&self) {
48+
serial_print!("{}...\t", core::any::type_name::<T>());
49+
self();
50+
serial_println!("[ok]");
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
pub fn test_runner(tests: &[&dyn Testable]) {
56+
println!("Running {} tests", tests.len());
57+
58+
for test in tests {
59+
test.run();
60+
}
61+
62+
exit_qemu(QemuExitCode::Success);
63+
}
64+
65+
#[test_case]
66+
fn trivial_assertion() {
67+
assert_eq!(1, 1);
68+
}
69+
70+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71+
#[repr(u32)]
72+
pub enum QemuExitCode {
73+
Success = 0x10,
74+
Failed = 0x11,
75+
}
76+
77+
pub fn exit_qemu(exit_code: QemuExitCode) {
78+
use x86_64::instructions::port::Port;
79+
80+
unsafe {
81+
let mut port = Port::new(0xf4);
82+
port.write(exit_code as u32);
83+
}
84+
}

src/serial.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use lazy_static::lazy_static;
2+
use spin::Mutex;
3+
use uart_16550::SerialPort;
4+
5+
lazy_static! {
6+
pub static ref SERIAL1: Mutex<SerialPort> = {
7+
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
8+
serial_port.init();
9+
Mutex::new(serial_port)
10+
};
11+
}
12+
13+
#[doc(hidden)]
14+
pub fn _print(args: ::core::fmt::Arguments) {
15+
use core::fmt::Write;
16+
17+
SERIAL1
18+
.lock()
19+
.write_fmt(args)
20+
.expect("Printing to serial port failed.")
21+
}
22+
23+
#[macro_export]
24+
macro_rules! serial_print {
25+
($($arg:tt)*) => {
26+
$crate::serial::_print(format_args!($($arg)*))
27+
};
28+
}
29+
30+
#[macro_export]
31+
macro_rules! serial_println {
32+
() => {
33+
$crate::serial_print!("\n")
34+
};
35+
($fmt:expr) => {
36+
$crate::serial_print!(concat!($fmt, "\n"))
37+
};
38+
($fmt:expr, $($arg:tt)*) => {
39+
$crate::serial_print!(concat!($fmt, "\n"), $($arg)*)
40+
};
41+
}

0 commit comments

Comments
 (0)