This commit is contained in:
Gvidas Juknevičius 2024-06-10 23:57:38 +03:00
parent 6de42c3dba
commit 1b2dd2bfbe
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
21 changed files with 1066 additions and 31 deletions

View File

@ -1,6 +1,6 @@
[unstable]
bindeps = true
build-std = ["core", "compiler_builtins"]
build-std = ["core", "compiler_builtins", "alloc"]
build-std-features = ["compiler-builtins-mem"]
[build]

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "log"]
path = kernel/libs/log
url = https://github.com/rust-lang/log.git

242
Cargo.lock generated
View File

@ -2,6 +2,75 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"lazy_static",
"log",
"multiboot2",
"pc-keyboard",
"pic8259",
"spin 0.9.8",
"uart_16550",
"x86",
"x86_64",
]
[[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 = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.21"
@ -9,8 +78,177 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "poppin-kernel"
version = "0.1.0"
name = "multiboot2"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad7ef048d4783355163fd0c874aac3db54b919dc6a86dc29bb13f67308b114b0"
dependencies = [
"bitflags 2.5.0",
"derive_more",
"log",
"ptr_meta",
"uefi-raw",
]
[[package]]
name = "pc-keyboard"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed089a1fbffe3337a1a345501c981f1eb1e47e69de5a40e852433e12953c3174"
[[package]]
name = "pic8259"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62d9a86c292b165f757e47e7fd66855def189b2564609bc4203727b27c33db22"
dependencies = [
"x86_64",
]
[[package]]
name = "proc-macro2"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-cpuid"
version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "uart_16550"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dc00444796f6c71f47c85397a35e9c4dbf9901902ac02386940d178e2b78687"
dependencies = [
"bitflags 1.3.2",
"rustversion",
"x86",
]
[[package]]
name = "uefi-raw"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa8716f52e8cab8bcedfd5052388a0f263b69fe5cc2561548dc6a530678333c"
dependencies = [
"bitflags 2.5.0",
"ptr_meta",
"uguid",
]
[[package]]
name = "uguid"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "x86"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
dependencies = [
"bit_field",
"bitflags 1.3.2",
"raw-cpuid",
]
[[package]]
name = "x86_64"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bc79523af8abf92fb1a970c3e086c5a343f6bcc1a0eb890f575cbb3b45743df"
dependencies = [
"bit_field",
"bitflags 2.5.0",
"rustversion",
"volatile",
]

View File

@ -23,6 +23,9 @@ _start:
; load the 64-bit GDT
lgdt [gdt64.pointer]
mov dword [0xb8000], 0x2f4b2f4f
jmp gdt64.code:long_mode_start
; print `OK` to screen

View File

@ -46,7 +46,7 @@ function link(){
${BUILD_DIR}/multiboot.o \
${BUILD_DIR}/boot.o \
${BUILD_DIR}/long_mode.o \
${BUILD_DIR}/x86_64-unknown-none/release/libpoppin_kernel.a
${BUILD_DIR}/x86_64-unknown-none/release/libkernel.a
}
function check_mb() {
@ -67,7 +67,10 @@ function make_iso() {
}
function run_qemu() {
qemu-system-x86_64 -cdrom $ISO
qemu-system-x86_64 \
-cdrom $ISO \
-serial stdio \
-device VGA
}
function print_help() {

View File

@ -1,14 +1,22 @@
[package]
name = "poppin-kernel"
name = "kernel"
version = "0.1.0"
edition = "2021"
test=false
[lib]
name="poppin_kernel"
name="kernel"
test=false
crate-type=["staticlib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
log = "0.4.21"
multiboot2 = "0.20.2"
pc-keyboard = "0.7.0"
pic8259 = "0.11.0"
spin = "0.9.8"
uart_16550 = "0.3.0"
x86 = "0.52.0"
x86_64 = "0.15.1"

12
kernel/src/events/mod.rs Normal file
View File

@ -0,0 +1,12 @@
pub struct KernelEventHandler {
}
impl KernelEventHandler {
}

50
kernel/src/gdt/mod.rs Normal file
View File

@ -0,0 +1,50 @@
use core::ptr::addr_of;
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
use x86_64::VirtAddr;
use x86_64::structures::tss::TaskStateSegment;
use lazy_static::lazy_static;
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
lazy_static! {
static ref TSS: TaskStateSegment = {
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
const STACK_SIZE: usize = 4096 * 5;
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
let stack_start = VirtAddr::from_ptr(unsafe { addr_of!(STACK) });
let stack_end = stack_start + STACK_SIZE as u64;
stack_end
};
tss
};
}
lazy_static! {
static ref GDT: (GlobalDescriptorTable, Selectors) = {
let mut gdt = GlobalDescriptorTable::new();
let code_selector = gdt.append(Descriptor::kernel_code_segment());
let tss_selector = gdt.append(Descriptor::tss_segment(&TSS));
(gdt, Selectors { code_selector, tss_selector })
};
}
struct Selectors {
code_selector: SegmentSelector,
tss_selector: SegmentSelector,
}
pub fn init() {
log::debug!("GDT init");
use x86_64::instructions::tables::load_tss;
use x86_64::instructions::segmentation::{CS, Segment};
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
}
}

View File

@ -0,0 +1,31 @@
use x86_64::structures::idt::{InterruptStackFrame, PageFaultErrorCode};
use crate::interrupts::InterruptIndex;
mod ps2_keyboard;
pub use ps2_keyboard::ps2_kb_int;
pub extern "x86-interrupt" fn breakpoint(sf: InterruptStackFrame){
log::error!("EXCEPT: BREAKPOINT\n{:#?}", sf);
}
pub extern "x86-interrupt" fn timer_interrupt(_: InterruptStackFrame){
//log::debug!("INT: TIMER\n{:#?}", sf);
// print!(".");
unsafe {
super::PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer as u8);
}
}
pub extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, _: u64) -> ! {
panic!("EXCEPT: DOUBLE FAULT \n{:#?}", stack_frame);
}
pub extern "x86-interrupt" fn page_fault(stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
use x86_64::registers::control::Cr2;
log::error!("EXCEPT: PAGE FAULT");
log::debug!("Addr: {:?}", Cr2::read());
log::debug!("ErrCo: {:?}\n{:#?}", error_code, stack_frame);
crate::utils::hcf();
}

View File

@ -0,0 +1,35 @@
use x86_64::structures::idt::InterruptStackFrame;
use crate::interrupts::PICS;
pub extern "x86-interrupt" fn ps2_kb_int(_sf: InterruptStackFrame){
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;
lazy_static::lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> =
Mutex::new(Keyboard::new(ScancodeSet1::new(),
layouts::Us104Key, HandleControl::Ignore)
);
}
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
if false {
match key {
DecodedKey::Unicode(character) => print!("{}", character),
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(super::InterruptIndex::Keyboard as u8);
}
}

View File

@ -0,0 +1,48 @@
use pic8259::ChainedPics;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
mod handlers;
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
lazy_static::lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(handlers::breakpoint);
idt.page_fault.set_handler_fn(handlers::page_fault);
idt[InterruptIndex::Timer as u8]
.set_handler_fn(handlers::timer_interrupt);
idt[InterruptIndex::Keyboard as u8]
.set_handler_fn(handlers::ps2_kb_int);
unsafe {
idt.double_fault.set_handler_fn(handlers::double_fault)
.set_stack_index(crate::gdt::DOUBLE_FAULT_IST_INDEX);
}
idt
};
}
pub static PICS: spin::Mutex<ChainedPics> =
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard, // PS/2
}
pub fn init_idt() {
log::debug!("IDT init");
IDT.load();
unsafe { PICS.lock().initialize() };
x86_64::instructions::interrupts::enable();
}

