diff --git a/Cargo.lock b/Cargo.lock index 639a2a4..36412dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,6 +87,11 @@ dependencies = [ "syn", ] +[[package]] +name = "fdt" +version = "0.2.0-alpha1" +source = "git+https://github.com/repnop/fdt#059bb2383873f8001959456e36ec123228f67642" + [[package]] name = "flagset" version = "0.4.7" @@ -99,6 +104,7 @@ version = "0.3.0" dependencies = [ "acpi", "enumflags2", + "fdt", "flagset", "lazy_static", "limine", diff --git a/Cargo.toml b/Cargo.toml index ef5b213..d405af1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] acpi = "5.1.0" enumflags2 = "0.7.11" +fdt = { git = "https://github.com/repnop/fdt", version = "0.2.0-alpha1" } flagset = "0.4.7" lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } limine = "0.4.0" diff --git a/Makefile.toml b/Makefile.toml index 238f5f8..bbc81ec 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -30,6 +30,16 @@ PROFILE = "dev" PROFILEDIR = "debug" ARTIFACTDIR = "target/${TARGET}/${PROFILEDIR}" +[env.dev] +PROFILE = "dev" +PROFILEDIR = "debug" +ARTIFACTDIR = "target/${TARGET}/${PROFILEDIR}" + +[env.debug] +PROFILE = "dev" +PROFILEDIR = "debug" +ARTIFACTDIR = "target/${TARGET}/${PROFILEDIR}" + [env.release] PROFILE = "release" PROFILEDIR = "release" diff --git a/README.md b/README.md index 15c9c6f..f165903 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Licensed under the GNU Public License v3. See [LICENSE](LICENSE) for details. - [arch/](src/kernel/arch/): Architecture specific features like the display, serial, and interrupts. Each architecture is a subfolder, containing a file or module for each feature. - [boot.rs](src/kernel/boot.rs): Handles bootloader-managed data structures. Gila uses Limine. Other bootloaders are NOT supported. - [constants.rs](src/kernel/constants.rs): Constants referenced elsewhere in the kernel. + - [device.rs](src/kernel/device.rs): Functions for discovering hardware and assigning drivers. - [log.rs](src/kernel/log.rs): Logging structures, macros, and singletons for logging to serial or the display. - [main.rs](src/kernel/main.rs): The entry point that gets called by the bootloader. - [memory.rs](src/kernel/memory.rs): Types relating to memory regions and allocation. diff --git a/src/kernel/arch/x86_64/gdt.rs b/src/kernel/arch/x86_64/gdt.rs index 59ce8ca..6175a1b 100644 --- a/src/kernel/arch/x86_64/gdt.rs +++ b/src/kernel/arch/x86_64/gdt.rs @@ -4,6 +4,9 @@ use lazy_static::lazy_static; use super::asm::*; +use crate::{log_trace, format}; +use crate::log::{LogLevel, LOGGER}; + lazy_static! { pub static ref GDT: GlobalDescriptorTable = { let mut gdt = GlobalDescriptorTable::new(); @@ -17,6 +20,10 @@ pub fn setup_gdt() { interrupt_disable(); } GDT.load(); + log_trace!( + "GDT: Setting up global descriptor table at 0x{:x}", + core::ptr::addr_of!(GDT) as usize + ); unsafe { interrupt_enable(); } diff --git a/src/kernel/boot.rs b/src/kernel/boot.rs index 4f1610d..a68843b 100644 --- a/src/kernel/boot.rs +++ b/src/kernel/boot.rs @@ -35,3 +35,6 @@ pub static FRAMEBUFFER_REQUEST: FramebufferRequest = limine::request::Framebuffe #[used] #[unsafe(link_section = ".requests")] pub static MEMMAP_REQUEST: MemoryMapRequest = limine::request::MemoryMapRequest::new(); +#[used] +#[unsafe(link_section = ".requests")] +pub static DTB_REQUEST: DeviceTreeBlobRequest = limine::request::DeviceTreeBlobRequest::new(); diff --git a/src/kernel/device.rs b/src/kernel/device.rs new file mode 100644 index 0000000..1ae6d96 --- /dev/null +++ b/src/kernel/device.rs @@ -0,0 +1,24 @@ +use fdt::*; +use lazy_static::lazy_static; +use limine::response::{DeviceTreeBlobResponse, RsdpResponse}; + +use crate::boot::{DTB_REQUEST, RSDP_REQUEST}; +use crate::log::{LOGGER, LogLevel}; +use crate::{format, log_trace}; + +lazy_static! { + pub static ref DTB: Option<&'static DeviceTreeBlobResponse> = DTB_REQUEST.get_response(); + pub static ref RDSP: Option<&'static RsdpResponse> = RSDP_REQUEST.get_response(); +} + +#[allow(dead_code)] +pub fn device_tree_parsed() -> Option> { + if let Some(ok_dtb) = *DTB + && let Ok(ok_parsed_dt) = unsafe { fdt::Fdt::from_ptr(ok_dtb.dtb_ptr() as *const u8) } + { + log_trace!("Device: Parsed device tree"); + Some(ok_parsed_dt) + } else { + None + } +} diff --git a/src/kernel/log.rs b/src/kernel/log.rs index afe2c48..1135ad7 100644 --- a/src/kernel/log.rs +++ b/src/kernel/log.rs @@ -10,18 +10,21 @@ use alloc::string::*; use alloc::vec::*; use spin::Mutex; +#[macro_export] macro_rules! log_info { ($($arg:tt)*) => { LOGGER.log(LogLevel::Info, &format!($($arg)*)) }; } +#[macro_export] macro_rules! log_trace { ($($arg:tt)*) => { LOGGER.log(LogLevel::Trace, &format!($($arg)*)) }; } +#[macro_export] #[allow(unused_macros)] macro_rules! log_warning { ($($arg:tt)*) => { @@ -29,6 +32,7 @@ macro_rules! log_warning { }; } +#[macro_export] #[allow(unused_macros)] macro_rules! log_error { ($($arg:tt)*) => { @@ -36,6 +40,7 @@ macro_rules! log_error { }; } +#[macro_export] #[allow(unused_macros)] macro_rules! log_critical { ($($arg:tt)*) => { @@ -71,10 +76,10 @@ pub struct EnumParseError {} pub struct LoggerInner { pub level: LogLevel, - pub subscriber: Vec>>, + pub subscriber: Vec>>, } -pub trait LogSubscriber { +pub trait LogSubscriber: Send + Sync { fn write(&self, msg: &str); } diff --git a/src/kernel/main.rs b/src/kernel/main.rs index 8cd136f..8ef70a0 100644 --- a/src/kernel/main.rs +++ b/src/kernel/main.rs @@ -9,6 +9,7 @@ mod arch; mod boot; mod constants; +mod device; #[macro_use] mod log; mod memory; @@ -16,12 +17,12 @@ mod panic; mod params; mod process; mod resources; -mod syscall_runner; -use core::ptr; - -use arch::x86_64::gdt::{self, GDT}; +#[cfg(target_arch = "x86_64")] +use arch::x86_64::gdt; +#[cfg(target_arch = "x86_64")] use arch::x86_64::serial::Serialport; + use boot::*; use constants::*; use log::*; @@ -38,9 +39,6 @@ unsafe extern "C" fn main() -> ! { // Assert supported bootloader version assert!(BASE_REVISION.is_supported()); - // TODO: This is hacky and will not work on any other machine. - let qemu_serial = Serialport::new(0x3f8); - let executable_file_response = FILE_REQUEST .get_response() .expect("Bootloader did not return executable data"); @@ -61,7 +59,9 @@ unsafe extern "C" fn main() -> ! { // Append display console to log subs } if log_device_list.contains(&"serial") { - LOGGER.add_subscriber(qemu_serial); + // TODO: Set up device discovery + #[cfg(target_arch = "x86_64")] + LOGGER.add_subscriber(Serialport::new(0x3f8)); } log_trace!("Boot: Configured kernel logging devices"); } @@ -101,7 +101,17 @@ unsafe extern "C" fn main() -> ! { } } - let _rsdp_response = RSDP_REQUEST.get_response(); + // Try to obtain ACPI information first. If not available, fall back to + // flattened device tree. + let rsdp_response = RSDP_REQUEST.get_response(); + let dtb_response = DTB_REQUEST.get_response(); + if let Some(rdsp) = rsdp_response { + log_info!("Boot: RDSP response received"); + log_trace!("Boot: RDSP located at 0x{:x}", rdsp.address()); + } else if let Some(dtb) = dtb_response { + log_info!("Boot: DTB response received"); + log_trace!("Boot: DTB located at 0x{:x}", dtb.dtb_ptr() as usize) + } let module_response = MODULE_REQUEST .get_response() @@ -142,80 +152,80 @@ unsafe extern "C" fn main() -> ! { } .expect("initramfs not found in modules list"); - if let Some(mmap_response) = MEMMAP_REQUEST.get_response() { - log_info!("Boot: Memory map received"); - if !mmap_response.entries().is_empty() { - let mut log_msg: String = String::from("Boot: Memory map:"); - let mut usable: u64 = 0; - let mut reclaimable: u64 = 0; - let mut hardware: u64 = 0; - let mut unusable: u64 = 0; - for entry in mmap_response.entries() { - log_msg.push_str(&format!( - "\n\t\t0x{:x} bytes @ 0x{:x}: {}", - entry.length, - entry.base, - match entry.entry_type { - EntryType::ACPI_NVS => { - hardware += entry.length; - "ACPI (reserved)" - } - EntryType::ACPI_RECLAIMABLE => { - reclaimable += entry.length; - "ACPI (reclaimable)" - } - EntryType::BAD_MEMORY => { - unusable += entry.length; - "damaged/unusable" - } - EntryType::BOOTLOADER_RECLAIMABLE => { - reclaimable += entry.length; - "bootloader (reclaimable)" - } - #[allow(unreachable_patterns, deprecated)] - EntryType::EXECUTABLE_AND_MODULES | EntryType::KERNEL_AND_MODULES => { - unusable += entry.length; - "executable & modules" - } - EntryType::FRAMEBUFFER => { - hardware += entry.length; - "framebuffer" - } - EntryType::RESERVED => { - unusable += entry.length; - "reserved" - } - EntryType::USABLE => { - usable += entry.length; - "usable" - } - _ => "unidentified", + // Panic if this is absent. It's needed to set up the GDT and paging + let mmap_response = MEMMAP_REQUEST.get_response().expect("Bootloader did not supply memory map"); + log_info!("Boot: Memory map received"); + if !mmap_response.entries().is_empty() { + let mut log_msg: String = String::from("Boot: Memory map:"); + let mut usable: u64 = 0; + let mut reclaimable: u64 = 0; + let mut hardware: u64 = 0; + let mut unusable: u64 = 0; + for entry in mmap_response.entries() { + log_msg.push_str(&format!( + "\n\t\t0x{:x} bytes @ 0x{:x}: {}", + entry.length, + entry.base, + match entry.entry_type { + EntryType::ACPI_NVS => { + hardware += entry.length; + "ACPI (reserved)" } - )); - } - log_trace!("{log_msg}"); - let total = usable + reclaimable + hardware + unusable; - log_info!( - "Boot: Memory report:\n\t\tFree: {usable}\n\t\tAvailable: {reclaimable}\n\t\tUsable: {}\n\t\tHardware: {hardware}\n\t\tUnusable: {unusable}\n\t\tTotal: {}", - usable + reclaimable, - total - ); + EntryType::ACPI_RECLAIMABLE => { + reclaimable += entry.length; + "ACPI (reclaimable)" + } + EntryType::BAD_MEMORY => { + unusable += entry.length; + "damaged/unusable" + } + EntryType::BOOTLOADER_RECLAIMABLE => { + reclaimable += entry.length; + "bootloader (reclaimable)" + } + #[allow(unreachable_patterns, deprecated)] + EntryType::EXECUTABLE_AND_MODULES | EntryType::KERNEL_AND_MODULES => { + unusable += entry.length; + "executable & modules" + } + EntryType::FRAMEBUFFER => { + hardware += entry.length; + "framebuffer" + } + EntryType::RESERVED => { + unusable += entry.length; + "reserved" + } + EntryType::USABLE => { + usable += entry.length; + "usable" + } + _ => "unidentified", + } + )); } + log_trace!("{log_msg}"); + let total = usable + reclaimable + hardware + unusable; + log_info!( + "Boot: Memory report:\n\t\tFree: {usable}\n\t\tAvailable: {reclaimable}\n\t\tUsable: {}\n\t\tHardware: {hardware}\n\t\tUnusable: {unusable}\n\t\tTotal: {}", + usable + reclaimable, + total + ); + } else { + panic!("Memory map contains no entries"); } - log_trace!( - "GDT: Setting up global descriptor table at 0x{:x}", - ptr::addr_of!(GDT) as usize - ); + #[cfg(target_arch = "x86_64")] gdt::setup_gdt(); #[allow(unreachable_code)] loop { - for _i in 0..50000000 { + for _i in 0..50000000u64 { unsafe { core::arch::asm!("nop"); } } + core::hint::black_box(()); log_trace!("Heartbeat"); } }