diff --git a/.cargo/config.toml b/.cargo/config.toml index 2610a48..9f62278 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -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] diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4b66b11 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "log"] + path = kernel/libs/log + url = https://github.com/rust-lang/log.git diff --git a/Cargo.lock b/Cargo.lock index d607f21..eb3ac1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/boot/boot.s b/boot/boot.s index b41f2ef..9034282 100644 --- a/boot/boot.s +++ b/boot/boot.s @@ -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 diff --git a/build.sh b/build.sh index 2044fb2..d998715 100755 --- a/build.sh +++ b/build.sh @@ -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() { diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index e9a1263..354aebe 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -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" diff --git a/kernel/src/events/mod.rs b/kernel/src/events/mod.rs new file mode 100644 index 0000000..bb97166 --- /dev/null +++ b/kernel/src/events/mod.rs @@ -0,0 +1,12 @@ + + + +pub struct KernelEventHandler { + + +} + +impl KernelEventHandler { + + +} diff --git a/kernel/src/gdt/mod.rs b/kernel/src/gdt/mod.rs new file mode 100644 index 0000000..c8d1fbe --- /dev/null +++ b/kernel/src/gdt/mod.rs @@ -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); + } +} diff --git a/kernel/src/interrupts/handlers/mod.rs b/kernel/src/interrupts/handlers/mod.rs new file mode 100644 index 0000000..95b0134 --- /dev/null +++ b/kernel/src/interrupts/handlers/mod.rs @@ -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(); +} diff --git a/kernel/src/interrupts/handlers/ps2_keyboard.rs b/kernel/src/interrupts/handlers/ps2_keyboard.rs new file mode 100644 index 0000000..7f82c66 --- /dev/null +++ b/kernel/src/interrupts/handlers/ps2_keyboard.rs @@ -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> = + 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); + } +} diff --git a/kernel/src/interrupts/mod.rs b/kernel/src/interrupts/mod.rs new file mode 100644 index 0000000..a54e618 --- /dev/null +++ b/kernel/src/interrupts/mod.rs @@ -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 = + 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(); +} + + diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 99afa80..811d06a 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -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(); } diff --git a/kernel/src/logger.rs b/kernel/src/logger.rs deleted file mode 100644 index 6200b74..0000000 --- a/kernel/src/logger.rs +++ /dev/null @@ -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) {} -} diff --git a/kernel/src/logger/macros.rs b/kernel/src/logger/macros.rs new file mode 100644 index 0000000..e69de29 diff --git a/kernel/src/logger/mod.rs b/kernel/src/logger/mod.rs new file mode 100644 index 0000000..42827b8 --- /dev/null +++ b/kernel/src/logger/mod.rs @@ -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(()) +} + diff --git a/kernel/src/logger/serial.rs b/kernel/src/logger/serial.rs new file mode 100644 index 0000000..c425cb2 --- /dev/null +++ b/kernel/src/logger/serial.rs @@ -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 { + let mut p = unsafe {SerialPort::new(port_no)}; + p.init(); + Mutex::new(p) +} + +lazy_static::lazy_static!( + pub static ref COM0: Mutex = init_com_port(COM_PORTS[0]); + pub static ref COM1: Mutex = init_com_port(COM_PORTS[1]); + pub static ref COM2: Mutex = init_com_port(COM_PORTS[2]); +); + +pub fn write_com(cp: &'static Mutex, 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"; +} diff --git a/kernel/src/logger/vga/macros.rs b/kernel/src/logger/vga/macros.rs new file mode 100644 index 0000000..c1861cf --- /dev/null +++ b/kernel/src/logger/vga/macros.rs @@ -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(); + }); +} diff --git a/kernel/src/logger/vga/mod.rs b/kernel/src/logger/vga/mod.rs new file mode 100644 index 0000000..77cab02 --- /dev/null +++ b/kernel/src/logger/vga/mod.rs @@ -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 = 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)) +} diff --git a/kernel/src/mem/area_frame_alloc.rs b/kernel/src/mem/area_frame_alloc.rs new file mode 100644 index 0000000..f210bb3 --- /dev/null +++ b/kernel/src/mem/area_frame_alloc.rs @@ -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 { + 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 + } +} diff --git a/kernel/src/mem/mod.rs b/kernel/src/mem/mod.rs new file mode 100644 index 0000000..3d1a737 --- /dev/null +++ b/kernel/src/mem/mod.rs @@ -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; + 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, + } + } +} + diff --git a/kernel/src/utils.rs b/kernel/src/utils.rs new file mode 100644 index 0000000..4df881e --- /dev/null +++ b/kernel/src/utils.rs @@ -0,0 +1,12 @@ +use x86_64::instructions; + + + +pub fn hcf() -> ! { + loop { + instructions::hlt(); + core::hint::spin_loop(); + } + + +}