Compare commits

...

4 Commits

Author SHA1 Message Date
Philipp Oppermann
dee04d74d6 Shift lines up instead of clearing the screen when full 2020-09-09 19:00:51 +02:00
Philipp Oppermann
4d2f18d481 Force unlock the Writer on panic 2020-09-09 16:35:52 +02:00
Philipp Oppermann
4f4a70bf79 Don't panic on unknown pixel formats 2020-09-09 16:35:29 +02:00
Philipp Oppermann
261f4d0649 Update to latest bootloader prototype 2020-09-09 15:31:43 +02:00
13 changed files with 501 additions and 235 deletions

View File

@@ -1,8 +1,14 @@
[unstable] [unstable]
build-std = ["core", "compiler_builtins", "alloc"] # TODO: uncomment once https://github.com/rust-lang/cargo/issues/8687 is resolved
# build-std = ["core", "alloc"]
[build] [build]
target = "x86_64-blog_os.json" # TODO: uncomment once https://github.com/rust-lang/cargo/issues/8687 is resolved
# target = "x86_64-blog_os.json"
[target.'cfg(target_os = "none")'] [target.'cfg(target_os = "none")']
runner = "bootimage runner" runner = "cargo run --package disk_image --bin runner --quiet"
[alias]
xrun = "run --target x86_64-blog_os.json -Zbuild-std=core,alloc"
bootimage = "run --package disk_image --bin disk_image --quiet"

139
Cargo.lock generated
View File

@@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "anyhow"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.0" version = "1.0.0"
@@ -25,6 +31,7 @@ dependencies = [
"bootloader", "bootloader",
"conquer-once", "conquer-once",
"crossbeam-queue", "crossbeam-queue",
"font8x8",
"futures-util", "futures-util",
"lazy_static", "lazy_static",
"linked_list_allocator", "linked_list_allocator",
@@ -40,8 +47,15 @@ dependencies = [
[[package]] [[package]]
name = "bootloader" name = "bootloader"
version = "0.9.8" version = "0.9.8"
[[package]]
name = "bootloader-locator"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad686b9b47363de7d36c05fb6885f17d08d0f2d15b1bc782a101fe3c94a2c7c" checksum = "aaaa9db3339d32c2622f2e5d0731eb82a468d3439797c9d4fe426744fe2bd551"
dependencies = [
"json",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -51,9 +65,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]] [[package]]
name = "conquer-once" name = "conquer-once"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f" checksum = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3"
dependencies = [ dependencies = [
"conquer-util", "conquer-util",
] ]
@@ -90,6 +104,22 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "disk_image"
version = "0.1.0"
dependencies = [
"anyhow",
"bootloader-locator",
"locate-cargo-manifest",
"runner-utils",
]
[[package]]
name = "font8x8"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be"
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.4" version = "0.3.4"
@@ -113,6 +143,12 @@ dependencies = [
"pin-utils", "pin-utils",
] ]
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -122,6 +158,12 @@ dependencies = [
"spin", "spin",
] ]
[[package]]
name = "libc"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]] [[package]]
name = "linked_list_allocator" name = "linked_list_allocator"
version = "0.8.0" version = "0.8.0"
@@ -131,6 +173,15 @@ dependencies = [
"spinning_top", "spinning_top",
] ]
[[package]]
name = "locate-cargo-manifest"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db985b63431fe09e8d71f50aeceffcc31e720cb86be8dad2f38d084c5a328466"
dependencies = [
"json",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.3.3" version = "0.3.3"
@@ -161,12 +212,40 @@ version = "0.1.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
[[package]]
name = "proc-macro2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "rlibc" name = "rlibc"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe"
[[package]]
name = "runner-utils"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9dc6848b056990cd51e72aa5556bdbea4a96013e8b18635d183c84159c2988f"
dependencies = [
"thiserror",
"wait-timeout",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@@ -188,6 +267,37 @@ dependencies = [
"lock_api", "lock_api",
] ]
[[package]]
name = "syn"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "uart_16550" name = "uart_16550"
version = "0.2.7" version = "0.2.7"
@@ -199,16 +309,29 @@ dependencies = [
] ]
[[package]] [[package]]
name = "volatile" name = "unicode-xid"
version = "0.2.6" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "volatile"
version = "0.3.0"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "x86_64" name = "x86_64"
version = "0.11.0" version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" checksum = "7c6e9604d08cf91ba0e5cc2175e8cbbdeeb2b8230b818fd9052604c4c5ffd620"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"bitflags", "bitflags",

