Merge pull request #5 from sdww0/main

boot and irq
This commit is contained in:
Tate, Hongliang Tian 2022-08-16 19:16:42 -07:00 committed by GitHub
commit 2142d69a60
17 changed files with 636 additions and 24 deletions

8
src/.cargo/config.toml Normal file
View File

@ -0,0 +1,8 @@
[target.'cfg(target_os = "none")']
runner = "cargo run --package kxos-boot --"
[alias]
kbuild = "build --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"
kimage = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem -- --no-run"
krun = "run --target x86_64-custom.json -Zbuild-std=core,alloc,compiler_builtins -Zbuild-std-features=compiler-builtins-mem"

115
src/Cargo.lock generated
View File

@ -2,23 +2,138 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bootloader"
version = "0.10.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d9b14b92a825ecc3b24e4c163a578af473fbba5f190bfaf48092b29b604504"
[[package]]
name = "bootloader-locator"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaaa9db3339d32c2622f2e5d0731eb82a468d3439797c9d4fe426744fe2bd551"
dependencies = [
"json",
]
[[package]]
name = "buddy_system_allocator"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4e85e760e105b46ae0bd1236578793c6c147ae7463fe95c8350296b8bfcb830"
dependencies = [
"spin 0.7.1",
]
[[package]]
name = "font8x8"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]]
name = "kxos"
version = "0.1.0"
dependencies = [
"bootloader",
"kxos-frame",
]
[[package]]
name = "kxos-boot"
version = "0.1.0"
dependencies = [
"bootloader-locator",
"locate-cargo-manifest",
]
[[package]]
name = "kxos-frame"
version = "0.1.0"
dependencies = [
"bitflags",
"bootloader",
"buddy_system_allocator",
"font8x8",
"lazy_static",
"spin 0.5.2",
"volatile",
"x86_64",
]
[[package]]
name = "kxos-std"
version = "0.1.0"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin 0.5.2",
]
[[package]]
name = "locate-cargo-manifest"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db985b63431fe09e8d71f50aeceffcc31e720cb86be8dad2f38d084c5a328466"
dependencies = [
"json",
]
[[package]]
name = "rustversion"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
[[package]]
name = "volatile"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c"
[[package]]
name = "x86_64"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69"
dependencies = [
"bit_field",
"bitflags",
"rustversion",
"volatile",
]

View File

@ -1,7 +1,22 @@
[package]
name = "kxos"
version = "0.1.0"
edition = "2021"
[dependencies]
bootloader = {version="0.10.12"}
kxos-frame = {path = "kxos-frame"}
[workspace]
members = [
"kxos",
"kxos-frame",
"kxos-std",
"kxos-boot",
]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"

9
src/kxos-boot/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "kxos-boot"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bootloader-locator = "0.0.4" # for locating the `bootloader` dependency on disk
locate-cargo-manifest = "0.2.0" # for locating the kernel's `Cargo.toml`

80
src/kxos-boot/src/main.rs Normal file
View File

@ -0,0 +1,80 @@
use std::{
path::{Path, PathBuf},
process::Command,
};
const RUN_ARGS: &[&str] = &["--no-reboot", "-s"];
fn main() {
let mut args = std::env::args().skip(1); // skip executable name
let kernel_binary_path = {
let path = PathBuf::from(args.next().unwrap());
path.canonicalize().unwrap()
};
println!("{:?}", kernel_binary_path);
let no_boot = if let Some(arg) = args.next() {
match arg.as_str() {
"--no-run" => true,
other => panic!("unexpected argument `{}`", other),
}
} else {
false
};
let bios = create_disk_images(&kernel_binary_path);
if no_boot {
println!("Created disk image at `{}`", bios.display());
return;
}
let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd
.arg("-drive")
.arg(format!("format=raw,file={}", bios.display()));
run_cmd.args(RUN_ARGS);
let exit_status = run_cmd.status().unwrap();
if !exit_status.success() {
std::process::exit(exit_status.code().unwrap_or(1));
}
}
pub fn create_disk_images(kernel_binary_path: &Path) -> PathBuf {
let bootloader_manifest_path = bootloader_locator::locate_bootloader("bootloader").unwrap();
let kernel_manifest_path = locate_cargo_manifest::locate_manifest().unwrap();
println!("{:?}", kernel_manifest_path);
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 !build_cmd.status().unwrap().success() {
panic!("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!("boot-bios-{}.img", kernel_binary_name));
if !disk_image.exists() {
panic!(
"Disk image does not exist at {} after bootloader build",
disk_image.display()
);
}
disk_image
}

View File

@ -7,3 +7,13 @@ edition = "2021"
[dependencies]
bitflags = "1.3"
x86_64 = "0.14.2"
spin = "0.5.2"
volatile = {version="0.4.5", features = ["unstable"] }
buddy_system_allocator = "0.6"
bootloader = "0.10.12"
font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"]}
[dependencies.lazy_static]
version = "1.0"
features = ["spin_no_std"]

View File

@ -0,0 +1,20 @@
use crate::config::KERNEL_HEAP_SIZE;
use buddy_system_allocator::LockedHeap;
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
#[alloc_error_handler]
pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
panic!("Heap allocation error, layout = {:?}", layout);
}
static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
pub fn init_heap() {
unsafe {
HEAP_ALLOCATOR
.lock()
.init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE);
}
}