View File

@ -1,20 +1,50 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
use multiboot2::BootInformationHeader;
use x86_64::registers::control::Cr3;
use crate::mem::FrameAllocator;
#[macro_use]
mod logger;
mod interrupts;
mod gdt;
mod utils;
mod events;
mod mem;
#[no_mangle]
extern "C" fn kmain() -> ! {
loop {
extern "C" fn kmain(mb2_info_addr: usize) -> ! {
logger::init(log::LevelFilter::Trace).unwrap();
gdt::init();
interrupts::init_idt();
let mem_info = mem::MemInfo::load(mb2_info_addr);
let mut alloc = mem::area_frame_alloc::AreaFrameAllocator::new(mem_info);
for i in 0.. {
if let None = alloc.allocate_frame() {
println!("allocated {} frames", i);
break;
}
}
// let (level_4_page_table, _) = Cr3::read();
// println!("Level 4 page table at: {:?}", level_4_page_table.start_address());
utils::hcf();
}
#[panic_handler]
fn panic(_pi: &core::panic::PanicInfo) -> ! {
loop {}
fn panic(pi: &core::panic::PanicInfo) -> ! {
log::error!("{pi}");
utils::hcf();
}

View File

@ -1,19 +0,0 @@
use log::{Record, Level, Metadata};
struct SimpleLogger {
}
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}

View File

87
kernel/src/logger/mod.rs Normal file
View File

@ -0,0 +1,87 @@
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
use crate::logger::serial::{write_com, ANSI_COLORS, COM0};
use self::vga::{Color, ColorCode, WRITER};
#[macro_use]
pub mod vga;
pub mod serial;
#[macro_use]
pub mod macros;
pub static LOGGER: KLogger = KLogger;
pub struct KLogger;
const C_TRACE: ColorCode = ColorCode::new(Color::Cyan, Color::Black);
const C_DEBUG: ColorCode = ColorCode::new(Color::Blue, Color::Black);
const C_INFO: ColorCode = ColorCode::new(Color::Green, Color::Black);
const C_WARN: ColorCode = ColorCode::new(Color::Yellow, Color::Black);
const C_ERROR: ColorCode = ColorCode::new(Color::Red, Color::Black);
const C_NORMAL: ColorCode = ColorCode::new(Color::White, Color::Black);
impl log::Log for KLogger {
fn enabled(&self, _: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
{WRITER.lock().set_color(Color::Magenta, Color::Black); }
print!("[");
write_com(&COM0, format_args!("{}[", ANSI_COLORS::MAGENTA));
match record.level() {
Level::Trace => {
{WRITER.lock().set_color_code(C_TRACE); }
print!("TRACE");
write_com(&COM0, format_args!("{}TRACE", ANSI_COLORS::BLUE2));
}
Level::Debug => {
{WRITER.lock().set_color_code(C_DEBUG); }
print!("DEBUG");
write_com(&COM0, format_args!("{}DEBUG", ANSI_COLORS::BLUE));
}
Level::Info => {
{WRITER.lock().set_color_code(C_INFO); }
print!("INFO");
write_com(&COM0, format_args!("{}INFO", ANSI_COLORS::GREEN));
}
Level::Warn => {
{WRITER.lock().set_color_code(C_WARN); }
print!("WARN");
write_com(&COM0, format_args!("{}WARN", ANSI_COLORS::YELLOW));
}
Level::Error => {
{WRITER.lock().set_color_code(C_ERROR); }
print!("ERROR");
write_com(&COM0, format_args!("{}ERROR", ANSI_COLORS::RED));
}
}
{WRITER.lock().set_color(Color::Magenta, Color::Black); }
print!("]");
{WRITER.lock().set_color_code(C_NORMAL); }
write_com(&COM0, format_args!("{}]", ANSI_COLORS::MAGENTA));
println!(" {}: {}", record.module_path().unwrap_or(""), record.args());
write_com(&COM0, format_args!(" {}{}: {}\n", ANSI_COLORS::RESET, record.module_path().unwrap_or(""), record.args()));
}
}
fn flush(&self) {}
}
pub fn init(lvl: LevelFilter) -> Result<(), SetLoggerError> {
log::set_logger(&LOGGER)?;
log::set_max_level(lvl);
log::info!("Logger init OK");
Ok(())
}

View File

@ -0,0 +1,64 @@
use core::fmt::Write;
use spin::Mutex;
use uart_16550::SerialPort;
const COM_PORTS: [u16; 8] = [0x3F8, 0x2F8, 0x3E8, 0x2E8, 0x5F8, 0x4F8, 0x5E8, 0x4E8];
fn init_com_port(port_no: u16) -> Mutex<SerialPort> {
let mut p = unsafe {SerialPort::new(port_no)};
p.init();
Mutex::new(p)
}
lazy_static::lazy_static!(
pub static ref COM0: Mutex<SerialPort> = init_com_port(COM_PORTS[0]);
pub static ref COM1: Mutex<SerialPort> = init_com_port(COM_PORTS[1]);
pub static ref COM2: Mutex<SerialPort> = init_com_port(COM_PORTS[2]);
);
pub fn write_com(cp: &'static Mutex<SerialPort>, args: core::fmt::Arguments) {
use x86_64::instructions::interrupts::without_interrupts;
without_interrupts(|| {
cp.lock().write_fmt(args).unwrap();
})
}
#[allow(non_snake_case)]
#[allow(dead_code)]
pub mod ANSI_COLORS {
pub const RESET: &str = "\x1b[0m";
pub const BOLD: &str = "\x1b[1m";
pub const ITALIC: &str = "\x1b[3m";
pub const UNDERLINE: &str = "\x1b[4m";
pub const BLINK: &str = "\x1b[5m";
pub const BLINK2: &str = "\x1b[6m";
pub const SELECTED: &str = "\x1b[7m";
pub const BLACK: &str = "\x1b[30m";
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m";
pub const YELLOW: &str = "\x1b[33m";
pub const BLUE: &str = "\x1b[34m";
pub const MAGENTA: &str = "\x1b[35m";
pub const BEIGE: &str = "\x1b[36m";
pub const WHITE: &str = "\x1b[37m";
pub const BLACKBG: &str = "\x1b[40m";
pub const REDBG: &str = "\x1b[41m";
pub const GREENBG: &str = "\x1b[42m";
pub const YELLOWBG: &str = "\x1b[43m";
pub const BLUEBG: &str = "\x1b[44m";
pub const MAGENTABG: &str = "\x1b[45m";
pub const BEIGEBG: &str = "\x1b[46m";
pub const WHITEBG: &str = "\x1b[47m";
pub const GREY: &str = "\x1b[90m";
pub const RED2: &str = "\x1b[91m";
pub const GREEN2: &str = "\x1b[92m";
pub const YELLOW2: &str = "\x1b[93m";
pub const BLUE2: &str = "\x1b[94m";
pub const MAGENTA2: &str = "\x1b[95m";
pub const BEIGE2: &str = "\x1b[96m";
pub const WHITE2: &str = "\x1b[97m";
}

View File

@ -0,0 +1,30 @@
use core::fmt;
use x86_64::instructions::interrupts;
use crate::logger::vga::Writer;
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_string(s);
Ok(())
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::logger::vga::macros::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
interrupts::without_interrupts(|| {
crate::logger::vga::WRITER.lock().write_fmt(args).unwrap();
});
}