View File

@@ -4,6 +4,11 @@ version = "0.1.0"
authors = ["Philipp Oppermann <dev@phil-opp.com>"] authors = ["Philipp Oppermann <dev@phil-opp.com>"]
edition = "2018" edition = "2018"
[workspace]
members = [
"disk_image",
]
[[test]] [[test]]
name = "should_panic" name = "should_panic"
harness = false harness = false
@@ -13,15 +18,16 @@ name = "stack_overflow"
harness = false harness = false
[dependencies] [dependencies]
bootloader = { version = "0.9.8", features = ["map_physical_memory"]} bootloader = { path = "../../uefi-test"}
rlibc = "1.0.0" rlibc = "1.0.0"
volatile = "0.2.6"
spin = "0.5.2" spin = "0.5.2"
x86_64 = "0.11.0" x86_64 = "0.11.0"
uart_16550 = "0.2.0" uart_16550 = "0.2.0"
pic8259_simple = "0.2.0" pic8259_simple = "0.2.0"
pc-keyboard = "0.5.0" pc-keyboard = "0.5.0"
linked_list_allocator = "0.8.0" linked_list_allocator = "0.8.0"
volatile = { path= "../../volatile", features = ["unstable"] }
font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"]}
[dependencies.lazy_static] [dependencies.lazy_static]
version = "1.0" version = "1.0"
@@ -33,7 +39,7 @@ default-features = false
features = ["alloc"] features = ["alloc"]
[dependencies.conquer-once] [dependencies.conquer-once]
version = "0.2.0" version = "0.2.1"
default-features = false default-features = false
[dependencies.futures-util] [dependencies.futures-util]
@@ -41,9 +47,5 @@ version = "0.3.4"
default-features = false default-features = false
features = ["alloc"] features = ["alloc"]
[package.metadata.bootimage] [package.metadata.bootloader]
test-args = [ map-physical-memory = true
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio",
"-display", "none"
]
test-success-exit-code = 33 # (0x10 << 1) | 1

13
disk_image/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "disk_image"
version = "0.1.0"
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.32"
bootloader-locator = "0.0.4"
runner-utils = "0.0.2"
locate-cargo-manifest = "0.2.0"

View File

@@ -0,0 +1,57 @@
use anyhow::anyhow;
use std::{
path::PathBuf,
process::{Command, ExitStatus},
time::Duration,
};
const RUN_ARGS: &[&str] = &["-d", "int", "--no-reboot", "-s"];
const TEST_ARGS: &[&str] = &[
"-device",
"isa-debug-exit,iobase=0xf4,iosize=0x04",
"-serial",
"stdio",
"-display",
"none",
"--no-reboot",
];
const TEST_TIMEOUT_SECS: u64 = 10;
fn main() -> anyhow::Result<()> {
let kernel_binary_path = {
let path = PathBuf::from(std::env::args().nth(1).unwrap());
path.canonicalize()?
};
let disk_image = disk_image::create_disk_image(&kernel_binary_path, true)?;
let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd
.arg("-drive")
.arg(format!("format=raw,file={}", disk_image.display()));
let binary_kind = runner_utils::binary_kind(&kernel_binary_path);
if binary_kind.is_test() {
run_cmd.args(TEST_ARGS);
let exit_status = run_test_command(run_cmd)?;
match exit_status.code() {
Some(33) => {} // success
other => return Err(anyhow!("Test failed (exit code: {:?})", other)),
}
} else {
run_cmd.args(RUN_ARGS);
let exit_status = run_cmd.status()?;
if !exit_status.success() {
std::process::exit(exit_status.code().unwrap_or(1));
}
}
Ok(())
}
fn run_test_command(mut cmd: Command) -> anyhow::Result<ExitStatus> {
let status = runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS))?;
Ok(status)
}