View File

@ -0,0 +1,5 @@
mod buddy_system_allocator;
pub fn init() {
buddy_system_allocator::init_heap();
}

View File

@ -0,0 +1,14 @@
#![allow(unused)]
pub const USER_STACK_SIZE: usize = 4096 * 2;
pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
pub const KERNEL_HEAP_SIZE: usize = 0x20_0000;
pub const PAGE_SIZE: usize = 0x1000;
pub const PAGE_SIZE_BITS: usize = 0xc;
pub const MEM_START: usize = 0x8000_0000;
pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE;
pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS;

View File

@ -0,0 +1,151 @@
use bootloader::boot_info::PixelFormat;
use core::fmt;
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_mut()),
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::device::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();
});
}

View File

@ -1,7 +1,100 @@
use crate::prelude::*;
/// An interupt request (IRQ) line.
pub struct IrqLine {}
use alloc::collections::{linked_list::CursorMut, LinkedList};
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::{
set_general_handler,
structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue},
};
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
set_general_handler!(&mut idt, my_general_hander);
idt
};
}
lazy_static! {
static ref IRQ_LIST: Vec<IrqLine> = {
let mut list: Vec<IrqLine> = Vec::new();
for i in 0..256 {
list.push(IrqLine {
irq_num: i as u8,
callback_list: Mutex::new(Vec::new()),
});
}
list
};
}
lazy_static! {
static ref ID_ALLOCATOR: Mutex<RecycleAllocator> = Mutex::new(RecycleAllocator::new());
}
pub fn init() {
IDT.load();
}
fn my_general_hander(stack_frame: InterruptStackFrame, index: u8, error_code: Option<u64>) {
let irq_line = IRQ_LIST.get(index as usize).unwrap();
let callback_functions = irq_line.callback_list.lock();
for callback_function in callback_functions.iter() {
callback_function.function.call((InterruptInformation {
interrupt_stack_frame: *stack_frame,
error_code,
},));
}
}
struct RecycleAllocator {
current: usize,
recycled: Vec<usize>,
}
impl RecycleAllocator {
pub fn new() -> Self {
RecycleAllocator {
current: 0,
recycled: Vec::new(),
}
}
#[allow(unused)]
pub fn alloc(&mut self) -> usize {
if let Some(id) = self.recycled.pop() {
id
} else {
self.current += 1;
self.current - 1
}
}
#[allow(unused)]
pub fn dealloc(&mut self, id: usize) {
assert!(id < self.current);
assert!(
!self.recycled.iter().any(|i| *i == id),
"id {} has been deallocated!",
id
);
self.recycled.push(id);
}
}
struct CallbackElement {
function: Box<dyn Fn(InterruptInformation) + Send + Sync + 'static>,
id: usize,
}
/// An interrupt request (IRQ) line.
pub struct IrqLine {
irq_num: u8,
callback_list: Mutex<Vec<CallbackElement>>,
}
#[derive(Debug)]
pub struct InterruptInformation {
pub interrupt_stack_frame: InterruptStackFrameValue,
pub error_code: Option<u64>,
}
impl IrqLine {
/// Acquire an interrupt request line.
@ -10,13 +103,13 @@ impl IrqLine {
///
/// This function is marked unsafe as manipulating interrupt lines is
/// considered a dangerous operation.
pub unsafe fn acquire(irq_num: u32) -> Arc<Self> {
todo!()
pub unsafe fn acquire(irq_num: u8) -> Arc<&'static Self> {
Arc::new(IRQ_LIST.get(irq_num as usize).unwrap())
}
/// Get the IRQ number.
pub fn num(&self) -> u32 {
todo!()
pub fn num(&self) -> u8 {
self.irq_num
}
/// Register a callback that will be invoked when the IRQ is active.
@ -27,9 +120,17 @@ impl IrqLine {
/// For each IRQ line, multiple callbacks may be registered.
pub fn on_active<F>(&self, callback: F) -> IrqCallbackHandle
where
F: Fn(&Self),
F: Fn(InterruptInformation) + Sync + Send + 'static,
{
todo!()
let allocate_id = ID_ALLOCATOR.lock().alloc();
self.callback_list.lock().push(CallbackElement {
function: Box::new(callback),
id: allocate_id,
});
IrqCallbackHandle {
irq_num: self.irq_num,
id: allocate_id,
}
}
}
@ -37,10 +138,20 @@ impl IrqLine {
///
/// When the handle is dropped, the callback will be unregistered automatically.
#[must_use]
pub struct IrqCallbackHandle {}
pub struct IrqCallbackHandle {
irq_num: u8,
id: usize,
// cursor: CursorMut<'a, Box<dyn Fn(&IrqLine)+Sync+Send+'static>>
}
impl Drop for IrqCallbackHandle {
fn drop(&mut self) {
todo!("unregister the callback")
let mut a = IRQ_LIST
.get(self.irq_num as usize)
.unwrap()
.callback_list
.lock();
a.retain(|item| if (*item).id == self.id { false } else { true });
ID_ALLOCATOR.lock().dealloc(self.id);
}
}

View File

@ -1,7 +1,15 @@
//! Device-related APIs.
pub mod framebuffer;
mod io_port;
mod irq;
use bootloader::BootInfo;
pub use self::io_port::IoPort;
pub use self::irq::{IrqCallbackHandle, IrqLine};
pub use self::irq::{InterruptInformation, IrqCallbackHandle, IrqLine};
pub fn init(boot_info: &'static mut BootInfo) {
framebuffer::init(boot_info.framebuffer.as_mut().unwrap());
irq::init();
}

View File

@ -3,9 +3,15 @@
#![allow(dead_code)]
#![allow(unused_variables)]
#![feature(negative_impls)]
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]
#![feature(fn_traits)]
#![feature(linked_list_cursors)]
extern crate alloc;
mod allocator;
pub mod config;
pub mod cpu;
pub mod device;
mod error;
@ -17,3 +23,29 @@ mod util;
pub mod vm;
pub use self::error::Error;
use alloc::sync::Arc;
use bootloader::BootInfo;
use device::{InterruptInformation, IrqCallbackHandle, IrqLine};
static mut STORE: Option<IrqCallbackHandle> = None;
pub fn init(boot_info: &'static mut BootInfo) {
allocator::init();
device::init(boot_info);
device::framebuffer::WRITER.lock().as_mut().unwrap().clear();
// breakpoint
let breakpoint_irq: Arc<&IrqLine>;
unsafe {
breakpoint_irq = IrqLine::acquire(3);
}
let a = breakpoint_irq.on_active(breakpoint_handler);
let b = breakpoint_irq.on_active(breakpoint_handler);
unsafe {
STORE = Some(a);
}
x86_64::instructions::interrupts::int3(); // new
}
fn breakpoint_handler(interrupt_information: InterruptInformation) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", interrupt_information);
}

View File

@ -1,8 +0,0 @@
[package]
name = "kxos"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

30
src/src/main.rs Normal file
View File

@ -0,0 +1,30 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![forbid(unsafe_code)]
extern crate kxos_frame;
use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo;
use kxos_frame::println;
entry_point!(kernel_main);
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
// turn the screen gray
// if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
// for byte in framebuffer.buffer_mut() {
// *byte = 0x00;
// }
// }
kxos_frame::init(boot_info);
println!("finish init kxos_frame");
loop {}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}

15
src/x86_64-custom.json Normal file
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"
}