diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8f11a11..c7c29c4 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -17,6 +17,10 @@ panic = "abort" name = "entrypoint" path = "src/internal/arch/x86/entry.rs" +[[bin]] +name = "main" +path = "src/internal/main.rs" + [lib] name = "aphrodite" path = "src/include/mod.rs" diff --git a/kernel/build.rs b/kernel/build.rs index 3d472c1..6d198a5 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -4,8 +4,8 @@ fn main() { // Begin checks println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_DISABLE_MULTIBOOT2_SUPPORT, values("true", "false", none()))"#); - println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_HALT_ON_PANIC, values("true", "false", none()))"#); - println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_SPIN_ON_PANIC, values("true", "false", none()))"#); + println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_HALT_ON_PANIC, values("true", "false", none()))"#); + println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_SPIN_ON_PANIC, values("true", "false", none()))"#); println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH, values("true", "false", none()))"#); println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH, values("true", "false", none()))"#); diff --git a/kernel/config.aphro.example b/kernel/config.aphro.example index 387c61c..adde185 100644 --- a/kernel/config.aphro.example +++ b/kernel/config.aphro.example @@ -1,5 +1,5 @@ -# config.aphro for aphrodite devel-2df6e24-out-of-tree -CFG_VERSION=devel-2df6e24-out-of-tree +# config.aphro for aphrodite devel-6a2a677-out-of-tree +CFG_VERSION=devel-6a2a677-out-of-tree CONT_WITH_DIFFERENT_VERSION=false # Begin metadata @@ -14,8 +14,8 @@ VERSION=generate CONFIG_DISABLE_MULTIBOOT2_SUPPORT=false # Panic behavior. When debugging, generally halt on panic is more useful. -CONFIG_PREUSER_HALT_ON_PANIC=true -CONFIG_PREUSER_SPIN_ON_PANIC=false +CONFIG_HALT_ON_PANIC=true +CONFIG_SPIN_ON_PANIC=false CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH=true CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH=false diff --git a/kernel/grub/boot/aphrodite.kernel b/kernel/grub/boot/aphrodite.kernel index 048f87c..88044a2 100755 Binary files a/kernel/grub/boot/aphrodite.kernel and b/kernel/grub/boot/aphrodite.kernel differ diff --git a/kernel/grub/boot/grub/grub.cfg b/kernel/grub/boot/grub/grub.cfg index ba863f4..b4d0af5 100644 --- a/kernel/grub/boot/grub/grub.cfg +++ b/kernel/grub/boot/grub/grub.cfg @@ -1,8 +1,8 @@ set timeout=15 set default=0 -menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-devel-2df6e24-out-of-tree' { - echo 'Loading Aphrodite aphrodite-devel-2df6e24-out-of-tree ...' +menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-devel-6a2a677-out-of-tree' { + echo 'Loading Aphrodite aphrodite-devel-6a2a677-out-of-tree ...' multiboot2 /boot/aphrodite.kernel boot } \ No newline at end of file diff --git a/kernel/kernel.flat b/kernel/kernel.flat index 048f87c..88044a2 100755 Binary files a/kernel/kernel.flat and b/kernel/kernel.flat differ diff --git a/kernel/src/include/_entry.rs b/kernel/src/include/_entry.rs new file mode 100644 index 0000000..fbdf07e --- /dev/null +++ b/kernel/src/include/_entry.rs @@ -0,0 +1,115 @@ +//! The main code for the kernel. +#![warn(missing_docs)] +#![allow(unexpected_cfgs)] +#![allow(static_mut_refs)] + +use crate::arch::x86::output::*; +use crate::arch::x86::egatext as egatext; +use crate::multiboot2::BootInfo; +use egatext::*; + +/// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files eventually call this. +#[allow(non_snake_case)] +pub fn _entry(ega: Option, BI: &BootInfo) -> ! { + if ega.is_some() { + let ega = ega.unwrap(); + ega.clear_screen(WHITE_ON_BLACK); + sreset(); + + let extended_functions = crate::arch::x86::cpuid_extended_functions(); + + if extended_functions { + binfosln("This CPU supports extended functions", ega).unwrap(); + + let longmode_support = crate::arch::x86::cpuid(0x80000001).1 & (1<<29) > 1; + if longmode_support { + binfosln("This CPU supports long mode", ega).unwrap(); + } else { + binfosln("This CPU does NOT support long mode!", ega).unwrap(); + bdebugs("Long mode CPUID: ", ega).unwrap(); + bdebugbnpln(&crate::u32_as_u8_slice(crate::arch::x86::cpuid(0x80000001).1), ega).unwrap(); + } + } else { + binfosln("This CPU does NOT support extended functions or long mode!", ega).unwrap(); + } + if BI.bootloader_name.is_some() { + binfos("Kernel booted by ", ega).unwrap(); + binfosnpln(BI.bootloader_name.unwrap().into(), ega).unwrap(); + } + if BI.cmdline.is_some() { + binfos("Command line passed: \"", ega).unwrap(); + binfosnp(BI.cmdline.unwrap().into(), ega).unwrap(); + binfosnpln("\"", ega).unwrap(); + } + if BI.mem_lower.is_some() { + binfos("Amount of lower memory: ", ega).unwrap(); + binfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap()), ega).unwrap(); + } + if BI.mem_upper.is_some() { + binfos("Amount of upper memory: ", ega).unwrap(); + binfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap()), ega).unwrap(); + } + if BI.memory_map.is_some() { + binfos("Recieved memory map from bootloader with ", ega).unwrap(); + binfobnp(&crate::usize_as_u8_slice(BI.memory_map.unwrap().sections.len()), ega).unwrap(); + binfosnpln(" sections", ega).unwrap(); + let mut i = 0; + for ele in BI.memory_map.unwrap().sections { + binfos("Section #", ega).unwrap(); + binfobnp(&crate::usize_as_u8_slice(i), ega).unwrap(); + binfosnp(": ", ega).unwrap(); + match ele.mem_type { + 1 => { + binfosnp("Available RAM", ega).unwrap(); + }, + 2 => { + binfosnp("Reserved by hardware", ega).unwrap(); + } + 3 => { + binfosnp("ACPI information", ega).unwrap(); + }, + 4 => { + binfosnp("Reserved memory", ega).unwrap(); + }, + 5 => { + binfosnp("Defective", ega).unwrap(); + }, + _ => { + binfosnp("Reserved/unknown (type=", ega).unwrap(); + binfobnp(&crate::u32_as_u8_slice(ele.mem_type), ega).unwrap(); + binfosnp(")", ega).unwrap(); + } + } + binfosnp(", starting at ", ega).unwrap(); + binfobnp(&crate::u64_as_u8_slice(ele.base_addr), ega).unwrap(); + binfosnp(" and running for ", ega).unwrap(); + binfobnp(&crate::u64_as_u8_slice(ele.length), ega).unwrap(); + binfosnpln(" bytes", ega).unwrap(); + + i += 1; + } + } + } else { + if BI.bootloader_name.is_some() { + sinfos("Kernel booted by "); + sinfosnpln(BI.bootloader_name.unwrap().into()); + } + if BI.cmdline.is_some() { + sinfos("Command line passed: \""); + sinfosnp(BI.cmdline.unwrap().into()); + sinfosnpln("\""); + } + if BI.memory_map.is_some() { + sinfosln("Recieved memory map from bootloader"); + } + if BI.mem_lower.is_some() { + sinfos("Amount of lower memory: "); + sinfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap())); + } + if BI.mem_upper.is_some() { + sinfos("Amount of upper memory: "); + sinfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap())); + } + } + loop {} +} \ No newline at end of file diff --git a/kernel/src/include/arch/x86/egatext.rs b/kernel/src/include/arch/x86/egatext.rs index e97f0f2..9214fd3 100644 --- a/kernel/src/include/arch/x86/egatext.rs +++ b/kernel/src/include/arch/x86/egatext.rs @@ -14,6 +14,8 @@ pub struct FramebufferInfo { pub height: u32, /// Bits per pixel. pub bpp: u8, + /// Whether to change the cursor position after outputting text. + pub change_cursor: bool, } /// Returned when the provided position is invalid in the X direction. @@ -29,7 +31,7 @@ pub const BLACK_ON_BLACK: u8 = 0b00000000; impl FramebufferInfo { /// Writes a character to the screen. - pub fn write_char(self, pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> { + pub fn write_char(self, mut pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> { if pos.0>self.width { return Err(crate::Error::new("Invalid X position", ERR_INVALID_X)); } @@ -43,6 +45,10 @@ impl FramebufferInfo { let base_ptr = addr as *mut u16; (*base_ptr) = ((color as u16)<<8) | (char as u16); } + pos.1 += 1; + if self.change_cursor { + self.set_cursor_location(pos); + } Ok(()) } @@ -56,30 +62,52 @@ impl FramebufferInfo { } /// Writes a &str to the screen. - pub fn write_str(self, pos: (u32, u32), str: &str, color: u8) -> Result<(u32, u32), crate::Error<'static>> { + pub fn write_str(mut self, pos: (u32, u32), str: &str, color: u8) -> Result<(u32, u32), crate::Error<'static>> { let (mut x, mut y) = pos; + let change_cursor = self.change_cursor; + if change_cursor { + self.change_cursor = false; + } for char in str.as_bytes() { self.write_char((x, y), *char, color)?; + if *char == 0 { + continue + } x += 1; while x>self.width { x -= self.width; y += 1; } } + if change_cursor { + self.change_cursor = true; + self.set_cursor_location((x, y)); + } Ok((x, y)) } /// Writes a &\[u8] to the screen. - pub fn write_bytes(self, pos: (u32, u32), str: &[u8], color: u8) -> Result<(u32, u32), crate::Error<'static>> { + pub fn write_bytes(mut self, pos: (u32, u32), str: &[u8], color: u8) -> Result<(u32, u32), crate::Error<'static>> { let (mut x, mut y) = pos; + let change_cursor = self.change_cursor; + if change_cursor { + self.change_cursor = false; + } for char in str { self.write_char((x, y), *char, color)?; + if *char == 0 { + continue + } x += 1; while x>self.width { x -= self.width; y += 1; } } + if change_cursor { + self.change_cursor = true; + self.set_cursor_location((x, y)); + } Ok((x, y)) } @@ -107,4 +135,17 @@ impl FramebufferInfo { super::ports::outb(0x3D4, 0x0E); super::ports::outb(0x3D5, ((addr >> 8) & 0xFF) as u8); } + + /// Gets the cursor's location. + pub fn get_cursor_location(self) -> (u32, u32) { + let mut addr: u32 = 0; + + super::ports::outb(0x3D4, 0x0F); + addr |= super::ports::inb(0x3D5) as u32; + + super::ports::outb(0x3D4, 0x0E); + addr |= (super::ports::inb(0x3D5) as u32) << 8; + + return (addr % self.width, addr / self.width); + } } diff --git a/kernel/src/include/arch/x86/mod.rs b/kernel/src/include/arch/x86/mod.rs index 95d44b0..5079c8f 100644 --- a/kernel/src/include/arch/x86/mod.rs +++ b/kernel/src/include/arch/x86/mod.rs @@ -17,11 +17,22 @@ pub use constants::*; pub fn cpuid(id: u32) -> (u32, u32, u32) { let mut out = (0u32, 0u32, 0u32); unsafe { - // ebx is moved into eax as apparently ebx is used internally by LLVM asm!( - "cpuid", - "mov eax, ebx", in("eax") id, lateout("eax") out.0, out("edx") out.1, out("ecx") out.2 + "cpuid", in("eax") id, out("ebx") out.0, out("edx") out.1, out("ecx") out.2 ) } out +} + +/// Returns whether extended functions are available +/// (more specifically, 0x80000001 or higher) +pub fn cpuid_extended_functions() -> bool { + let out: u32; + unsafe { + asm!( + "mov eax, 0x80000000", + "cpuid", out("eax") out + ) + } + out >= 0x80000001 } \ No newline at end of file diff --git a/kernel/src/include/arch/x86/output.rs b/kernel/src/include/arch/x86/output.rs index 2d7887d..6150c5f 100644 --- a/kernel/src/include/arch/x86/output.rs +++ b/kernel/src/include/arch/x86/output.rs @@ -233,6 +233,79 @@ macro_rules! message_funcs { } Ok(()) } + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// Outputs a $func_name message &str to the terminal. + pub fn [< b $func_name s >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name s >](s); + [< t $func_name s >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &str and a newline to the terminal. + pub fn [< b $func_name sln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name sln >](s); + [< t $func_name sln >](s, info)?; + Ok(()) + } + + /// Outputs a $func_name message &\[u8] to the terminal. + pub fn [< b $func_name b >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name b >](s); + [< t $func_name b >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &\[u8] and a newline to the terminal. + pub fn [< b $func_name bln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bln >](s); + [< t $func_name bln >](s, info)?; + Ok(()) + } + + /// Outputs a(n) $func_name message u8 to the terminal. + pub fn [< b $func_name u >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name u >](s); + [< t $func_name u >](s, info)?; + Ok(()) + } + + /////////////////////////////////////////////////////////////// + + /// Outputs a $func_name message &str to the terminal without a prefix. + pub fn [< b $func_name snp >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name snp >](s); + [< t $func_name snp >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &str and a newline to the terminal without a prefix. + pub fn [< b $func_name snpln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name snpln >](s); + [< t $func_name snpln >](s, info)?; + Ok(()) + } + + /// Outputs a $func_name message &\[u8] to the terminal without a prefix. + pub fn [< b $func_name bnp >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bnp >](s); + [< t $func_name bnp >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &\[u8] and a newline to the terminal without a prefix. + pub fn [< b $func_name bnpln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bnpln >](s); + [< t $func_name bnpln >](s, info)?; + Ok(()) + } + + /// Outputs a(n) $func_name message u8 to the terminal without a prefix. + pub fn [< b $func_name unp >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name unp >](s); + [< t $func_name unp >](s, info)?; + Ok(()) + } } } } @@ -244,3 +317,10 @@ message_funcs!(error, "[ERROR] ", CONFIG_PREUSER_OUTPUT_ERROR); message_funcs!(fatal, "[FATAL] ", CONFIG_PREUSER_OUTPUT_FATAL); message_funcs!(output, "", NONE); +/// Resets the position of output to the screen. +pub fn sreset() { + unsafe { + OUTPUT_TERM_POSITION = (0, 0); + } +} + diff --git a/kernel/src/include/mod.rs b/kernel/src/include/mod.rs index 5a45b2a..6fc216c 100644 --- a/kernel/src/include/mod.rs +++ b/kernel/src/include/mod.rs @@ -2,12 +2,14 @@ #![no_std] #![warn(missing_docs)] #![feature(ptr_metadata)] +#![feature(const_trait_impl)] mod constants; mod util; pub mod multiboot2; pub mod arch; mod errors; +pub mod _entry; #[allow(unused_imports)] // if there are no constants, then it gives a warning pub use constants::*; diff --git a/kernel/src/include/multiboot2.rs b/kernel/src/include/multiboot2.rs index 3c73ad3..550aa9c 100644 --- a/kernel/src/include/multiboot2.rs +++ b/kernel/src/include/multiboot2.rs @@ -1,7 +1,7 @@ //! Definitions of structs for multiboot2 information. Mostly used during pre-userspace. /// Used when a CString is passed. Move into separate file? -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct CString { /// The raw pointer to the string. pub ptr: *const u8, @@ -24,6 +24,15 @@ impl core::ops::Index for CString { } } +impl Into<&'static str> for CString { + fn into(self) -> &'static str { + unsafe { + let val: *const str = core::ptr::from_raw_parts(self.ptr, self.len); + return &*val; + } + } +} + /// Used for Multiboot2 tags. This shouldn't be used after a [BootInfo] struct has been initalized, but it still can be used. #[repr(C)] #[derive(Clone)] @@ -89,16 +98,14 @@ pub struct RawMemoryMap { } /// A full memory map provided by a Multiboot2 bootloader. -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct MemoryMap { /// The version of the memory map. Should be disregarded as it's 0. pub version: u32, // currently is 0, future Multiboot2 versions may increment /// Size of one entry(one [MemorySection] for Aphrodite) pub entry_size: u32, - /// A pointer to the first section. - pub sections: *const MemorySection, - /// The number of sections. - pub sections_len: usize + /// All sections. + pub sections: &'static [MemorySection], } /// A color descriptor for [ColorInfo::Palette]. @@ -190,13 +197,13 @@ pub struct BootInfo { // Due to the way modules work, it's not easily possible to make a struct that contains all the modules. // Therefore, they are loaded on the fly. - // Multiboot2 bootloaders may provide us with ELF symbols, but I'm feeling lazy and right now the kernel is a - // flat binary, so I don't care. Sorry if you are affected by this. + // Multiboot2 bootloaders may provide us with ELF symbols, but I'm feeling lazy and right now it's mostly + // unnecessary, so I don't care. Sorry if you are affected by this. /// The memory map provided by the bootloader. pub memory_map: Option, - /// The name of the bootloader(for example, "GRUB"). C-style UTF-8(null-terminated UTF-8) string. + /// The name of the bootloader(for example, "GRUB 2.12"). C-style UTF-8(null-terminated UTF-8) string. /// This should contain the original pointer provided by the bootloader. pub bootloader_name: Option, diff --git a/kernel/src/include/util.rs b/kernel/src/include/util.rs index daaade4..1cad947 100644 --- a/kernel/src/include/util.rs +++ b/kernel/src/include/util.rs @@ -72,3 +72,20 @@ pub fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { } buf } + +/// Converts an u64 to an [u8; 10]. +pub fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { + let mut buf = [0u8; 20]; + let mut i = 19; + if value == 0 { + buf[0] = b'0'; + } + while value > 0 { + let digit = value%10; + let char = b'0' + digit as u8; + buf[i] = char; + value = value / 10; + i -= 1; + } + buf +} diff --git a/kernel/src/internal/arch/x86/entry.rs b/kernel/src/internal/arch/x86/entry.rs index f915cb6..94a8002 100644 --- a/kernel/src/internal/arch/x86/entry.rs +++ b/kernel/src/internal/arch/x86/entry.rs @@ -8,7 +8,7 @@ #![feature(cfg_match)] use core::{arch::asm, ffi::CStr, panic::PanicInfo}; -use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag}; +use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, MemorySection, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag}; use aphrodite::arch::x86::output::*; use aphrodite::arch::x86::egatext as egatext; use egatext::*; @@ -142,8 +142,7 @@ extern "C" fn _start() -> ! { BI.memory_map = Some(MemoryMap { version: (*rawmemorymap).entry_version, entry_size: (*rawmemorymap).entry_size, - sections: &(*rawmemorymap).sections[0], - sections_len: (*rawmemorymap).sections.len() + sections: &*core::ptr::from_raw_parts((&(*rawmemorymap).sections[0]) as &MemorySection, (*rawmemorymap).sections.len()), }); }, 2 => { // Bootloader name @@ -268,21 +267,24 @@ extern "C" fn _start() -> ! { }, 2 => { // EGA Text sdebugsnpln("(EGA Text)"); - sdebugsln("Attempting to output to screen(will then loop for 100000000 cycles)..."); + sdebugsln("Beginning output to screen..."); let ega = egatext::FramebufferInfo { address: framebuffer_info.address, pitch: framebuffer_info.pitch, width: framebuffer_info.width, height: framebuffer_info.height, - bpp: framebuffer_info.bpp + bpp: framebuffer_info.bpp, + change_cursor: true, }; - ega.clear_screen(BLACK_ON_BLACK); + ega.clear_screen(WHITE_ON_BLACK); ega.enable_cursor(14, 15); ega.set_cursor_location((0, 0)); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); + + aphrodite::_entry::_entry(Some(ega), &BI); }, _ => { unreachable!(); @@ -291,12 +293,14 @@ extern "C" fn _start() -> ! { } } - panic!("kernel unexpectedly exited"); + unsafe { + aphrodite::_entry::_entry(None, &BI); + } } #[unsafe(link_section = ".panic")] #[panic_handler] -#[cfg(not(CONFIG_PREUSER_HALT_ON_PANIC = "false"))] +#[cfg(not(CONFIG_HALT_ON_PANIC = "false"))] fn halt_on_panic(info: &PanicInfo) -> ! { let message = info.message().as_str().unwrap_or(""); if message != "" { @@ -311,7 +315,7 @@ fn halt_on_panic(info: &PanicInfo) -> ! { #[unsafe(link_section = ".panic")] #[panic_handler] -#[cfg(all(CONFIG_PREUSER_SPIN_ON_PANIC = "true", CONFIG_PREUSER_HALT_ON_PANIC = "false"))] +#[cfg(all(CONFIG_SPIN_ON_PANIC = "true", CONFIG_PREUSER_HALT_ON_PANIC = "false"))] fn spin_on_panic(info: &PanicInfo) -> ! { let message = info.message().as_str().unwrap_or(""); if message != "" {