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
20 changed files with 598 additions and 312 deletions

View File

@@ -1,9 +1,14 @@
[unstable]
build-std = ["core", "compiler_builtins", "alloc"]
build-std-features = ["compiler-builtins-mem"]
# TODO: uncomment once https://github.com/rust-lang/cargo/issues/8687 is resolved
# build-std = ["core", "alloc"]
[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")']
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"

View File

@@ -1,4 +1,4 @@
name: Code
name: Build Code
on:
push:
@@ -16,7 +16,6 @@ jobs:
check:
name: Check
strategy:
fail-fast: false
matrix:
platform: [
ubuntu-latest,
@@ -42,7 +41,6 @@ jobs:
test:
name: Test
strategy:
fail-fast: false
matrix:
platform: [
ubuntu-latest,
@@ -78,10 +76,14 @@ jobs:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
- name: Install QEMU (Windows)
- name: Install Scoop (Windows)
run: |
choco install qemu --version 2021.5.5
echo "$Env:Programfiles\qemu" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
echo ::add-path::$HOME\scoop\shims
if: runner.os == 'Windows'
shell: pwsh
- name: Install QEMU (Windows)
run: scoop install qemu
if: runner.os == 'Windows'
shell: pwsh
- name: "Print QEMU Version"

230
Cargo.lock generated
View File

@@ -1,18 +1,22 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bit_field"
version = "0.10.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56"
[[package]]
name = "bitflags"
@@ -27,22 +31,31 @@ dependencies = [
"bootloader",
"conquer-once",
"crossbeam-queue",
"font8x8",
"futures-util",
"lazy_static",
"linked_list_allocator",
"pc-keyboard",
"pic8259",
"pic8259_simple",
"rlibc",
"spin",
"uart_16550",
"volatile 0.2.7",
"volatile",
"x86_64",
]
[[package]]
name = "bootloader"
version = "0.9.23"
version = "0.9.8"
[[package]]
name = "bootloader-locator"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6e02311b16c9819e7c72866d379cdd3026c3b7b25c1edf161f548f8e887e7ff"
checksum = "aaaa9db3339d32c2622f2e5d0731eb82a468d3439797c9d4fe426744fe2bd551"
dependencies = [
"json",
]
[[package]]
name = "cfg-if"
@@ -66,14 +79,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a"
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
name = "cpuio"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
checksum = "d531514efb06912141fa65967447de805691b685a7565c87d1765afe34a98aa7"
[[package]]
name = "crossbeam-queue"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
dependencies = [
"cfg-if",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
@@ -87,30 +105,50 @@ dependencies = [
]
[[package]]
name = "futures-core"
version = "0.3.15"
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 = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1"
checksum = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be"
[[package]]
name = "futures-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
[[package]]
name = "futures-task"
version = "0.3.15"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae"
checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
[[package]]
name = "futures-util"
version = "0.3.15"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
dependencies = [
"autocfg",
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -121,55 +159,92 @@ dependencies = [
]
[[package]]
name = "linked_list_allocator"
version = "0.9.0"
name = "libc"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b725207570aa16096962d0b20c79f8a543df2280bd3c903022b9b0b4d7ea68"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "linked_list_allocator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18c618c431dfe4419afbe22852f6aceffbc17bd82ba0a18b982def291000824c"
dependencies = [
"spinning_top",
]
[[package]]
name = "lock_api"
version = "0.4.4"
name = "locate-cargo-manifest"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
checksum = "db985b63431fe09e8d71f50aeceffcc31e720cb86be8dad2f38d084c5a328466"
dependencies = [
"json",
]
[[package]]
name = "lock_api"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
dependencies = [
"scopeguard",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "pc-keyboard"
version = "0.5.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac"
checksum = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce"
[[package]]
name = "pic8259"
version = "0.10.1"
name = "pic8259_simple"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cc920d83ee33c0f9b73aa441e75468bf2d10c959a3eb6260cf720b05ac91a1"
checksum = "af2a5497fb8e59bf8015f67b7dff238d75ef445e03f23edac24ac3a8f09be952"
dependencies = [
"x86_64",
"cpuio",
]
[[package]]
name = "pin-project-lite"
version = "0.2.6"
name = "pin-utils"
version = "0.1.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
[[package]]
name = "pin-utils"
version = "0.1.0"
name = "proc-macro2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
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]]
name = "rlibc"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "scopeguard"
@@ -185,42 +260,79 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spinning_top"
version = "0.2.4"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c"
checksum = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24"
dependencies = [
"lock_api",
]
[[package]]
name = "uart_16550"
version = "0.2.14"
name = "syn"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "503a6c0e6d82daa87985e662d120c0176b09587c92a68db22781b28ae95405dd"
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]]
name = "uart_16550"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58fc40dc1712664fc9b0a7bd8ca2f21ab49960924fb245a80a05e1e92f3dfe9"
dependencies = [
"bitflags",
"x86_64",
]
[[package]]
name = "volatile"
version = "0.2.7"
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "volatile"
version = "0.4.4"
version = "0.3.0"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "x86_64"
version = "0.14.7"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb611915c917c6296d11e23f71ff1ecfe49c5766daba92cd3df52df6b58285b6"
checksum = "7c6e9604d08cf91ba0e5cc2175e8cbbdeeb2b8230b818fd9052604c4c5ffd620"
dependencies = [
"bit_field",
"bitflags",
"volatile 0.4.4",
]

View File

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

View File

@@ -1,6 +1,6 @@
# Blog OS (Async/Await)
[![Build Status](https://github.com/phil-opp/blog_os/workflows/Code/badge.svg?branch=post-12)](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Code%22+branch%3Apost-12)
[![Build Status](https://github.com/phil-opp/blog_os/workflows/Build%20Code/badge.svg?branch=post-12)](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Build+Code%22+branch%3Apost-12)
This repository contains the source code for the [Async/Await][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.

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(())
}

View File

@@ -31,9 +31,8 @@ pub struct FixedSizeBlockAllocator {
impl FixedSizeBlockAllocator {
/// Creates an empty FixedSizeBlockAllocator.
pub const fn new() -> Self {
const EMPTY: Option<&'static mut ListNode> = None;
FixedSizeBlockAllocator {
list_heads: [EMPTY; BLOCK_SIZES.len()],
list_heads: [None; BLOCK_SIZES.len()],
fallback_allocator: linked_list_allocator::Heap::empty(),
}
}

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

@@ -41,12 +41,12 @@ struct Selectors {
}
pub fn init() {
use x86_64::instructions::segmentation::{Segment, CS};
use x86_64::instructions::segmentation::set_cs;
use x86_64::instructions::tables::load_tss;
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
set_cs(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
}
}

View File

@@ -1,6 +1,6 @@
use crate::{gdt, hlt_loop, print, println};
use lazy_static::lazy_static;
use pic8259::ChainedPics;
use pic8259_simple::ChainedPics;
use spin;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
@@ -47,12 +47,12 @@ pub fn init_idt() {
IDT.load();
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn page_fault_handler(
stack_frame: InterruptStackFrame,
stack_frame: &mut InterruptStackFrame,
error_code: PageFaultErrorCode,
) {
use x86_64::registers::control::Cr2;
@@ -65,13 +65,13 @@ extern "x86-interrupt" fn page_fault_handler(
}
extern "x86-interrupt" fn double_fault_handler(
stack_frame: InterruptStackFrame,
stack_frame: &mut InterruptStackFrame,
_error_code: u64,
) -> ! {
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
print!(".");
unsafe {
PICS.lock()
@@ -79,7 +79,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr
}
}
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
use x86_64::instructions::port::Port;
let mut port = Port::new(0x60);

View File

@@ -2,20 +2,25 @@
#![cfg_attr(test, no_main)]
#![feature(custom_test_frameworks)]
#![feature(abi_x86_interrupt)]
#![feature(const_mut_refs)]
#![feature(alloc_error_handler)]
#![feature(const_fn)]
#![feature(const_in_array_repeat_expressions)]
#![feature(wake_trait)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
extern crate rlibc;
use core::panic::PanicInfo;
pub mod allocator;
pub mod framebuffer;
pub mod gdt;
pub mod interrupts;
pub mod memory;
pub mod serial;
pub mod task;
pub mod vga_buffer;
pub fn init() {
gdt::init();
@@ -94,3 +99,8 @@ fn test_kernel_main(_boot_info: &'static BootInfo) -> ! {
fn panic(info: &PanicInfo) -> ! {
test_panic_handler(info)
}
#[alloc_error_handler]
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
panic!("allocation error: {:?}", layout)
}

View File

@@ -13,17 +13,19 @@ use core::panic::PanicInfo;
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::memory::{self, BootInfoFrameAllocator};
use x86_64::VirtAddr;
blog_os::framebuffer::init(boot_info.framebuffer.as_mut().unwrap());
println!("Hello World{}", "!");
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 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");
@@ -40,6 +42,9 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
unsafe {
blog_os::framebuffer::WRITER.force_unlock();
};
println!("{}", info);
blog_os::hlt_loop();
}

View File

@@ -1,6 +1,8 @@
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
use bootloader::memory_map::{MemoryRegion, MemoryRegionKind};
use x86_64::{
structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB},
structures::paging::{
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
},
PhysAddr, VirtAddr,
};
@@ -33,6 +35,24 @@ unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut
&mut *page_table_ptr // unsafe
}
/// Creates an example mapping for the given page to frame `0xb8000`.
pub fn create_example_mapping(
page: Page,
mapper: &mut OffsetPageTable,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) {
use x86_64::structures::paging::PageTableFlags as Flags;
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
let flags = Flags::PRESENT | Flags::WRITABLE;
let map_to_result = unsafe {
// FIXME: this is not safe, we do it only for testing
mapper.map_to(page, frame, flags, frame_allocator)
};
map_to_result.expect("map_to failed").flush();
}
/// A FrameAllocator that always returns `None`.
pub struct EmptyFrameAllocator;
@@ -44,7 +64,7 @@ unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
/// A FrameAllocator that returns usable frames from the bootloader's memory map.
pub struct BootInfoFrameAllocator {
memory_map: &'static MemoryMap,
memory_map: &'static [MemoryRegion],
next: usize,
}
@@ -54,7 +74,7 @@ impl BootInfoFrameAllocator {
/// 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
/// 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 {
memory_map,
next: 0,
@@ -65,9 +85,9 @@ impl BootInfoFrameAllocator {
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
// get usable regions from memory map
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
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
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
// create `PhysFrame` types from the start addresses

View File

@@ -62,11 +62,11 @@ impl Executor {
}
fn sleep_if_idle(&self) {
use x86_64::instructions::interrupts::{self, enable_and_hlt};
use x86_64::instructions::interrupts::{self, enable_interrupts_and_hlt};
interrupts::disable();
if self.task_queue.is_empty() {
enable_and_hlt();
enable_interrupts_and_hlt();
} else {
interrupts::enable();
}

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;
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 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");
test_main();

View File

@@ -44,7 +44,7 @@ pub fn init_test_idt() {
}
extern "x86-interrupt" fn test_double_fault_handler(
_stack_frame: InterruptStackFrame,
_stack_frame: &mut InterruptStackFrame,
_error_code: u64,
) -> ! {
serial_println!("[ok]");