diff --git a/emulation/bochsrc b/emulation/bochsrc index 3fbfc21..5ead1e9 100644 --- a/emulation/bochsrc +++ b/emulation/bochsrc @@ -2,7 +2,6 @@ display_library: x, options="gui_debug" port_e9_hack: enabled=1 cpu: reset_on_triple_fault=0, model=corei7_icelake_u magic_break: enabled=1 -clock: sync=realtime, time0=local ata0-master: type=cdrom, path=../kernel/aphrodite-x86.iso, status=inserted boot: cdrom diff --git a/emulation/bochsrc.template b/emulation/bochsrc.template index 4ce46c9..48506d3 100644 --- a/emulation/bochsrc.template +++ b/emulation/bochsrc.template @@ -2,7 +2,6 @@ display_library: x, options="gui_debug" port_e9_hack: enabled=1 cpu: reset_on_triple_fault=0, model=corei7_icelake_u magic_break: enabled=1 -clock: sync=realtime, time0=local ata0-master: type=cdrom, path=../kernel/%{BUILT_FILE}, status=inserted boot: cdrom diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index e3b8bba..d8f16cc 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] paste = "1.0.15" +aphrodite_proc_macros = { path = "./aphrodite_proc_macros"} [profile.release] opt-level = "z" diff --git a/kernel/aphrodite_common/Cargo.toml b/kernel/aphrodite_common/Cargo.toml new file mode 100644 index 0000000..ef75eb6 --- /dev/null +++ b/kernel/aphrodite_common/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "aphrodite_common" +version = "0.1.0" +edition = "2024" + +[lib] +path = "../src/common/mod.rs" + +[dependencies] +strum = "0.27.0" +strum_macros = "0.27.0" +syn = "2.0.98" diff --git a/kernel/aphrodite_proc_macros/Cargo.toml b/kernel/aphrodite_proc_macros/Cargo.toml new file mode 100644 index 0000000..219725f --- /dev/null +++ b/kernel/aphrodite_proc_macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aphrodite_proc_macros" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true +path = "../src/proc_macros/mod.rs" + +[dependencies] +quote = "1.0.38" +syn = { version = "2.0.98", features = ["full"] } +aphrodite_common = { path = "../aphrodite_common" } +proc-macro2 = "1.0.93" diff --git a/kernel/build b/kernel/build index 5256646..1afd7b7 100755 --- a/kernel/build +++ b/kernel/build @@ -122,7 +122,7 @@ echo "[INFO] Checking target with clippy" cargo clippy --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target echo "[INFO] Building target" - cargo build --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target + cargo build --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target 2>/dev/null cp "target/$(echo $target_json | sed 's/\.json//')/release/entrypoint_$target" kernel-$target if [[ "$CONFIG_BUILD_GRUB" = "true" ]]; then diff --git a/kernel/build.rs b/kernel/build.rs index 11c64ed..89cbf86 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -1,6 +1,4 @@ -fn main() -> Result<(), std::io::Error> { - println!("cargo:rerun-if-changed=src/kernel/arch/x86/change_code_segment.s"); - +fn main() { let env = std::env::vars(); // Begin checks @@ -51,10 +49,6 @@ fn main() -> Result<(), std::io::Error> { println!( r#"cargo:rustc-check-cfg=cfg(CONFIG_POWERON_TEST_ALLOC, values("true", "false", none()))"# ); - - println!( - r#"cargo:rustc-check-cfg=cfg(CONFIG_POWERON_TEST_DISPLAY, values("true", "false", none()))"# - ); // End checks // Configuration name used when a config is required but should always evaluate @@ -68,20 +62,4 @@ fn main() -> Result<(), std::io::Error> { println!("cargo:rerun-if-env-changed={}", var); println!("cargo:rustc-cfg={}=\"{}\"", var, val); } - - if !std::process::Command::new("as") - .arg("src/kernel/arch/x86/x86.s") - .arg("-march=i686") - .arg("--32") - .arg("-o") - .arg(format!("{}/x86_asm.o", std::env::var("OUT_DIR").unwrap())) - .spawn()? - .wait()?.success() { - return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Assembler failed to run")); - } else { - println!("cargo::rustc-link-arg={}/x86_asm.o", std::env::var("OUT_DIR").unwrap()); - } - - Ok(()) - } diff --git a/kernel/check b/kernel/check index 19276e6..9c4d6ae 100755 --- a/kernel/check +++ b/kernel/check @@ -19,7 +19,7 @@ real_check=false if [[ "$HAVE_GETOPT" = "true" ]]; then - LONGOPTS=real_check,real-check + LONGOPTS=real_check OPTIONS=c PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") || ( @@ -30,7 +30,7 @@ while true; do case "$1" in - -c|--real_check|--real-check) + -c|--real_check) real_check=true shift ;; diff --git a/kernel/config.aphro.example b/kernel/config.aphro.example index 7034cbd..04ae3db 100644 --- a/kernel/config.aphro.example +++ b/kernel/config.aphro.example @@ -14,7 +14,6 @@ VERSION=generate CONFIG_DISABLE_MULTIBOOT2_SUPPORT=false # Panic behavior. When debugging, generally halt on panic is more useful. -# Halt on panic takes priority over spin on panic if both are enabled. CONFIG_HALT_ON_PANIC=true CONFIG_SPIN_ON_PANIC=false @@ -45,7 +44,4 @@ CONFIG_POWERON_TESTS=true # Whether to run the allocator power on test. CONFIG_POWERON_TEST_ALLOC=true - -# Whether to run the display power on test. -CONFIG_POWERON_TEST_DISPLAY=true # End configs \ No newline at end of file diff --git a/kernel/emulate b/kernel/emulate index 97fb943..276c918 100755 --- a/kernel/emulate +++ b/kernel/emulate @@ -27,7 +27,7 @@ if [[ "$1" = "x86" ]]; then sed -i "s@%{BUILT_FILE}@aphrodite-$1.iso@g" bochsrc - bochs -q -debugger + bochs -q else if [[ "$TARGETS" =~ "$1" ]]; then echo "[ERROR] Cannot emulate specified architecture \"$1\"." diff --git a/kernel/grub_template/boot/grub/grub.cfg b/kernel/grub_template/boot/grub/grub.cfg index 1d00ace..464e45f 100644 --- a/kernel/grub_template/boot/grub/grub.cfg +++ b/kernel/grub_template/boot/grub/grub.cfg @@ -6,11 +6,8 @@ insmod gfxterm insmod efi_uga insmod efi_gop -menuentry "Aphrodite (default)" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' { - echo 'Loading Aphrodite aphrodite-%{VERSION}...' - if multiboot2 /boot/aphrodite.kernel; then - boot - else - echo 'Error loading kernel; not attempting to boot' - fi +menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' { + echo 'Loading Aphrodite aphrodite-%{VERSION} ...' + multiboot2 /boot/aphrodite.kernel + boot } \ No newline at end of file diff --git a/kernel/src/arch_boot_entry/x86.rs b/kernel/src/arch_boot_entry/x86.rs index 9670909..bbb5b12 100644 --- a/kernel/src/arch_boot_entry/x86.rs +++ b/kernel/src/arch_boot_entry/x86.rs @@ -17,9 +17,11 @@ use core::panic::PanicInfo; use aphrodite::arch::egatext; use aphrodite::arch::output::*; use aphrodite::boot::{BootInfo, MemoryMapping}; +use aphrodite::display::COLOR_DEFAULT; use aphrodite::multiboot2::{ FramebufferInfo, MemoryMap, MemorySection, RawMemoryMap, RootTag, Tag, }; +use aphrodite::output::*; #[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))] #[unsafe(link_section = ".bootheader")] @@ -32,7 +34,7 @@ static MULTIBOOT2_HEADER: [u8; 48] = [ 0x0A, 0x00, // Relocatable tag 0x00, 0x00, // Flags, 0x18, 0x00, 0x00, 0x00, // Size of tag - 0x00, 0x00, 0x00, 0x00, // Starting minimum location + 0x00, 0x00, 0x00, 0xB0, // Starting minimum location 0xFF, 0xFF, 0xFF, 0xFF, // Ending maximum location: End of 32-bit address space 0x00, 0x00, 0x00, 0x00, // Image alignment 0x01, 0x00, 0x00, 0x00, // Loading preference: lowest possible @@ -68,6 +70,7 @@ static mut MAGIC: u32 = 0xFFFFFFFF; #[unsafe(link_section = ".start")] #[unsafe(no_mangle)] +#[aphrodite_proc_macros::kernel_item(ArchBootEntry)] extern "C" fn _start() -> ! { unsafe { // Copy values provided by the bootloader out @@ -310,7 +313,7 @@ extern "C" fn _start() -> ! { sdebugsln("Bootloader information has been successfully loaded"); sdebugunp(b'\n'); - //aphrodite::arch::initalize_rtc(); + aphrodite::arch::initalize_rtc(); unsafe { if BI.output.clone().is_some() { @@ -329,14 +332,20 @@ extern "C" fn _start() -> ! { sdebugs("Framebuffer bpp: "); sdebugbnpln(&aphrodite::u8_as_u8_slice(framebuffer_info.bpp)); + sdebugsln("Beginning test output to screen..."); let ega: &dyn aphrodite::display::TextDisplay = &framebuffer_info; + framebuffer_info.disable_cursor(); + ega.clear_screen(COLOR_DEFAULT).unwrap(); + toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); + toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); + toutputsln("Testing EGA Text framebuffer...", ega).unwrap(); - aphrodite::indep_boot_entry::indep_boot_entry(Some(ega), &BI); + aphrodite::indep_boot_entry::IndepBootEntry(Some(ega), &BI); } } - aphrodite::indep_boot_entry::indep_boot_entry(None, &BI); + aphrodite::indep_boot_entry::IndepBootEntry(None, &BI); } #[unsafe(link_section = ".panic")] diff --git a/kernel/src/common/mod.rs b/kernel/src/common/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/kernel/src/common/mod.rs @@ -0,0 +1 @@ + diff --git a/kernel/src/kernel/arch/example_impl/mod.rs b/kernel/src/kernel/arch/example_impl/mod.rs index f71f044..d8a1649 100644 --- a/kernel/src/kernel/arch/example_impl/mod.rs +++ b/kernel/src/kernel/arch/example_impl/mod.rs @@ -23,22 +23,28 @@ pub mod interrupts { pub const USER_SYSCALL_VECTOR: u16 = 0xA0; /// Returns whether interrupts are enabled or not. + #[aphrodite_proc_macros::kernel_item(InterruptsCheck)] fn interrupts_enabled() -> bool { false } /// Enables interrupts. + #[aphrodite_proc_macros::kernel_item(InterruptsEnable)] fn enable_interrupts() {} /// Disables interrupts. + #[aphrodite_proc_macros::kernel_item(InterruptsDisable)] fn disable_interrupts() {} /// Disables interrupts and a value that can be used to restore them /// with [restore_irq]. + #[aphrodite_proc_macros::kernel_item(InterruptsPop)] fn pop_irq() -> u64 { 0 } /// Restores interrupts after a [pop_irq] call. + #[aphrodite_proc_macros::kernel_item(InterruptsRestore)] fn restore_irq(_irq: u64) {} /// Activates an IDT. + #[aphrodite_proc_macros::kernel_item(ActivateIDT)] fn activate_idt(_idt: Idt) {} /// An IDT. diff --git a/kernel/src/kernel/arch/x86/gdt.rs b/kernel/src/kernel/arch/x86/gdt.rs index 9965271..0475ef8 100644 --- a/kernel/src/kernel/arch/x86/gdt.rs +++ b/kernel/src/kernel/arch/x86/gdt.rs @@ -4,88 +4,39 @@ use core::alloc::Layout; use core::arch::asm; +use alloc::vec::Vec; + /// The GDTR. Used internally in [activate_gdt]. #[repr(C, packed)] -#[derive(Clone, Copy)] struct Gdtr { - // raw pointer to the GDT - base: u32, - // size of the GDT in bytes - size: u16, + base: *const u8, + size: usize, } -unsafe impl Sync for Gdtr {} - -/// Activates the GDT using `lgdt`. Does NOT, I repeat, does NOT change the -/// segment registers! pub unsafe fn activate_gdt(ptr: *const [u8]) { - unsafe { - asm!( - "mov [3f], ax", // load limit - "mov [3f+2], ebx", // load base - "xor ax, ax", // clear ax - "lldt ax", // deactivate LDT - "lgdt [3f]", // load GDT - "jmp 2f", // jump past the data - "3:", // GDT data - "nop; nop; nop; nop; nop; nop", - "2:", // end - in("ax") ptr.len() as u16, - in("ebx") ptr as *const u8 as usize as u32, - options(readonly) - ) - // super::output::sdebugs("base: "); - // super::output::sdebugbnp(&crate::u32_as_u8_slice(GDTR.base)); - // super::output::sdebugsnp(" size: "); - // super::output::sdebugbnpln(&crate::u16_as_u8_slice(GDTR.size)); - } + let gdtr = Gdtr { + base: ptr as *const u8, + size: ptr.len(), + }; + unsafe { asm!("lgdt {}", in(reg) (&gdtr) as *const Gdtr as usize) } } /// Writes a series of GDT entries to an allocated section of memory and returns /// a pointer. pub unsafe fn write_gdt_entries( - entries: &[GDTEntry], + entries: Vec, ) -> Result<*const [u8], crate::Error<'static>> { let mut mem = - unsafe { alloc::alloc::alloc(Layout::from_size_align(8 * entries.len(), 8).unwrap()) }; - for ele in entries { - let serialized = ele.serialize()?; - unsafe { - core::ptr::write(mem as *mut [u8; 8], serialized); - } + unsafe { alloc::alloc::alloc(Layout::from_size_align(8 * entries.len(), 1).unwrap()) }; + for ele in &entries { + let ele: &GDTEntry = ele; + unsafe { ele.write_to_addr(mem as *mut ())? } mem = (mem as usize + 8) as *mut u8; } Ok(core::ptr::from_raw_parts(mem, 8 * entries.len())) } -const fn concat_arrays(a: [T; M], b: [T; N]) -> [T; M + N] { - let mut result = core::mem::MaybeUninit::uninit(); - let dest = result.as_mut_ptr() as *mut T; - unsafe { - core::ptr::copy_nonoverlapping(a.as_ptr(), dest, M); - core::ptr::copy_nonoverlapping(b.as_ptr(), dest.add(M), N); - core::mem::forget(a); - core::mem::forget(b); - result.assume_init() - } -} - -pub const fn serialize_gdt_entries( - entries: [GDTEntry; 5], -) -> [u8; 5 * 8] { - concat_arrays( - concat_arrays( - concat_arrays( - concat_arrays(entries[0].serialize_panicing(), entries[1].serialize_panicing()), - entries[2].serialize_panicing(), - ), - entries[3].serialize_panicing(), - ), - entries[4].serialize_panicing(), - ) -} - /// A GDT entry. #[derive(Clone, Copy)] pub struct GDTEntry { @@ -108,52 +59,35 @@ pub const GDT_NULL_ENTRY: GDTEntry = GDTEntry { /// An error returned by [GDTEntry::write_to_addr] when the limit is greater /// than 0xFFFFF. -pub const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1; +const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1; impl GDTEntry { - const fn serialize(self) -> Result<[u8; 8], crate::Error<'static>> { + const unsafe fn write_to_addr(self, ptr: *mut ()) -> Result<(), crate::Error<'static>> { if self.limit > 0xFFFFF { return Err(crate::Error::new( "Invalid GDT entry limit(more than 0xFFFFF)", GDT_WRITE_ADDR_INVALID_LIMIT, )); } - let mut out = [0u8; 8]; + let mut serialized = (0u64).to_ne_bytes(); - out[0] = (self.limit & 0xFF) as u8; - out[1] = ((self.limit >> 8) & 0xFF) as u8; - out[6] = ((self.limit >> 16) & 0x0F) as u8; + serialized[0] = (self.limit & 0xFF) as u8; + serialized[1] = ((self.limit >> 8) & 0xFF) as u8; + serialized[6] = ((self.limit >> 16) & 0x0F) as u8; - out[2] = (self.base & 0xFF) as u8; - out[3] = ((self.base >> 8) & 0xFF) as u8; - out[4] = ((self.base >> 16) & 0xFF) as u8; - out[7] = ((self.base >> 24) & 0xFF) as u8; + serialized[2] = (self.base & 0xFF) as u8; + serialized[3] = ((self.base >> 8) & 0xFF) as u8; + serialized[4] = ((self.base >> 16) & 0xFF) as u8; + serialized[7] = ((self.base >> 24) & 0xFF) as u8; - out[5] = self.access; + serialized[5] = self.access; - out[6] |= self.flags << 4; + serialized[6] |= self.flags << 4; - Ok(out) - } - const fn serialize_panicing(self) -> [u8; 8] { - if self.limit > 0xFFFFF { - panic!("Invalid GDT entry limit(more than 0xFFFFF)"); + unsafe { + core::ptr::write(ptr as *mut [u8; 8], serialized); } - let mut out = [0u8; 8]; - out[0] = (self.limit & 0xFF) as u8; - out[1] = ((self.limit >> 8) & 0xFF) as u8; - out[6] = ((self.limit >> 16) & 0x0F) as u8; - - out[2] = (self.base & 0xFF) as u8; - out[3] = ((self.base >> 8) & 0xFF) as u8; - out[4] = ((self.base >> 16) & 0xFF) as u8; - out[7] = ((self.base >> 24) & 0xFF) as u8; - - out[5] = self.access; - - out[6] |= self.flags << 4; - - out + Ok(()) } } diff --git a/kernel/src/kernel/arch/x86/interrupt_impls.rs b/kernel/src/kernel/arch/x86/interrupt_impls.rs deleted file mode 100644 index 1ea1df4..0000000 --- a/kernel/src/kernel/arch/x86/interrupt_impls.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Implementations of interrupts. -#![cfg(target_arch = "x86")] -#![allow(undefined_naked_function_abi)] // special calling convention anyway - -use core::arch::naked_asm; - -macro_rules! int_wrapper { - ($func:block, $num:expr) => { - paste::paste! { - /// autogenerated interrupt wrapper - #[naked] - pub unsafe fn [< int $num >]() { - unsafe { - naked_asm!( - "pushad", - "cld", - "call {}", - "popad", - "iret", - sym [< int $num _rust >] - ) - } - } - - /// autogenerated interrupt body - unsafe extern "C" fn [< int $num _rust >]() $func - } - }; -} - -int_wrapper!( - { - super::output::sdebugsln("Interrupt handler #0 ran"); - }, - 0 -); diff --git a/kernel/src/kernel/arch/x86/interrupts.rs b/kernel/src/kernel/arch/x86/interrupts.rs index 27106c0..ed08071 100644 --- a/kernel/src/kernel/arch/x86/interrupts.rs +++ b/kernel/src/kernel/arch/x86/interrupts.rs @@ -9,6 +9,7 @@ use core::mem::MaybeUninit; pub const USER_SYSCALL_VECTOR: u16 = 0xA0; /// Returns whether interrupts are enabled or not. +#[aphrodite_proc_macros::kernel_item(InterruptsCheck)] pub fn interrupts_enabled() -> bool { let flags: u32; unsafe { @@ -21,11 +22,9 @@ pub fn interrupts_enabled() -> bool { } /// Disables interrupts. +#[aphrodite_proc_macros::kernel_item(InterruptsDisable)] pub fn disable_interrupts() { unsafe { asm!("cli") } } -/// Enables interrupts. -pub fn enable_interrupts() { unsafe { asm!("sti") } } - /// PoppedInterrupts implements drop and restores the interrupts upon being /// dropped. This is useful in functions where you need interrupts disabled /// during it but also want to use functions like [Result::unwrap] or @@ -38,6 +37,7 @@ impl Drop for PoppedInterrupts { } /// Disables interrupts and returns the value of them. +#[aphrodite_proc_macros::kernel_item(InterruptsPop)] pub fn pop_irq() -> PoppedInterrupts { let flags: u32; unsafe { @@ -51,6 +51,7 @@ pub fn pop_irq() -> PoppedInterrupts { } /// Restores interrupts after a [pop_irq] call. +#[aphrodite_proc_macros::kernel_item(InterruptsRestore)] pub fn restore_irq(flags: PoppedInterrupts) { let flags = flags.0; unsafe { @@ -70,21 +71,12 @@ struct Idtr { /// Loads an interrupt descriptor table. unsafe fn load_idt(base: *const u8, size: usize) { - static mut IDTR: Idtr = Idtr { - base: 0 as *const u8, - size: 0, - }; - unsafe { - IDTR = Idtr { - base, - size, - }; - } - unsafe { asm!("lidt {}", sym IDTR) } + let idtr = Idtr { base, size }; + unsafe { asm!("lidt {}", in(reg) (&idtr) as *const Idtr as usize) } } #[derive(Clone, Copy)] -pub struct IdtEntry { +pub(super) struct IdtEntry { pub offset_high: u16, pub data: u16, pub segment: u16, @@ -116,7 +108,8 @@ impl From for RawIdtEntry { /// /// # Panics /// Panics if the global allocator has not been setup -pub unsafe fn activate_idt(idt: Idt) { +#[aphrodite_proc_macros::kernel_item(ActivateIDT)] +fn activate_idt(idt: Idt) { let mut entries = alloc::vec::Vec::new(); for i in 0..idt.len { if idt.using_raw[i] { @@ -202,7 +195,7 @@ pub unsafe fn activate_idt(idt: Idt) { #[derive(Clone, Copy)] pub struct Idt { vectors: [u16; 256], - funcs: [MaybeUninit; 256], + funcs: [MaybeUninit; 256], user_callable: [bool; 256], exception: [bool; 256], raw_entries: [IdtEntry; 256], @@ -214,7 +207,7 @@ pub struct Idt { #[derive(Clone, Copy)] pub struct IdtBuilder { vectors: [u16; 256], - funcs: [MaybeUninit; 256], + funcs: [MaybeUninit; 256], user_callable: [bool; 256], exception: [bool; 256], raw_entries: [IdtEntry; 256], @@ -245,7 +238,7 @@ impl IdtBuilder { pub fn add_fn( &mut self, vector: u16, - func: unsafe fn(), + func: fn(), user_callable: bool, exception: bool, ) -> &mut Self { diff --git a/kernel/src/kernel/arch/x86/memory.rs b/kernel/src/kernel/arch/x86/memory.rs new file mode 100644 index 0000000..99a946d --- /dev/null +++ b/kernel/src/kernel/arch/x86/memory.rs @@ -0,0 +1,180 @@ +//! Hardware-level memory sections. Unimplemented for certain hardware, x86 +//! implements with GDT. +#![cfg(target_arch = "x86")] + +use core::arch::asm; + +use alloc::vec; +use alloc::vec::Vec; + +use crate::memsections::*; + +use super::gdt::{GDTEntry, write_gdt_entries}; + +/// A list of memory sections. Create one with [MemorySectionBuilder]. +pub struct MemorySections { + sections: Vec, +} + +#[repr(packed)] +struct GDTR { + address: u32, + size: u16, +} + +unsafe impl crate::memsections::MemorySections for MemorySections { + unsafe fn write(self) -> Result<(), crate::Error<'static>> { + let mut entries: Vec = vec![]; + + for section in self.sections { + let mut section: MemorySection = section; + // rust-analyzer doesn't want to cooperate and recognize that section is already + // MemorySection, so I'm telling it here. + fn make_entry(section: &mut MemorySection, entries: &mut Vec) { + if section.length == 0 { + return; + } + let mut len = section.length as u32; + while len > 0xFFFFF { + len -= 0xFFFFF; + } + let mut access = 0b10000001u8; + match section.owner { + Owner::Kernelspace => { + access |= 0b0000000; + }, + Owner::Modulespace => { + access |= 0b0100000; + }, + Owner::Userspace => { + access |= 0b1100000; + }, + } + if let SectionType::TaskSection { busy } = section.section_type { + access |= 0b00000; + if busy { + access |= 0x9; + } else { + access |= 0xB; + } + } else { + access |= 0b10000; + if let SectionType::CodeSection { + can_powerful_sections_jump, + } = section.section_type + { + access |= 0b1000; + if can_powerful_sections_jump { + access |= 0b100; + } + if section.readable { + access |= 0b10; + } + } else if section.section_type == SectionType::DataSection { + access |= 0b0000; + if section.writable { + access |= 0b10; + } + } + } + + let flags = 0b1100u8; + + let entry = GDTEntry { + limit: len, + base: section.address as u32, + access, + flags, + }; + if section.length > 0xFFFFF { + section.length -= 0xFFFFF; + } + entries.push(entry); + } + while section.length > 0xFFFFF { + make_entry(&mut section, &mut entries); + } + make_entry(&mut section, &mut entries); + } + unsafe { + let _ = super::interrupts::pop_irq(); + + let segment_entries: Vec = entries.clone(); + + let ptr = write_gdt_entries(entries)?; + + let gdtr = GDTR { + address: ptr as *const u8 as usize as u32, + size: (ptr.len() - 1) as u16, + }; + + let addr = &gdtr as *const GDTR as *const () as usize as u32; + + asm!( + "lgdt eax", + in("eax") addr + ); + + let mut code_segment = 0u16; + let mut code_set = false; + let mut data_segment = 0u16; + let mut data_set = false; + + let mut i = 0; + for entry in segment_entries { + let entry: GDTEntry = entry; + i += 1; + if code_set && data_set { + break; + } + + if entry.access & 0b11000 == 0b11000 && !code_set { + code_segment = i - 1; + code_set = true; + } else if entry.access & 0b10000 == 0b10000 && !data_set { + data_segment = i - 1; + data_set = true; + } + } + + asm!( + "jmp bx:2 ; `#[deny(named_asm_labels)]` on by default; see + 2: ; ax is already loaded with the correct value from rustland + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax", + in("bx") code_segment, + in("ax") data_segment, + options(preserves_flags, nomem, nostack) + ); + } + + Ok(()) + } +} + +/// A memory section builder. +pub struct MemorySectionBuilder { + sections: Vec, +} + +impl MemorySectionBuilder { + /// Create a new MemorySectionBuilder. + pub fn new() -> Self { MemorySectionBuilder { sections: vec![] } } + + /// Adds a section to this MemorySectionBuilder. + pub fn add_section(&mut self, section: MemorySection) -> &mut Self { + self.sections.push(section); + + self + } + + /// Finishes this MemorySectionBuilder and returns a MemorySections. + pub fn finish(self) -> MemorySections { + MemorySections { + sections: self.sections, + } + } +} diff --git a/kernel/src/kernel/arch/x86/mod.rs b/kernel/src/kernel/arch/x86/mod.rs index 5a3ee96..8e22688 100644 --- a/kernel/src/kernel/arch/x86/mod.rs +++ b/kernel/src/kernel/arch/x86/mod.rs @@ -5,19 +5,19 @@ use core::arch::asm; pub mod egatext; mod gdt; -mod interrupt_impls; pub mod interrupts; +pub mod memory; pub mod output; pub mod paging; pub mod ports; mod constants; +use alloc::vec; use constants::*; use gdt::GDTEntry; -use interrupts::{disable_interrupts, enable_interrupts, pop_irq, restore_irq}; +use interrupts::{pop_irq, restore_irq}; use ports::{inb, outb}; -use output::*; /// Returns the most specific architecture available. pub const fn get_arch() -> super::Architecture { super::Architecture::X86 } @@ -92,66 +92,27 @@ pub fn initalize_rtc() { unsafe { RTC_INITALIZED = true } } +pub fn sleep(seconds: u32) { initalize_rtc(); } + pub fn alloc_available_boot() { - disable_interrupts(); - { - // GDT - sdebugsln("Setting up GDT"); + let irq = pop_irq(); + let mut entries = vec![]; + entries.push(gdt::GDT_NULL_ENTRY); + entries.push(GDTEntry { + limit: 0, + base: 0, + access: 0b10011011, + flags: 0b1100, + }); // kernel code segment + entries.push(GDTEntry { + limit: 0, + base: 0, + access: 0b10010011, + flags: 0b1100, + }); // - let entries = gdt::serialize_gdt_entries([ - gdt::GDT_NULL_ENTRY, - GDTEntry { // kernel code segment, segment 0x08 - limit: 0xFFFFF, - base: 0, - access: 0x9A, - flags: 0xC, - }, - GDTEntry { // kernel data segment, segment 0x10 - limit: 0xFFFFF, - base: 0, - access: 0x92, - flags: 0xC, - }, - GDTEntry { // user code segment, segment 0x18 - limit: 0xFFFFF, - base: 0, - access: 0xFA, - flags: 0xC, - }, - GDTEntry { // user data segment, segment 0x20 - limit: 0xFFFFF, - base: 0, - access: 0xF2, - flags: 0xC, - } - ]); - - sdebugsln("GDT prepared"); - - unsafe { - gdt::activate_gdt(&entries); - } - - sdebugsln("GDT successfully activated; resetting segment registers"); - - unsafe { - asm!( - "call reloadSegments", // I hate rust's inline assembly - out("ax") _ - ); - } - sdebugsln("Segment registers reset"); - } - { - // IDT - sdebugsln("Setting up IDT"); - let idt = self::interrupts::IdtBuilder::new() - .add_fn(0, interrupt_impls::int0, false, true) - .finish(); - unsafe { - interrupts::activate_idt(idt); - } - enable_interrupts(); - sdebugsln("IDT successfully loaded"); + unsafe { + gdt::activate_gdt(gdt::write_gdt_entries(entries).unwrap()); } + restore_irq(irq); } diff --git a/kernel/src/kernel/arch/x86/paging.rs b/kernel/src/kernel/arch/x86/paging.rs index c09f1f4..a7cc795 100644 --- a/kernel/src/kernel/arch/x86/paging.rs +++ b/kernel/src/kernel/arch/x86/paging.rs @@ -3,6 +3,8 @@ use core::arch::asm; +use aphrodite_proc_macros::kernel_item; + /// One page directory entry. Use [PageDirectoryEntry::create_fourmb] or /// [PageDirectoryEntry::create_other] to make these. pub enum PageDirectoryEntry { @@ -118,9 +120,11 @@ static mut PAGE_DIRECTORY: PageDirectoryEntry = PageDirectoryEntry::create_other(0, false, 0, false, false, false, false, false, false, false); /// Initalize paging. +#[kernel_item(PagingInit)] pub fn initalize_paging() {} /// Disables paging by clearing bit 31 in the cr0 register. +#[kernel_item(PagingDeinit)] pub fn disable_paging() { unsafe { asm!( diff --git a/kernel/src/kernel/arch/x86/x86.s b/kernel/src/kernel/arch/x86/x86.s deleted file mode 100644 index e2ada5d..0000000 --- a/kernel/src/kernel/arch/x86/x86.s +++ /dev/null @@ -1,24 +0,0 @@ -.intel_syntax noprefix - -.global reloadSegments - -reloadSegments: - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - xchg bx, bx - call get_retaddr_ppro - add eax, 7 - pushd 0x8 - push eax - retf - -.reload_cs: - ret - -get_retaddr_ppro: - mov eax, [esp] - ret \ No newline at end of file diff --git a/kernel/src/kernel/boot.rs b/kernel/src/kernel/boot.rs index d2f33cd..1328dd0 100644 --- a/kernel/src/kernel/boot.rs +++ b/kernel/src/kernel/boot.rs @@ -127,7 +127,7 @@ impl core::iter::Iterator for MemoryMap { self.reset_iter(); return None; } - Some(self.sections[self.idx - 1]) + Some(self.sections[self.idx - 1].into()) } } diff --git a/kernel/src/kernel/cfg.rs b/kernel/src/kernel/cfg.rs index c00de8f..38ce8eb 100644 --- a/kernel/src/kernel/cfg.rs +++ b/kernel/src/kernel/cfg.rs @@ -1,6 +1,6 @@ //! Config-related stuff. -/// Get configurations as a certain type +/// C #[macro_export] macro_rules! cfg_int { ($cfg:literal, $type:ident) => { diff --git a/kernel/src/kernel/indep_boot_entry.rs b/kernel/src/kernel/indep_boot_entry.rs index f1ea444..4080ae5 100644 --- a/kernel/src/kernel/indep_boot_entry.rs +++ b/kernel/src/kernel/indep_boot_entry.rs @@ -4,12 +4,15 @@ #![allow(static_mut_refs)] use crate::arch::output::*; -use crate::display::NoneTextDisplay; +use crate::display::{COLOR_DEFAULT, NoneTextDisplay}; use crate::output::*; +use aphrodite_proc_macros::*; + /// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files /// eventually call this. -pub fn indep_boot_entry( +#[kernel_item(IndepBootEntry)] +fn indep_boot_entry( display: Option<&dyn crate::display::TextDisplay>, #[allow(non_snake_case)] BI: &crate::boot::BootInfo, ) -> ! { @@ -19,12 +22,13 @@ pub fn indep_boot_entry( "Somehow the kernel successfully booted into IndepBootEntry with a dummy architecture" ); - sdebugsln("IndepBootEntry running"); - let display = display.unwrap_or(&NoneTextDisplay {}); + display.clear_screen(COLOR_DEFAULT); + sreset(); + let mem_map = BI.memory_map.unwrap(); - crate::mem::memory_map_alloc_init(mem_map).unwrap(); + crate::mem::MemMapAllocInit(mem_map).unwrap(); crate::arch::alloc_available_boot(); diff --git a/kernel/src/kernel/mem.rs b/kernel/src/kernel/mem.rs index 3fdcf8a..f840b72 100644 --- a/kernel/src/kernel/mem.rs +++ b/kernel/src/kernel/mem.rs @@ -9,6 +9,8 @@ use core::ptr::{NonNull, null_mut}; use crate::boot::{MemoryMap, MemoryType}; +use aphrodite_proc_macros::*; + #[derive(Clone, Copy)] struct Allocation { /// Whether this allocation is used. This is used so that the @@ -63,6 +65,7 @@ static mut ALLOCATOR: MaybeMemoryMapAlloc<'static> = MaybeMemoryMapAlloc::new(No static mut ALLOCATOR_MEMMAP: MaybeUninit = MaybeUninit::uninit(); static mut ALLOCATOR_INITALIZED: bool = false; +#[kernel_item(MemMapAlloc)] pub fn get_allocator() -> Option<&'static MemoryMapAlloc<'static>> { if unsafe { ALLOCATOR_INITALIZED } { #[allow(static_mut_refs)] @@ -87,7 +90,8 @@ pub unsafe fn get_allocator_unchecked() -> &'static MemoryMapAlloc<'static> { } } -pub fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> { +#[kernel_item(MemMapAllocInit)] +fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> { #[allow(static_mut_refs)] unsafe { ALLOCATOR_MEMMAP.write(memmap); @@ -199,7 +203,7 @@ impl<'a> MemoryMapAlloc<'a> { } } } - if out.allocations.is_null() { + if out.allocations == core::ptr::null_mut() { return Err(crate::Error::new( "no free memory with space for 32 allocations", ALLOCATIONS_NOT_ENOUGH_SPACE, @@ -341,7 +345,7 @@ impl<'a> MemoryMapAlloc<'a> { /// Finds a free block of memory that can fit the requested size and /// alignment fn find_free_block(&self, size: u64, align: usize) -> Option { - for mapping in *self.memory_map { + for mapping in self.memory_map.clone() { if mapping.len < size { continue; } diff --git a/kernel/src/kernel/mod.rs b/kernel/src/kernel/mod.rs index 8645790..3c9c3e6 100644 --- a/kernel/src/kernel/mod.rs +++ b/kernel/src/kernel/mod.rs @@ -7,7 +7,6 @@ #![deny(rustdoc::invalid_html_tags)] #![deny(rustdoc::invalid_rust_codeblocks)] #![deny(unsafe_op_in_unsafe_fn)] -#![allow(incomplete_features)] #![feature(ptr_metadata)] #![feature(const_trait_impl)] #![feature(f128)] @@ -21,8 +20,6 @@ #![allow(internal_features)] #![feature(core_intrinsics)] #![feature(vec_into_raw_parts)] -#![feature(naked_functions)] -#![feature(generic_const_exprs)] extern crate alloc; diff --git a/kernel/src/kernel/multiboot2.rs b/kernel/src/kernel/multiboot2.rs index eeee735..53a6cfe 100644 --- a/kernel/src/kernel/multiboot2.rs +++ b/kernel/src/kernel/multiboot2.rs @@ -54,18 +54,18 @@ pub struct MemorySection { reserved: u32, } -impl From for crate::boot::MemoryMapping { - fn from(val: MemorySection) -> Self { +impl Into for MemorySection { + fn into(self) -> crate::boot::MemoryMapping { MemoryMapping { - mem_type: match val.mem_type { + mem_type: match self.mem_type { 1 => crate::boot::MemoryType::Free, 2 => crate::boot::MemoryType::HardwareReserved, 3 => crate::boot::MemoryType::HardwareSpecific(3, false), 5 => crate::boot::MemoryType::Faulty, _ => crate::boot::MemoryType::Reserved, }, - start: val.base_addr, - len: val.length, + start: self.base_addr, + len: self.length, } } } diff --git a/kernel/src/kernel/power_on_tests/display.rs b/kernel/src/kernel/power_on_tests/display.rs deleted file mode 100644 index b19efbe..0000000 --- a/kernel/src/kernel/power_on_tests/display.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![cfg(all( - not(CONFIG_POWERON_TESTS = "false"), - not(CONFIG_POWERON_TEST_DISPLAY = "false") -))] - -use crate::display::{TextDisplay, COLOR_BLACK, COLOR_DEFAULT}; -use crate::output::{toutputsln, sreset}; - -pub fn run(display: &dyn TextDisplay) { - display.clear_screen(COLOR_DEFAULT).unwrap(); - sreset(); - toutputsln("Testing display...", display).unwrap(); - toutputsln("Testing display...", display).unwrap(); - toutputsln("Testing display...", display).unwrap(); - display.clear_screen(COLOR_BLACK).unwrap(); - sreset(); - display.clear_screen(COLOR_DEFAULT).unwrap(); - sreset(); -} \ No newline at end of file diff --git a/kernel/src/kernel/power_on_tests/memmapalloc.rs b/kernel/src/kernel/power_on_tests/memmapalloc.rs index 6209d26..be615a7 100644 --- a/kernel/src/kernel/power_on_tests/memmapalloc.rs +++ b/kernel/src/kernel/power_on_tests/memmapalloc.rs @@ -27,7 +27,7 @@ pub fn run(display: &dyn TextDisplay) { tdebugsnpln(" byte(s) of memory...", display).unwrap(); let allocation = allocator.allocate(Layout::from_size_align(size, 1).unwrap()); - if allocation.is_err() { + if let Err(_) = allocation { terrors("Failed to allocate: ", display).unwrap(); unsafe { crate::mem::LAST_MEMMAP_ERR.unwrap_err().display_np(display) } panic!("Allocator test failure"); @@ -51,5 +51,6 @@ pub fn run(display: &dyn TextDisplay) { tdebugsln("Successfully deallocated!", display).unwrap(); } } + tdebugsln("", display).unwrap(); } } diff --git a/kernel/src/kernel/power_on_tests/mod.rs b/kernel/src/kernel/power_on_tests/mod.rs index 9b3bdcc..6f52a96 100644 --- a/kernel/src/kernel/power_on_tests/mod.rs +++ b/kernel/src/kernel/power_on_tests/mod.rs @@ -3,12 +3,8 @@ use crate::display::TextDisplay; mod memmapalloc; -mod display; pub fn run(display: &dyn TextDisplay) { #[cfg(not(CONFIG_POWERON_TEST_ALLOC = "false"))] memmapalloc::run(display); - - #[cfg(not(CONFIG_POWERON_TEST_DISPLAY = "false"))] - display::run(display); } diff --git a/kernel/src/kernel/util.rs b/kernel/src/kernel/util.rs index 22680d4..caac0fc 100644 --- a/kernel/src/kernel/util.rs +++ b/kernel/src/kernel/util.rs @@ -16,7 +16,7 @@ pub const fn i16_as_u8_slice(mut value: i16) -> [u8; 6] { let digit = value % 10; let char = b'0' + digit as u8; buf[i] = char; - value /= 10; + value = value / 10; i -= 1; } buf @@ -33,24 +33,7 @@ pub const fn u32_as_u8_slice(mut value: u32) -> [u8; 10] { let digit = value % 10; let char = b'0' + digit as u8; buf[i] = char; - value /= 10; - i -= 1; - } - buf -} - -/// Converts an u16 to an [u8; 5]. -pub const fn u16_as_u8_slice(mut value: u16) -> [u8; 5] { - let mut buf = [0u8; 5]; - let mut i = 4; - 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 /= 10; + value = value / 10; i -= 1; } buf @@ -65,15 +48,15 @@ pub const fn u8_as_u8_slice(mut value: u8) -> [u8; 3] { } while value > 0 { let digit = value % 10; - let char = b'0' + digit; + let char = b'0' + digit as u8; buf[i] = char; - value /= 10; + value = value / 10; i -= 1; } buf } -/// Converts an usize(32 or 64 bit) to an [u8; 20]. +/// Converts an usize(32 or 64 bit) to an [u8; 10]. pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { let mut buf = [0u8; 20]; let mut i = 19; @@ -84,7 +67,7 @@ pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { let digit = value % 10; let char = b'0' + digit as u8; buf[i] = char; - value /= 10; + value = value / 10; i -= 1; } buf @@ -101,7 +84,7 @@ pub const fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { let digit = value % 10; let char = b'0' + digit as u8; buf[i] = char; - value /= 10; + value = value / 10; i -= 1; } buf @@ -116,7 +99,7 @@ pub fn str_as_i16(mut value: &[u8]) -> i16 { } for byte in value { let byte = *byte; - if !byte.is_ascii_digit() { + if byte < b'0' || byte > b'9' { continue; } out *= 10; @@ -138,7 +121,7 @@ pub fn str_as_u32(value: &[u8]) -> u32 { let mut out = 0u32; for byte in value { let byte = *byte; - if !byte.is_ascii_digit() { + if byte < b'0' || byte > b'9' { continue; } out *= 10; @@ -160,7 +143,7 @@ pub fn str_as_u128(value: &[u8]) -> u128 { let mut out = 0u128; for byte in value { let byte = *byte; - if !byte.is_ascii_digit() { + if byte < b'0' || byte > b'9' { continue; } out *= 10; @@ -182,7 +165,7 @@ pub fn str_as_u64(value: &[u8]) -> u64 { let mut out = 0u64; for byte in value { let byte = *byte; - if !byte.is_ascii_digit() { + if byte < b'0' || byte > b'9' { continue; } out *= 10; diff --git a/kernel/src/proc_macros/mod.rs b/kernel/src/proc_macros/mod.rs new file mode 100644 index 0000000..f5b7df4 --- /dev/null +++ b/kernel/src/proc_macros/mod.rs @@ -0,0 +1,62 @@ +use proc_macro::TokenStream; +use quote::{ToTokens, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::{ItemFn, Signature, Token}; + +struct KernelItemNameInput { + item: syn::Ident, +} + +impl Parse for KernelItemNameInput { + fn parse(input: ParseStream) -> syn::Result { + let item: syn::Ident = input.parse()?; + Ok(KernelItemNameInput { item }) + } +} + +fn to_tokens(signature: Signature, tokens: &mut proc_macro2::TokenStream) { + let ts = tokens; + signature.constness.to_tokens(ts); + signature.asyncness.to_tokens(ts); + signature.unsafety.to_tokens(ts); + signature.abi.to_tokens(ts); + signature.fn_token.to_tokens(ts); + signature.generics.to_tokens(ts); + signature.paren_token.surround(ts, |tokens| { + signature.inputs.to_tokens(tokens); + if let Some(variadic) = &signature.variadic { + if !signature.inputs.empty_or_trailing() { + ::default().to_tokens(tokens); + } + variadic.to_tokens(tokens); + } + }); + signature.output.to_tokens(ts); + signature.generics.where_clause.to_tokens(ts); +} + +fn to_token_stream(signature: Signature) -> proc_macro2::TokenStream { + let mut tokens = proc_macro2::TokenStream::new(); + to_tokens(signature, &mut tokens); + tokens +} + +/// Implement a kernel item. +#[proc_macro_attribute] +pub fn kernel_item(attr: TokenStream, item: TokenStream) -> TokenStream { + let name: KernelItemNameInput = syn::parse_macro_input!(attr); + let item_name = name.item; + + let input_fn = syn::parse_macro_input!(item as ItemFn); + let fn_name = input_fn.clone().sig.ident; + let fn_sig = to_token_stream(input_fn.clone().sig); + + quote! { + /// The #item_name kernel item. + #[allow(non_upper_case_globals)] + pub const #item_name: #fn_sig = #fn_name; + + #input_fn + } + .into() +}