wow i did a lot of stuff silly :3

This commit is contained in:
Arthur Beck 2025-01-20 14:38:40 -06:00
parent c7cc51427b
commit 69855525cb
23 changed files with 1499 additions and 83 deletions

8
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"rust-analyzer.cargo.allTargets": false,
"rust-analyzer.check.allTargets": false,
"rust-analyzer.linkedProjects": [
"kernel/Cargo.toml",
"patcher/Cargo.toml"
]
}

@ -1 +0,0 @@
Subproject commit fd6f101699b11129be91c1ac8cb67bebb65a5c9b

View file

@ -0,0 +1,5 @@
[build]
target = "i686-unknown-none.json"
[unstable]
build-std = ["core"]

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
paste = "1.0.15"
[profile.release]
opt-level = "z"
@ -19,3 +20,7 @@ path = "src/internal/arch/x86/entry.rs"
[lib]
name = "aphrodite"
path = "src/include/mod.rs"
[[test]]
name = "test_aphrodite"
path = "src/include/test.rs"

View file

@ -1,4 +1,10 @@
#!/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
RUSTFLAGS='-Clink-arg=--script=link.x' cargo build --target i686-unknown-none.json --release -Zbuild-std --bin entrypoint
# build the kernel's entrypoint
cp target/i686-unknown-none/release/entrypoint kernel.flat
# copy it out
/home/arthur/aphrodite/patcher/target/release/patcher
# run the custom patching program to add the multiboot2 header

1076
kernel/disassembly Normal file

File diff suppressed because it is too large Load diff

3
kernel/expand Executable file
View file

@ -0,0 +1,3 @@
#!/bin/bash
RUSTFLAGS='-Clink-arg=--script=link.x' cargo expand --target i686-unknown-none.json --release -Zbuild-std --bin entrypoint

Binary file not shown.

View file

@ -4,7 +4,6 @@ OUTPUT_FORMAT(binary)
SECTIONS {
.text : {
. = ALIGN(8);
KEEP(*(.multiboot2))
KEEP(*(.start))
KEEP(*(.text))
KEEP(*(.panic))

View file

@ -0,0 +1,3 @@
//! Arch-specific code.
pub mod x86;

View file

@ -0,0 +1,5 @@
//! Constants used throughout kernel code.
#![cfg(any(target_arch = "x86"))]
/// The assembly port number to output debug messages to.
pub const DEBUG_PORT: u16 = 0xE9;

View file

@ -0,0 +1,61 @@
//! Provides interrupt-related functions
#![cfg(any(target_arch = "x86"))]
use core::arch::asm;
/// Returns whether interrupts are enabled or not.
pub fn interrupts_enabled() -> bool {
let flags: u32;
unsafe {
asm!(
"pushf",
"pop {0:e}", out(reg) flags
)
}
(flags & (1 << 9)) == 0
}
/// Disables interrupts and returns the value of them.
pub fn pop_irq() -> u32 {
let flags: u32;
unsafe {
asm!(
"pushf",
"cli",
"pop {0:e}", out(reg) flags
)
}
flags
}
/// Restores interrupts after a [pop_irq] call.
pub fn restore_irq(flags: u32) {
unsafe {
asm!(
"push {0:e}", in(reg) flags
);
asm!(
"popf"
);
}
}
/// The IDTR. Used internally in [load_idt].
#[repr(C)]
struct IDTR {
base: *const u8,
size: usize
}
/// Loads an interrupt descriptor table.
pub fn load_idt(base: *const u8, size: usize) {
let idtr = IDTR {
base,
size
};
unsafe {
asm!(
"lidt {}", in(reg) &idtr
)
}
}

View file

@ -0,0 +1,26 @@
//! General x86 functions
#![cfg(any(target_arch = "x86"))]
use core::arch::asm;
pub mod interrupts;
pub mod ports;
pub mod output;
mod constants;
pub use constants::*;
/// Returns information from the CPUID command in the form
/// (ebx, edx, ecx).
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
)
}
out
}

View file