View File

@ -0,0 +1,199 @@
use x86::io::{outb, inb};
use spin::Mutex;
#[macro_use]
pub mod macros;
lazy_static::lazy_static!{
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer::new());
}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
#[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,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ColorCode(u8);
impl ColorCode {
pub const fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct VgaChar {
pub chr: u8,
pub color: ColorCode,
}
#[repr(transparent)]
pub struct Buffer {
chars: [[VgaChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
pub struct Writer {
col: usize,
line: usize,
buffer: &'static mut Buffer,
color: ColorCode
}
impl Writer {
pub fn new() -> Self {
let mut s = Self {
col: 0,
line: 0,
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
color: ColorCode::new(Color::White, Color::Black)
};
s.clear_screen();
s
}
fn write_byte(&mut self, chr: u8) {
match chr {
b'\n' => self.new_line(),
_ => {
if self.col >= BUFFER_WIDTH {
self.new_line();
}
self.buffer.chars[self.line][self.col] = VgaChar {
chr,
color: self.color,
};
self.col += 1;
}
}
}
pub fn write_string(&mut self, s: &str) {
for c in s.as_bytes() {
match c {
// printable ASCII byte or newline
0x20..=0x7e | b'\n' => self.write_byte(*c),
// not part of printable ASCII range
_ => self.write_byte(0xfe),
}
}
update_cursor(self.col as u16, self.line as u16);
}
#[allow(dead_code)]
pub fn set_color(&mut self, fg: Color, bg: Color) {
self.color = ColorCode::new(fg, bg);
}
pub fn set_color_code(&mut self, c: ColorCode) {
self.color = c;
}
pub fn clear_screen(&mut self) {
let color = ColorCode::new(Color::White, Color::Black);
for line in 0..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
self.buffer.chars[line][col] = VgaChar {
chr: 0,
color,
};
}
}
}
pub fn clear_line(&mut self, line: usize) {
let color = ColorCode::new(Color::White, Color::Black);
for col in 0..BUFFER_WIDTH {
self.buffer.chars[line][col] = VgaChar {
chr: 0,
color,
};
}
}
pub fn new_line(&mut self) {
if self.line >= BUFFER_HEIGHT - 1{
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let chr = self.buffer.chars[row][col];
self.buffer.chars[row - 1][col] = chr;
}
}
self.clear_line(BUFFER_HEIGHT - 1);
} else {
self.line += 1;
}
self.col = 0;
}
}
#[allow(dead_code)]
pub fn enable_cursor(cur_start: u8, cur_end: u8) {
unsafe {
outb(0x3D4, 0x0A);
outb(0x3D5, (inb(0x3D5) & 0xC0) | cur_start);
outb(0x3D4, 0x0B);
outb(0x3D5, (inb(0x3D5) & 0xE0) | cur_end);
}
}
#[allow(dead_code)]
pub fn disable_cursor() {
unsafe {
outb(0x3D4, 0x0A);
outb(0x3D5, 0x20);
}
}
pub fn update_cursor(x: u16, y: u16) {
unsafe {
let pos: u16 = y * ( BUFFER_WIDTH as u16) + x;
outb(0x3D4, 0x0F);
outb(0x3D5, (pos & 0xFF) as u8);
outb(0x3D4, 0x0E);
outb(0x3D5, ((pos >> 8 ) & 0xFF) as u8);
}
}
#[allow(dead_code)]
pub fn get_cursor_position() -> (u16, u16) {
let mut pos: u16 = 0;
unsafe {
outb(0x3D4, 0x0F);
pos |= inb(0x3D5) as u16;
outb(0x3D4, 0x0E);
pos |= (inb(0x3D5) as u16) << 8;
}
(pos / (BUFFER_WIDTH as u16), pos % (BUFFER_WIDTH as u16))
}