45
disk_image/src/lib.rs Normal file
View File

@@ -0,0 +1,45 @@
use anyhow::anyhow;
use std::{
path::{Path, PathBuf},
process::Command,
};
pub fn create_disk_image(kernel_binary_path: &Path, bios_only: bool) -> anyhow::Result<PathBuf> {
let bootloader_manifest_path = bootloader_locator::locate_bootloader("bootloader")?;
let kernel_manifest_path = locate_cargo_manifest::locate_manifest()?;
let mut build_cmd = Command::new(env!("CARGO"));
build_cmd.current_dir(bootloader_manifest_path.parent().unwrap());
build_cmd.arg("builder");
build_cmd
.arg("--kernel-manifest")
.arg(&kernel_manifest_path);
build_cmd.arg("--kernel-binary").arg(&kernel_binary_path);
build_cmd
.arg("--target-dir")
.arg(kernel_manifest_path.parent().unwrap().join("target"));
build_cmd
.arg("--out-dir")
.arg(kernel_binary_path.parent().unwrap());
build_cmd.arg("--quiet");
if bios_only {
build_cmd.arg("--firmware").arg("bios");
}
if !build_cmd.status()?.success() {
return Err(anyhow!("build failed"));
}
let kernel_binary_name = kernel_binary_path.file_name().unwrap().to_str().unwrap();
let disk_image = kernel_binary_path
.parent()
.unwrap()
.join(format!("bootimage-bios-{}.bin", kernel_binary_name));
if !disk_image.exists() {
return Err(anyhow!(
"Disk image does not exist at {} after bootloader build",
disk_image.display()
));
}
Ok(disk_image)
}

40
disk_image/src/main.rs Normal file
View File

@@ -0,0 +1,40 @@
use anyhow::anyhow;
use std::process::Command;
const TARGET_NAME: &str = "x86_64-blog_os";
const KERNEL_BINARIES: &[&str] = &["blog_os"];
fn main() -> anyhow::Result<()> {
// build all binaries
let mut build_cmd = Command::new(env!("CARGO"));
build_cmd.arg("build");
build_cmd.arg("--release");
build_cmd.arg("-Zbuild-std=core");
build_cmd
.arg("--target")
.arg(format!("{}.json", TARGET_NAME));
if !build_cmd.status()?.success() {
return Err(anyhow!("build failed"));
};
let kernel_manifest = locate_cargo_manifest::locate_manifest()?;
let target_dir_root = kernel_manifest.parent().unwrap().join("target");
let target_dir = target_dir_root.join(TARGET_NAME).join("release");
for binary_name in KERNEL_BINARIES {
let binary_path = {
let path = target_dir.join(binary_name);
path.canonicalize()?
};
let disk_image = disk_image::create_disk_image(&binary_path, false)?;
println!(
"Created disk image for binary {} at {}",
binary_name,
disk_image.display()
);
}
Ok(())
}

179
src/framebuffer.rs Normal file
View File