@ -0,0 +1,30 @@
//! Functions to output to various things
#![cfg(any(target_arch = "x86"))]
use super::ports;
use paste::paste;
macro_rules! message_funcs {
($func_name:ident, $prefix:literal) => {
paste! {
/// Outputs a $func_name message &str to the debug serial port.
pub fn [< s $func_name s >](s: &str) {
ports::outbs(super::DEBUG_PORT, $prefix.as_bytes());
ports::outbs(super::DEBUG_PORT, s.as_bytes());
}
/// Outputs a $func_name message &\[u8] to the debug serial port.
pub fn [< s $func_name b >](s: &[u8]) {
ports::outbs(super::DEBUG_PORT, $prefix.as_bytes());
ports::outbs(super::DEBUG_PORT, s);
}
}
}
}
message_funcs!(debug, "[DEBUG] ");
message_funcs!(info, "[INFO] ");
message_funcs!(warning, "[WARN] ");
message_funcs!(error, "[ERROR] ");
message_funcs!(fatal, "[FATAL] ");

View file

@ -0,0 +1,39 @@
//! Provides utilities for interacting with assembly ports
#![cfg(any(target_arch = "x86"))]
use core::arch::asm;
/// Outputs a byte to an IO port
#[inline(always)]
pub fn outb(port: u16, val: u8) {
unsafe {
asm!(
"out dx, al", in("dx") port, in("al") val
)
}
}
/// Outputs an arbitrary number of bytes to an IO port
pub fn outbs(port: u16, val: &[u8]) {
for ele in val {
outb(port, *ele);
}
}
/// Reads a byte from an IO port
#[inline(always)]
pub fn inb(port: u16) -> u8 {
let out;
unsafe {
asm!(
"in {}, {1:x}", out(reg_byte) out, in(reg) port
)
}
out
}
/// Wait a short, indeterminable time
#[inline(always)]
pub fn io_wait() {
outb(0x80, 0);
}

View file

@ -1,4 +1 @@
//! Constants used throughout kernel code.
/// The assembly port number to output debug messages to.
pub const DEBUG_PORT: u8 = 0xE9;

View file

@ -0,0 +1,44 @@
//! Stuff related to errors.
/// Converts an i16 to an [u8; 6].
pub fn i16_as_u8_slice(mut value: i16) -> [u8; 6] {
let mut buf = [0u8; 6];
let mut i = 0;
if value < 0 {
buf[i] = b'-';
value = -value;
}
i = 5;
while value > 0 {
let digit = value%10;
let char = b'0' + digit as u8;
buf[i] = char;
value = value / 10;
i -= 1;
}
buf
}
/// An error used by aphrodite
pub struct Error<'a> {
message: &'a str,
code: i16
}
impl core::fmt::Debug for Error<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(core::str::from_utf8(&i16_as_u8_slice(self.code)).unwrap())?;
f.write_str(": ")?;
f.write_str(self.message)
}
}
impl core::fmt::Display for Error<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(core::str::from_utf8(&i16_as_u8_slice(self.code)).unwrap())?;
f.write_str(": ")?;
f.write_str(self.message)
}
}
impl core::error::Error for Error<'_> {}

View file

@ -1,9 +1,14 @@
//! This provides syscalls(for userspace programs) and types(for userspace and kernelspace programs) for the Aphrodite kernel.
#![no_std]
#![warn(missing_docs)]
#![feature(ptr_metadata)]
mod constants;
pub mod multiboot2;
pub mod arch;
mod errors;
#[allow(unused_imports)] // if there are no constants, then it gives a warning
pub use constants::*;
pub use errors::*;

View file