View File

@ -0,0 +1,86 @@
use core::slice::Iter;
use super::{Frame, FrameAllocator, MemInfo, MemoryAreaIter};
use multiboot2::MemoryArea;
pub struct AreaFrameAllocator<'a> {
next_free_frame: Frame,
current_area: Option<&'a MemoryArea>,
areas: MemoryAreaIter,
kernel_start: Frame,
kernel_end: Frame,
multiboot_start: Frame,
multiboot_end: Frame,
}
impl<'a> FrameAllocator for AreaFrameAllocator<'a> {
fn allocate_frame(&mut self) -> Option<Frame> {
if let Some(area) = self.current_area {
// "Clone" the frame to return it if it's free. Frame doesn't
// implement Clone, but we can construct an identical frame.
let frame = Frame{ number: self.next_free_frame.number };
// the last frame of the current area
let current_area_last_frame = {
let address = area.start_address() + area.size() - 1;
Frame::containing_address(address as usize)
};
if frame > current_area_last_frame {
// all frames of current area are used, switch to next area
self.choose_next_area();
} else if frame >= self.kernel_start && frame <= self.kernel_end {
// `frame` is used by the kernel
self.next_free_frame = Frame {
number: self.kernel_end.number + 1
};
} else if frame >= self.multiboot_start && frame <= self.multiboot_end {
// `frame` is used by the multiboot information structure
self.next_free_frame = Frame {
number: self.multiboot_end.number + 1
};
} else {
// frame is unused, increment `next_free_frame` and return it
self.next_free_frame.number += 1;
return Some(frame);
}
// `frame` was not valid, try it again with the updated `next_free_frame`
self.allocate_frame()
} else {
None // no free frames left
}
}
fn deallocate_frame(&mut self, _: Frame) {
unimplemented!()
}
}
impl<'a> AreaFrameAllocator<'a> {
fn choose_next_area(&mut self) {
self.current_area = self.areas.clone().filter(|area| {
let address = area.start_address() + area.size() - 1;
Frame::containing_address(address as usize) >= self.next_free_frame
}).min_by_key(|area| area.start_address());
if let Some(area) = self.current_area {
let start_frame = Frame::containing_address(area.start_address() as usize);
if self.next_free_frame < start_frame {
self.next_free_frame = start_frame;
}
}
}
pub fn new(mi: MemInfo) -> AreaFrameAllocator<'a> {
let mut allocator = AreaFrameAllocator {
next_free_frame: Frame::containing_address(0),
current_area: None,
areas: mi.mem_area_iter,
kernel_start: Frame::containing_address(mi.kernel_start),
kernel_end: Frame::containing_address(mi.kernel_end),
multiboot_start: Frame::containing_address(mi.mb_start),
multiboot_end: Frame::containing_address(mi.mb_end),
};
allocator.choose_next_area();
allocator
}
}