@@ -0,0 +1,179 @@
use bootloader::boot_info::PixelFormat;
use core::{fmt, slice};
use font8x8::UnicodeFonts;
use spin::Mutex;
use volatile::Volatile;
pub static WRITER: Mutex<Option<Writer>> = Mutex::new(None);
pub fn init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) {
let mut writer = Writer {
info: framebuffer.info(),
buffer: Volatile::new(framebuffer.buffer()),
x_pos: 0,
y_pos: 0,
};
writer.clear();
// global writer should not be locked here
let mut global_writer = WRITER.try_lock().unwrap();
assert!(global_writer.is_none(), "Global writer already initialized");
*global_writer = Some(writer);
}
pub struct Writer {
buffer: Volatile<&'static mut [u8]>,
info: bootloader::boot_info::FrameBufferInfo,
x_pos: usize,
y_pos: usize,
}
impl Writer {
fn newline(&mut self) {
self.y_pos += 8;
self.carriage_return();
}
fn carriage_return(&mut self) {
self.x_pos = 0;
}
/// Erases all text on the screen
pub fn clear(&mut self) {
self.x_pos = 0;
self.y_pos = 0;
self.buffer.fill(0);
}
fn shift_lines_up(&mut self) {
let offset = self.info.stride * self.info.bytes_per_pixel * 8;
self.buffer.copy_within(offset.., 0);
self.y_pos -= 8;
}
fn width(&self) -> usize {
self.info.horizontal_resolution
}
fn height(&self) -> usize {
self.info.vertical_resolution
}
fn write_char(&mut self, c: char) {
match c {
'\n' => self.newline(),
'\r' => self.carriage_return(),
c => {
if self.x_pos >= self.width() {
self.newline();
}
while self.y_pos >= (self.height() - 8) {
self.shift_lines_up();
}
let rendered = font8x8::BASIC_FONTS
.get(c)
.expect("character not found in basic font");
self.write_rendered_char(rendered);
}
}
}
fn write_rendered_char(&mut self, rendered_char: [u8; 8]) {
for (y, byte) in rendered_char.iter().enumerate() {
for (x, bit) in (0..8).enumerate() {
let on = *byte & (1 << bit) != 0;
self.write_pixel(self.x_pos + x, self.y_pos + y, on);
}
}
self.x_pos += 8;
}
fn write_pixel(&mut self, x: usize, y: usize, on: bool) {
let pixel_offset = y * self.info.stride + x;
let color = if on {
match self.info.pixel_format {
PixelFormat::RGB => [0x33, 0xff, 0x66, 0],
PixelFormat::BGR => [0x66, 0xff, 0x33, 0],
_other => [0xff, 0xff, 0xff, 0],
}
} else {
[0, 0, 0, 0]
};
let bytes_per_pixel = self.info.bytes_per_pixel;
let byte_offset = pixel_offset * bytes_per_pixel;
self.buffer
.index_mut(byte_offset..(byte_offset + bytes_per_pixel))
.copy_from_slice(&color[..bytes_per_pixel]);
}
/// 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 char in s.chars() {
self.write_char(char);
}
}
}
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::framebuffer::_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;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
WRITER.lock().as_mut().unwrap().write_fmt(args).unwrap();
});
}
#[test_case]
fn test_println_simple() {
println!("test_println_simple output");
}
#[test_case]
fn test_println_many() {
for _ in 0..200 {
println!("test_println_many output");
}
}
#[test_case]
fn test_println_output() {
use core::fmt::Write;
use x86_64::instructions::interrupts;
let s = "Some test string that fits on a single line";
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writeln!(writer, "\n{}", s).expect("writeln failed");
for (i, c) in s.chars().enumerate() {
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
}

View File

@@ -15,12 +15,12 @@ extern crate rlibc;
use core::panic::PanicInfo; use core::panic::PanicInfo;
pub mod allocator; pub mod allocator;
pub mod framebuffer;
pub mod gdt; pub mod gdt;
pub mod interrupts; pub mod interrupts;
pub mod memory; pub mod memory;
pub mod serial; pub mod serial;
pub mod task; pub mod task;
pub mod vga_buffer;
pub fn init() { pub fn init() {
gdt::init(); gdt::init();

View File

@@ -13,17 +13,19 @@ use core::panic::PanicInfo;
entry_point!(kernel_main); entry_point!(kernel_main);
fn kernel_main(boot_info: &'static BootInfo) -> ! { fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
use blog_os::allocator; use blog_os::allocator;
use blog_os::memory::{self, BootInfoFrameAllocator}; use blog_os::memory::{self, BootInfoFrameAllocator};
use x86_64::VirtAddr; use x86_64::VirtAddr;
blog_os::framebuffer::init(boot_info.framebuffer.as_mut().unwrap());
println!("Hello World{}", "!"); println!("Hello World{}", "!");
blog_os::init(); blog_os::init();
let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset.unwrap());
let mut mapper = unsafe { memory::init(phys_mem_offset) }; let mut mapper = unsafe { memory::init(phys_mem_offset) };
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_regions) };
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
@@ -40,6 +42,9 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
#[cfg(not(test))] #[cfg(not(test))]
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
unsafe {
blog_os::framebuffer::WRITER.force_unlock();
};
println!("{}", info); println!("{}", info);
blog_os::hlt_loop(); blog_os::hlt_loop();
} }