@ -1,7 +1,5 @@
//! 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 {
@ -61,30 +59,6 @@ pub struct Module {
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<usize> 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::<Module>();
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
@ -101,6 +75,23 @@ pub struct MemorySection {
reserved: u32,
}
/// The raw memory map provided by a Multiboot2 bootloader. This is interpreted
/// into a [MemoryMap].
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
pub struct RawMemoryMap {
/// The type of the tag.
pub tag_type: u32,
/// The length of the tag.
pub tag_len: u32,
/// Size of one entry(one [MemorySection] for Aphrodite)
pub entry_size: u32,
/// The version of the memory map. Should be disregarded as it's 0.
pub entry_version: u32, // currently is 0, future Multiboot2 versions may increment
/// The sections. This is the reason that [Clone] can't be implemented for [RawMemoryMap].
pub sections: [MemorySection]
}
/// A full memory map provided by a Multiboot2 bootloader.
#[derive(Clone)]
pub struct MemoryMap {
@ -117,6 +108,7 @@ pub struct MemoryMap {
/// A color descriptor for [ColorInfo::Palette].
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone, Copy)]
pub struct PaletteColorDescriptor {
/// The red value
pub red: u8,
@ -127,9 +119,9 @@ pub struct PaletteColorDescriptor {
}
/// Information about color, for use in [FramebufferInfo].
#[repr(C)]
#[repr(u8)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
#[derive(Clone, Copy)]
pub enum ColorInfo {
/// The palette for use on the framebuffer.
Palette {
@ -154,7 +146,9 @@ pub enum ColorInfo {
blue_field_position: u8,
/// See above.
blue_mask_size: u8,
}
},
/// Text information, no metadata
EGAText
}
/// Information about the framebuffer.
@ -162,6 +156,10 @@ pub enum ColorInfo {
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
pub struct FramebufferInfo {
/// 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,
/// A pointer to the framebuffer.
pub address: *mut u8,
/// The pitch of the framebuffer (i.e. the number of bytes in each row).
@ -176,8 +174,9 @@ pub struct FramebufferInfo {
pub fb_type: u8,
/// Reserved space. Ignore.
reserved: u8,
/// Color info. None if [fb_type](FramebufferInfo::fb_type) is 2.
pub color_info: Option<ColorInfo>
// Color info after this; we need separate structs for each colorinfo and
// we have to understand the format the bootloader gives us.
}
/// Boot info collected from provided [Tag]s.
@ -199,8 +198,8 @@ pub struct BootInfo {
/// See https://github.com/AverseABFun/aphrodite/wiki/Plan#bootloader (remember to update link later!) for the format.
pub cmdline: Option<CString>,
/// All modules provided by the bootloader.
pub modules: Option<Modules>,
// 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.
@ -218,6 +217,8 @@ pub struct BootInfo {
/// Provides information on the framebuffer.
pub framebuffer_info: Option<FramebufferInfo>,
/// Color info, stored separately from [FramebufferInfo] because rust
pub color_info: Option<ColorInfo>,
// 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.

View file

@ -0,0 +1,2 @@
//! Tests for the kernel.
#![no_std]

View file

@ -2,23 +2,10 @@
#![no_std]
#![no_main]
#![warn(missing_docs)]
#![feature(ptr_metadata)]
use core::{arch::asm, ffi::CStr, hint::unreachable_unchecked, panic::PanicInfo};
use aphrodite::multiboot2::{BootInfo, CString, 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,
];
use core::{arch::asm, ffi::CStr, panic::PanicInfo};
use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag};
// The root tag, provided directly from the multiboot bootloader.
static mut RT: *const RootTag = core::ptr::null();
@ -27,10 +14,10 @@ static mut BI: BootInfo = BootInfo {
mem_lower: None,
mem_upper: None,
cmdline: None,
modules: None,
memory_map: None,
bootloader_name: None,
framebuffer_info: None
framebuffer_info: None,
color_info: None,
};
// The raw pointer to bootloader-specific data.
@ -64,24 +51,27 @@ extern "C" fn _start() -> ! {
match (*current_tag).tag_type {
0 => { // Ending tag
if (*current_tag).tag_len != 8 { // Unexpected size, something is probably up
panic!("Size of ending tag != 8");
panic!("size of ending tag != 8");
}
break
},
4 => { // Basic memory information
if (*current_tag).tag_len != 16 { // Unexpected size, something is probably up
panic!("Size of basic memory information tag != 16");
panic!("size of basic memory information tag != 16");
}
BI.mem_lower = Some(*((current_tag as usize + 8) as *const u32));
BI.mem_upper = Some(*((current_tag as usize + 12) as *const u32));
// The end result of the above is adding an offset to a pointer and retrieving the value at that pointer
current_tag = (current_tag as usize + 16) as *const Tag;
},
5 => { // BIOS boot device, ignore
if (*current_tag).tag_len != 20 { // Unexpected size, something is probably up
panic!("size of bios boot device tag != 20");
}
},
1 => { // Command line
if (*current_tag).tag_len < 8 { // Unexpected size, something is probably up
panic!("Size of command line tag < 8");
panic!("size of command line tag < 8");
}
let cstring = CStr::from_ptr((current_tag as usize + 8) as *const i8);
// creates a &core::ffi::CStr from the start of the command line...
@ -92,35 +82,100 @@ extern "C" fn _start() -> ! {
};
// ...which can then be converted to a aphrodite::multiboot2::CString...
current_tag = (current_tag as usize + 8 + cstring.len) as *const Tag;
// ...before the current_tag is incremented to prevent ownership issues...
BI.cmdline = Some(cstring);
// ...before lastly the BootInfo's commandline is set.
// ...before the BootInfo's commandline is set.
},
_ => { // Unknown tag type
todo!("Implement tag");
6 => { // Memory map tag
if (*current_tag).tag_len < 16 { // Unexpected size, something is probably up
panic!("size of memory map tag < 16");
}
let rawmemorymap: *const RawMemoryMap = core::ptr::from_raw_parts(
current_tag, ((*current_tag).tag_len / *((current_tag as usize + 8usize) as *const u32)) as usize
);
// The end result of the above is creating a *const RawMemoryMap that has the same address as current_tag
// and has all of the [aphrodite::multiboot2::MemorySection]s for the memory map
BI.memory_map = Some(MemoryMap {
version: (*rawmemorymap).entry_version,
entry_size: (*rawmemorymap).entry_size,
sections: &(*rawmemorymap).sections[0],
sections_len: (*rawmemorymap).sections.len()
});
},
2 => { // Bootloader name
if (*current_tag).tag_len < 8 { // Unexpected size, something is probably up
panic!("size of command line tag < 8");
}
let cstring = CStr::from_ptr((current_tag as usize + 8) as *const i8);
// creates a &core::ffi::CStr from the start of the bootloader name...
let cstring = CString {
ptr: cstring.as_ptr() as *const u8,
len: cstring.to_bytes().len()
};
// ...which can then be converted to a aphrodite::multiboot2::CString...
BI.bootloader_name = Some(cstring);
// ...before the BootInfo's bootloader_name is set.
},
8 => { // Framebuffer info
if (*current_tag).tag_len < 40 { // Unexpected size, something is probably up
panic!("size of framebuffer info tag < 40");
}
let framebufferinfo: *const FramebufferInfo = current_tag as *const FramebufferInfo;
let colorinfo: ColorInfo;
match (*framebufferinfo).fb_type {
0 => { // Indexed
colorinfo = ColorInfo::Palette {
num_colors: *((current_tag as usize + 40) as *const u32),
palette: (current_tag as usize + 44) as *const PaletteColorDescriptor
};
},
1 => { // RGB
colorinfo = ColorInfo::RGBColor {
red_field_position: *((current_tag as usize + 40) as *const u8),
red_mask_size: *((current_tag as usize + 41) as *const u8),
green_field_position: *((current_tag as usize + 42) as *const u8),
green_mask_size: *((current_tag as usize + 43) as *const u8),
blue_field_position: *((current_tag as usize + 44) as *const u8),
blue_mask_size: *((current_tag as usize + 45) as *const u8)
}
},
_ => { // 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
)
2 => { // EGA Text
colorinfo = ColorInfo::EGAText;
},
_ => {
unreachable!();
}
}
BI.framebuffer_info = Some((*framebufferinfo).clone());
BI.color_info = Some(colorinfo);
},
_ => { // Unknown/unimplemented tag type, ignore
// TODO: Add info message
}
}
current_tag = (current_tag as usize + (*current_tag).tag_len as usize) as *const Tag;
}
},
_ => { // Unknown bootloader, panic
panic!("unknown bootloader");
}
}
}
loop {}
panic!("kernel exited");
}
#[unsafe(link_section = ".panic")]
#[panic_handler]
fn handle_panic(_: &PanicInfo) -> ! {
fn handle_panic(info: &PanicInfo) -> ! {
let message = info.message().as_str().unwrap_or("");
if message != "" {
aphrodite::arch::x86::output::sfatals(message);
aphrodite::arch::x86::ports::outb(aphrodite::arch::x86::DEBUG_PORT, b'\n');
}
unsafe {
asm!("hlt");
unreachable_unchecked();
asm!("hlt", options(noreturn));
}
}

6
patcher/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "patcher"
version = "0.1.0"
edition = "2024"
[dependencies]

41
patcher/src/main.rs Normal file
View file

@ -0,0 +1,41 @@
use std::{fs, io::Write};
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
];
fn from_u16(from: &mut [u16]) -> &[u8] {
if cfg!(target_endian = "little") {
for byte in from.iter_mut() {
*byte = byte.to_be();
}
}
let len = from.len().checked_mul(2).unwrap();
let ptr: *const u8 = from.as_ptr().cast();
unsafe { std::slice::from_raw_parts(ptr, len) }
}
fn main() {
let path = "./kernel.flat";
let mut buf = fs::read(path).unwrap();
buf = [
from_u16(&mut (MULTIBOOT_HEADER.clone())).to_vec(),
buf
].concat();
fs::OpenOptions::new()
.write(true)
.open(path)
.unwrap()
.write(buf.as_mut_slice())
.unwrap();
}