115
kernel/src/mem/mod.rs Normal file
View File

@ -0,0 +1,115 @@
use core::{alloc::{GlobalAlloc, Layout}, ops::Deref};
use multiboot2::{BootInformation, BootInformationHeader, MemoryArea, MemoryAreaType, MemoryMapTag, TagTrait};
pub mod area_frame_alloc;
#[global_allocator]
static GLOBAL_ALLOCATOR: DummyAlloc = DummyAlloc;
pub struct DummyAlloc;
unsafe impl GlobalAlloc for DummyAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 0 as *mut u8 }
unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {}
}
pub const PAGE_SIZE: usize = 4096;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame {
number: usize,
}
impl Frame {
fn containing_address(address: usize) -> Frame {
Frame{ number: address / PAGE_SIZE }
}
}
pub trait FrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame>;
fn deallocate_frame(&mut self, frame: Frame);
}
#[derive(Clone, Debug)]
pub struct MemoryAreaIter {
current_area: *const MemoryArea,
last_area: *const MemoryArea,
entry_size: u32,
}
impl Iterator for MemoryAreaIter {
type Item = &'static MemoryArea;
fn next(&mut self) -> Option<&'static MemoryArea> {
if self.current_area > self.last_area {
None
} else {
let area = unsafe{&*self.current_area};
self.current_area = ((self.current_area as u32) + self.entry_size)
as *const MemoryArea;
if area.typ() == MemoryAreaType::Available {
Some(area)
} else {self.next()}
}
}
}
impl MemoryAreaIter {
pub fn new(mmap_tag: &MemoryMapTag) -> Self {
let self_ptr = mmap_tag as *const MemoryMapTag;
let start_area = (mmap_tag.memory_areas().first().unwrap()) as *const MemoryArea;
MemoryAreaIter {
current_area: start_area,
last_area: ((self_ptr as *const () as usize) + mmap_tag.size() - mmap_tag.entry_size() as usize) as *const MemoryArea,
entry_size: mmap_tag.entry_size(),
}
}
}
#[derive(Debug, Clone)]
pub struct MemInfo{
mem_area_iter: MemoryAreaIter,
pub kernel_start: usize,
pub kernel_end: usize,
pub mb_start: usize,
pub mb_end: usize,
}
impl MemInfo {
pub fn load(mb_ptr: usize) -> Self {
let boot_info = unsafe {
multiboot2::BootInformation::load(mb_ptr as *const BootInformationHeader).unwrap()
};
let mmap_tag = boot_info.memory_map_tag().unwrap().clone();
// log::debug!("Memory areas:");
// for area in mmap_tag.memory_areas() {
// log::debug!(" start: 0x{:x}, sz: 0x{:x}", area.start_address(), area.size());
// }
// log::debug!("kernel sections:");
// for section in boot_info.elf_sections().unwrap() {
// log::debug!("{}: start: 0x{:x}, sz: 0x{:x}, flags: 0b{:b}", section.name().unwrap_or("NONE"),
// section.start_address(), section.size(), section.flags());
// }
let kernel_start = boot_info.elf_sections().unwrap().clone().map(|s| s.start_address()).min().unwrap() as usize;
let kernel_end = boot_info.elf_sections().unwrap().clone().map(|s| s.end_address()).max().unwrap() as usize;
let mb_start = boot_info.start_address();
let mb_end = boot_info.end_address();
//log::debug!("Kernel: start: 0x{:x} sz: 0x{:x}", mi.kernel_start, mi.kernel_end - mi.kernel_start);
//log::debug!("Multiboot: start: 0x{:x} sz: 0x{:x}", mi.mb_start, mi.mb_end - mi.mb_start);
Self {
mem_area_iter: MemoryAreaIter::new(mmap_tag),
kernel_start,
kernel_end,
mb_start,
mb_end,
}
}
}

12
kernel/src/utils.rs Normal file
View File

@ -0,0 +1,12 @@
use x86_64::instructions;
pub fn hcf() -> ! {
loop {
instructions::hlt();
core::hint::spin_loop();
}
}