View File

@@ -1,4 +1,4 @@
use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; use bootloader::memory_map::{MemoryRegion, MemoryRegionKind};
use x86_64::{ use x86_64::{
structures::paging::{ structures::paging::{
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB, FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
@@ -64,7 +64,7 @@ unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
/// A FrameAllocator that returns usable frames from the bootloader's memory map. /// A FrameAllocator that returns usable frames from the bootloader's memory map.
pub struct BootInfoFrameAllocator { pub struct BootInfoFrameAllocator {
memory_map: &'static MemoryMap, memory_map: &'static [MemoryRegion],
next: usize, next: usize,
} }
@@ -74,7 +74,7 @@ impl BootInfoFrameAllocator {
/// This function is unsafe because the caller must guarantee that the passed /// This function is unsafe because the caller must guarantee that the passed
/// memory map is valid. The main requirement is that all frames that are marked /// memory map is valid. The main requirement is that all frames that are marked
/// as `USABLE` in it are really unused. /// as `USABLE` in it are really unused.
pub unsafe fn init(memory_map: &'static MemoryMap) -> Self { pub unsafe fn init(memory_map: &'static [MemoryRegion]) -> Self {
BootInfoFrameAllocator { BootInfoFrameAllocator {
memory_map, memory_map,
next: 0, next: 0,
@@ -85,9 +85,9 @@ impl BootInfoFrameAllocator {
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> { fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
// get usable regions from memory map // get usable regions from memory map
let regions = self.memory_map.iter(); let regions = self.memory_map.iter();
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable); let usable_regions = regions.filter(|r| r.kind == MemoryRegionKind::Usable);
// map each region to its address range // map each region to its address range
let addr_ranges = usable_regions.map(|r| r.range.start_addr()..r.range.end_addr()); let addr_ranges = usable_regions.map(|r| r.start..r.end);
// transform to an iterator of frame start addresses // transform to an iterator of frame start addresses
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
// create `PhysFrame` types from the start addresses // create `PhysFrame` types from the start addresses

View File

@@ -1,204 +0,0 @@
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;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
WRITER.lock().write_fmt(args).unwrap();
});
}
#[test_case]
fn test_println_simple() {
println!("test_println_simple output");
}
#[test_case]
fn test_println_many() {
for _ in 0..200 {
println!("test_println_many output");
}
}
#[test_case]
fn test_println_output() {
use core::fmt::Write;
use x86_64::instructions::interrupts;
let s = "Some test string that fits on a single line";
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writeln!(writer, "\n{}", s).expect("writeln failed");
for (i, c) in s.chars().enumerate() {
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
}

View File

@@ -19,9 +19,9 @@ fn main(boot_info: &'static BootInfo) -> ! {
use x86_64::VirtAddr; use x86_64::VirtAddr;
blog_os::init(); blog_os::init();
let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset.unwrap());
let mut mapper = unsafe { memory::init(phys_mem_offset) }; let mut mapper = unsafe { memory::init(phys_mem_offset) };
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_regions) };
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
test_main(); test_main();