From f18846b32ea668923c16c873b5eb2ebc7ff5ed74 Mon Sep 17 00:00:00 2001 From: Arthur Beck Date: Sat, 18 Jan 2025 20:26:50 -0600 Subject: [PATCH] Inital work. Long way to go; does not boot yet. --- emulation/bochsrc | 3 + kernel/Cargo.toml | 21 +++ kernel/build | 4 + kernel/i686-unknown-none.json | 24 +++ kernel/kernel.flat | Bin 0 -> 12 bytes kernel/link.x | 12 ++ kernel/src/include/constants.rs | 4 + kernel/src/include/mod.rs | 9 + kernel/src/include/multiboot2.rs | 229 ++++++++++++++++++++++++++ kernel/src/internal/arch/x86/entry.rs | 65 ++++++++ 10 files changed, 371 insertions(+) create mode 100644 emulation/bochsrc create mode 100644 kernel/Cargo.toml create mode 100755 kernel/build create mode 100644 kernel/i686-unknown-none.json create mode 100755 kernel/kernel.flat create mode 100644 kernel/link.x create mode 100644 kernel/src/include/constants.rs create mode 100644 kernel/src/include/mod.rs create mode 100644 kernel/src/include/multiboot2.rs create mode 100644 kernel/src/internal/arch/x86/entry.rs diff --git a/emulation/bochsrc b/emulation/bochsrc new file mode 100644 index 0000000..6412e97 --- /dev/null +++ b/emulation/bochsrc @@ -0,0 +1,3 @@ +display_library: x, options="gui_debug" +port_e9_hack: enabled=1 +cpu: reset_on_triple_fault=0 \ No newline at end of file diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml new file mode 100644 index 0000000..516a87f --- /dev/null +++ b/kernel/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "kernel" +version = "0.1.0" +edition = "2024" + +[dependencies] + +[profile.release] +opt-level = "z" +strip = true +lto = true +codegen-units = 1 +panic = "abort" + +[[bin]] +name = "entrypoint" +path = "src/internal/arch/x86/entry.rs" + +[lib] +name = "aphrodite" +path = "src/include/mod.rs" \ No newline at end of file diff --git a/kernel/build b/kernel/build new file mode 100755 index 0000000..cdb9e6a --- /dev/null +++ b/kernel/build @@ -0,0 +1,4 @@ +#!/bin/bash + +RUSTFLAGS='-Clink-arg=--script=link.x' cargo build --target i686-unknown-none.json --release -Zbuild-std +cp target/i686-unknown-none/release/kernel kernel.flat \ No newline at end of file diff --git a/kernel/i686-unknown-none.json b/kernel/i686-unknown-none.json new file mode 100644 index 0000000..8e6dcb5 --- /dev/null +++ b/kernel/i686-unknown-none.json @@ -0,0 +1,24 @@ +{ + "cpu": "i686", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "llvm-target": "i686-unknown-none", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "features": "-mmx,-sse,+soft-float", + "os": "none", + "arch": "x86", + "panic-strategy": "abort", + "disable-redzone": true, + "code-model": "kernel", + "crt-objects-fallback": "false", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "max-atomic-width": 32, + "metadata": { + "description": "Freestanding/bare-metal i686 softfloat", + "host_tools": false, + "std": false, + "tier": 3 + } +} \ No newline at end of file diff --git a/kernel/kernel.flat b/kernel/kernel.flat new file mode 100755 index 0000000000000000000000000000000000000000..4bad6dc6442c178e648300cd85270fe8b838e4f3 GIT binary patch literal 12 TcmeCY(d`g&jKSmezcXh5De4Ch literal 0 HcmV?d00001 diff --git a/kernel/link.x b/kernel/link.x new file mode 100644 index 0000000..232a074 --- /dev/null +++ b/kernel/link.x @@ -0,0 +1,12 @@ +ENTRY(_start) +OUTPUT_FORMAT(binary) + +SECTIONS { + .text : { + . = ALIGN(8); + KEEP(*(.multiboot2)) + KEEP(*(.start)) + KEEP(*(.text)) + KEEP(*(.panic)) + } +} diff --git a/kernel/src/include/constants.rs b/kernel/src/include/constants.rs new file mode 100644 index 0000000..fb5cc98 --- /dev/null +++ b/kernel/src/include/constants.rs @@ -0,0 +1,4 @@ +//! Constants used throughout kernel code. + +/// The assembly port number to output debug messages to. +pub const DEBUG_PORT: u8 = 0xE9; \ No newline at end of file diff --git a/kernel/src/include/mod.rs b/kernel/src/include/mod.rs new file mode 100644 index 0000000..9d67cb5 --- /dev/null +++ b/kernel/src/include/mod.rs @@ -0,0 +1,9 @@ +//! This provides syscalls(for userspace programs) and types(for userspace and kernelspace programs) for the Aphrodite kernel. + +#![no_std] +#![warn(missing_docs)] + +mod constants; +pub mod multiboot2; + +pub use constants::*; \ No newline at end of file diff --git a/kernel/src/include/multiboot2.rs b/kernel/src/include/multiboot2.rs new file mode 100644 index 0000000..8a163ac --- /dev/null +++ b/kernel/src/include/multiboot2.rs @@ -0,0 +1,229 @@ +//! Definitions of structs for multiboot2 information. Mostly used during pre-userspace. + +#![warn(missing_docs)] + +/// Used when a CString is passed. Move into separate file? +#[derive(Clone)] +pub struct CString { + /// The raw pointer to the string. + pub ptr: *const u8, + /// The length of the string, excluding the null byte(\0) at the end. + pub len: usize, +} + +impl core::ops::Index for CString { + type Output = u8; + fn index(&self, index: usize) -> &Self::Output { + unsafe { + if index>self.len { + panic!("index into CString too large"); + } + let mut ptr = self.ptr as usize; + ptr += index * size_of::(); + let ptr = ptr as *const u8; + &*ptr + } + } +} + +/// Used for Multiboot2 tags. This shouldn't be used after a [BootInfo] struct has been initalized, but it still can be used. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +#[derive(Clone)] +pub struct Tag { + /// The type of the tag. + pub tag_type: u32, + /// The length of the tag. + pub tag_len: u32, + /// A pointer to after [tag_len](Tag::tag_len). This is where most type-specific data is. + pub data_ptr: *const u8 +} + +/// The root tag. The official Multiboot2 name is literally the "fixed part" of the tags, so I made a better name. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +#[derive(Clone)] +pub struct RootTag { + /// The total length between the root tag and the terminating tag. + /// You can also search for the tag that has a type of 0 and a length of 8. + pub total_len: u32, + /// Reserved space. Unused for anything. + reserved: u32, + /// A pointer to right after the reserved space. Should be a pointer to the next tag. + pub tag_ptr: *const u8 +} + +/// A Multiboot2 module. See https://github.com/AverseABFun/aphrodite/wiki/Plan/#Bootloader-modules (remember to update link later!). +#[derive(Clone)] +pub struct Module { + /// A pointer to the start of the module + pub mod_start: *const u8, + /// A pointer to the end of the module + pub mod_end: *const u8, + /// A string that should be in the format `module_name (command line arguments)`. + /// See https://github.com/AverseABFun/aphrodite/wiki/Plan/#Bootloader-modules (remember to update link later!). + pub mod_str: CString +} + +/// All modules provided by the bootloader. Very similar to [CString]. +#[derive(Clone)] +pub struct Modules { + /// A pointer to the first module. All modules should be consecutive. + pub ptr: *const Module, + /// The number of modules. If zero, [ptr](Modules::ptr) should not be trusted! + pub modules_num: usize +} + +impl core::ops::Index for Modules { + type Output = Module; + fn index(&self, index: usize) -> &Self::Output { + unsafe { + if index>self.modules_num { + panic!("index into Modules too large"); + } + let mut ptr = self.ptr as usize; + ptr += index * size_of::(); + let ptr = ptr as *const Module; + &*ptr + } + } +} + +/// One memory section provided by a Multiboot2 bootloader. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +#[derive(Clone)] +pub struct MemorySection { + /// The base address of the section. + pub base_addr: u64, + /// The length of the section. + pub length: u64, + /// The type of the section. Name is changed from the one provided in the Multiboot2 docs + /// as "type" is a keyword in rust. + pub mem_type: u32, + /// Reserved space. Should be ignored. + reserved: u32, +} + +/// A full memory map provided by a Multiboot2 bootloader. +#[derive(Clone)] +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 +} + +/// A color descriptor for [ColorInfo::Palette]. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +pub struct PaletteColorDescriptor { + /// The red value + pub red: u8, + /// The green value + pub green: u8, + /// The blue value + pub blue: u8 +} + +/// Information about color, for use in [FramebufferInfo]. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +#[derive(Clone)] +pub enum ColorInfo { + /// The palette for use on the framebuffer. + Palette { + /// The number of colors in the palette. + num_colors: u32, + /// The first color in the palette. + palette: *const PaletteColorDescriptor + }, + /// RGB information for use on the framebuffer. + RGBColor { + /// Red color information. + red_field_position: u8, + /// See above. + red_mask_size: u8, + + /// Green color information. + green_field_position: u8, + /// See above. + green_mask_size: u8, + + /// Blue color information. + blue_field_position: u8, + /// See above. + blue_mask_size: u8, + } +} + +/// Information about the framebuffer. +#[repr(C)] +#[repr(align(1))] // may or may not be necessary, but I'm not taking chances +#[derive(Clone)] +pub struct FramebufferInfo { + /// A pointer to the framebuffer. + pub address: *mut u8, + /// The pitch of the framebuffer (i.e. the number of bytes in each row). + pub pitch: u32, + /// The width of the framebuffer. + pub width: u32, + /// The height of the framebuffer. + pub height: u32, + /// Bits per pixel. + pub bpp: u8, + /// The type of the framebuffer. 0=indexed, 1=RGB, 2=text. + pub fb_type: u8, + /// Reserved space. Ignore. + reserved: u8, + /// Color info. None if [fb_type](FramebufferInfo::fb_type) is 2. + pub color_info: Option +} + +/// Boot info collected from provided [Tag]s. +#[derive(Clone)] +pub struct BootInfo { + /// See https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#Basic-memory-information. + /// Tl;dr: mem_lower indicates the amount of "lower memory" + /// and mem_upper the amount of "upper memory". + pub mem_lower: Option, + /// See above + pub mem_upper: Option, + + // Multiboot2 bootloaders may provide us with the BIOS device and partition, but we're not interested. + // To ensure future developers don't get any ideas, I'm leaving it out here. + // If you need it, good luck. + + /// We're provided with a C-style UTF-8(null-terminated UTF-8) string. This should contain the original pointer provided by + /// the bootloader. + /// See https://github.com/AverseABFun/aphrodite/wiki/Plan#bootloader (remember to update link later!) for the format. + pub cmdline: Option, + + /// All modules provided by the bootloader. + pub modules: Option, + + // 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. + + /// 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. + /// This should contain the original pointer provided by the bootloader. + pub bootloader_name: Option, + + // APM table is ignored as APM has been superseded by ACPI. If your system doesn't support ACPI, good luck. + + // VBE table is ignored for a similar reason to above: it's deprecated. Good luck if you need it. + + pub framebuffer_info: Option, + + // Even though SMBIOS is documented for Multiboot2, we're not using it and will instead search for it ourselves. + // This is because right now I cannot figure out what format it provides the SMBIOS table in. + + +} \ No newline at end of file diff --git a/kernel/src/internal/arch/x86/entry.rs b/kernel/src/internal/arch/x86/entry.rs new file mode 100644 index 0000000..8c93576 --- /dev/null +++ b/kernel/src/internal/arch/x86/entry.rs @@ -0,0 +1,65 @@ +//! The entrypoint to the kernel; placed before all other code. +#![no_std] +#![no_main] +#![warn(missing_docs)] + +use core::{arch::asm, hint::unreachable_unchecked, panic::PanicInfo}; +use aphrodite::multiboot2::{BootInfo, RootTag, Tag}; + +#[unsafe(link_section = ".multiboot2")] +static MULTIBOOT_HEADER: [u16; 14] = [ + // Magic fields + 0xE852, 0x50D6, // Magic number + 0x0000, 0x0000, // Architecture, 0=i386 + 0x0000, 0x000E, // length of MULTIBOOT_HEADER + 0x17AD, 0xAF1C, // checksum=all magic field excluding this+this=0 + + // Framebuffer tag- empty flags, no preference for width, height, or bit depth + 0x0005, 0x0000, + 0x0014, 0x0000, + 0x0000, 0x0000, +]; + +static mut RT: *const RootTag = core::ptr::null(); +static mut BI: *const BootInfo = core::ptr::null(); +static mut O: *const u8 = core::ptr::null(); +static mut MAGIC: u32 = 0xFFFFFFFF; + +#[unsafe(link_section = ".start")] +#[unsafe(no_mangle)] +extern "C" fn _start() -> ! { + unsafe { // Copy values provided by the bootloader out + asm!( + "mov {0:e}, eax", out(reg) MAGIC // Magic number + ); + asm!( + "mov {0:e}, ebx", out(reg) O // Bootloader-specific data + ); + } + unsafe { + match MAGIC { + 0x36d76289 => { // Multiboot2 + RT = O as *const RootTag; // This is unsafe rust! We can do whatever we want! *manical laughter* + + + }, + _ => { // Unknown bootloader, triple fault + asm!( + "lidt 0", // Make interrupt table invalid(may or may not be invalid, depending on the bootloader, but we don't know) + "int 0h" // Try to perform an interrupt + // CPU then triple faults, thus restarting it + ) + } + } + } + loop {} +} + +#[unsafe(link_section = ".panic")] +#[panic_handler] +fn handle_panic(_: &PanicInfo) -> ! { + unsafe { + asm!("hlt"); + unreachable_unchecked(); + } +} \ No newline at end of file