Implemented GDT and messed with some stuff. Going to try to fix the memory allocator next.

This commit is contained in:
Arthur Beck 2025-02-16 18:30:39 -06:00
parent a5b94ff5a7
commit 7ae1677a87
Signed by: ArthurB
GPG key ID: ACE3D14F5CEF14BF
11 changed files with 351 additions and 10 deletions

View file

@ -1,6 +1,6 @@
[build]
target = "i686-unknown-none.json"
rustflags = ["-Clink-arg=--script=link.x"]
rustflags = ["-Clink-arg=--script=link.x", "-Clink-arg=--gc-sections"]
[unstable]
build-std = ["core", "alloc"]

View file

@ -7,6 +7,10 @@
//! actual architecture, of course):
//! #![cfg(any(target_arch = "arch"))]
pub const fn get_arch() -> super::Architecture {
super::Architecture::ExampleDummy
}
pub mod interrupts {
//! Interrupt-related functions.

View file

@ -6,3 +6,14 @@ mod x86;
pub mod example_impl;
pub use x86::*;
/// The enum returned by arch::*::get_arch.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum Architecture {
/// Returned by [example_impl]. If this is returned by arch::*::get_arch, something
/// is incredibly wrong and a panic should occur immediately.
#[default]
ExampleDummy,
/// 32-bit x86.
X86,
}

View file

@ -0,0 +1,61 @@
//! GDT initalization.
use core::alloc::Layout;
use alloc::vec::Vec;
/// Writes a series of GDT entries to an allocated section of memory and returns a pointer.
pub unsafe fn write_gdt_entries(entries: Vec<GDTEntry>) -> Result<*const [u8], crate::Error<'static>> {
let mut mem = 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()))
}
/// A GDT entry.
#[derive(Clone, Copy)]
pub struct GDTEntry {
/// The size of the entry. Has to be less than 0xFFFFF.
pub limit: u32,
/// The base address of the entry.
pub base: u32,
/// The access byte of the entry.
pub access: u8,
/// The flags of the entry.
pub flags: u8,
}
/// An error returned by [GDTEntry::write_to_addr] when the limit is greater than 0xFFFFF.
const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1;
impl GDTEntry {
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 serialized = (0u64).to_ne_bytes();
serialized[0] = (self.limit & 0xFF) as u8;
serialized[1] = ((self.limit >> 8) & 0xFF) as u8;
serialized[6] = ((self.limit >> 16) & 0x0F) 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;
serialized[5] = self.access;
serialized[6] |= self.flags << 4;
unsafe {
core::ptr::write(ptr as *mut [u8; 8], serialized);
}
Ok(())
}
}

View file

@ -28,9 +28,21 @@ pub fn disable_interrupts() {
}
}
/// 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 [Option::unwrap].
#[derive(Clone)]
pub struct PoppedInterrupts(u32);
impl Drop for PoppedInterrupts {
fn drop(&mut self) {
restore_irq(self.clone());
}
}
/// Disables interrupts and returns the value of them.
#[aphrodite_proc_macros::kernel_item(InterruptsPop)]
pub fn pop_irq() -> u64 {
pub fn pop_irq() -> PoppedInterrupts {
let flags: u32;
unsafe {
asm!(
@ -39,13 +51,13 @@ pub fn pop_irq() -> u64 {
"pop {0:e}", out(reg) flags
)
}
flags as u64
PoppedInterrupts(flags)
}
/// Restores interrupts after a [pop_irq] call.
#[aphrodite_proc_macros::kernel_item(InterruptsRestore)]
pub fn restore_irq(flags: u64) {
let flags = flags as u32;
pub fn restore_irq(flags: PoppedInterrupts) {
let flags = flags.0;
unsafe {
asm!(
"push {0:e}", in(reg) flags
@ -87,9 +99,8 @@ fn activate_idt(idt: Idt, alloc: crate::mem::MemoryMapAlloc) {
let mem = alloc.allocate(unsafe { Layout::from_size_align_unchecked(8*idt.len, 1) }).unwrap().as_mut_ptr();
for i in 0..idt.len {
let vector = idt.vectors[i];
let func = unsafe { idt.funcs[i].assume_init() } as usize as u32;
let func = unsafe { idt.funcs[i].assume_init() } as usize as u64;
let user_callable = idt.user_callable[i];
let output: u64 = (func & 0b1111111111111111) as u64;
}
}

View file

@ -0,0 +1,174 @@
//! Hardware-level memory sections. Unimplemented for certain hardware, x86 implements with GDT.
use core::arch::asm;
use alloc::{vec, vec::Vec};
use crate::memsections::*;
use super::gdt::{write_gdt_entries, GDTEntry};
pub struct MemorySections {
sections: Vec<MemorySection>,
}
#[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<GDTEntry> = 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<GDTEntry>) {
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<GDTEntry> = 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 <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels>
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<MemorySection>,
}
impl MemorySectionBuilder {
pub fn new() -> Self {
MemorySectionBuilder { sections: vec![] }
}
pub fn add_section(&mut self, section: MemorySection) -> &mut Self {
self.sections.push(section);
self
}
pub fn finish(self) -> MemorySections {
MemorySections {
sections: self.sections,
}
}
}

View file

@ -8,6 +8,8 @@ pub mod ports;
pub mod output;
pub mod egatext;
pub mod paging;
mod gdt;
pub mod memory;
mod constants;
@ -15,6 +17,10 @@ pub(self) use constants::*;
use interrupts::{pop_irq, restore_irq};
use ports::{inb, outb};
pub const fn get_arch() -> super::Architecture {
super::Architecture::X86
}
#[aphrodite_proc_macros::kernel_item(PagingAvailabe)]
pub fn paging_available() -> bool {
true

View file

@ -11,7 +11,7 @@ pub struct Error<'a> {
impl<'a> Error<'a> {
/// Creates a new error.
pub fn new(message: &'a str, code: i16) -> Self {
pub const fn new(message: &'a str, code: i16) -> Self {
Error { message, code }
}
}

View file

@ -17,6 +17,7 @@ fn indep_boot_entry(
display: Option<&dyn crate::display::TextDisplay>,
#[allow(non_snake_case)] BI: &crate::boot::BootInfo,
) -> ! {
assert_ne!(crate::arch::get_arch(), crate::arch::Architecture::ExampleDummy);
crate::arch::output::sdebugsln("IndepBootEntry called");
let display = display.unwrap_or(&NoneTextDisplay {});

View file

@ -0,0 +1,69 @@
//! Architecture-independt memory section stuff.
//!
//! arch::*::memory is the architecture-dependent counterpart.
/// Section types for [MemorySection].
/// If the architecture doesn't support, for example, writing
/// to a code section, then this should take precedence over
/// [MemorySection] attributes.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SectionType {
/// A code section. Generally at least one of these for the kernel.
CodeSection {
/// Whether more powerful owners can jump to this if the owner
/// is less powerful.
can_powerful_sections_jump: bool
},
/// A data section. Generally at least one of these for the kernel.
DataSection,
/// A [task state section](https://wiki.osdev.org/Task_State_Segment).
/// These can be interpreted as [DataSection](SectionType::DataSection)s
/// if they aren't directly supported by the hardware.
TaskSection {
/// Whether the section is busy.
busy: bool
},
}
/// The owner of a [MemorySection].
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Owner {
/// Userspace.
Userspace,
/// Kernelspace.
Kernelspace,
/// Modulespace.
Modulespace,
}
/// A memory section.
#[derive(Clone, Copy)]
pub struct MemorySection {
/// The type of section.
pub section_type: SectionType,
/// The owner of the section.
pub owner: Owner,
/// Whether the kernel should minimize reads to the section.
pub minimal_read: bool,
/// Whether the segment is readable.
pub readable: bool,
/// Whether the segment is writable.
pub writable: bool,
/// The base address.
pub address: u64,
/// The length. If the implementation has a maximum length of
/// sections, it should be automatically split into however many sections are necessary.
pub length: u64,
}
/// Implemented by arch::*::memory::MemorySections. Note to implementers:
/// Copy should NOT be implemented. That would lead to issues where a
/// struct implementing this trait could be used after [write](MemorySections::write)
/// is called, which is not supposed to happen.
pub unsafe trait MemorySections {
/// Write the sections to an allocated region and then activate them.
///
/// This intentionally takes ownership of the MemorySections as it
/// shouldn't be used after this is called.
unsafe fn write(self) -> Result<(), crate::Error<'static>>;
}

View file

@ -1,6 +1,9 @@
//! This provides raw methods for internal kernel usage for the Aphrodite kernel. See aphrodite_user for userspace.
#![no_std]
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
#![deny(rustdoc::invalid_html_tags)]
#![deny(rustdoc::invalid_rust_codeblocks)]
// tidy-alphabetical-start
#![feature(ptr_metadata)]
#![feature(const_trait_impl)]
@ -31,9 +34,10 @@ pub mod output;
pub mod psfont;
mod traits;
mod util;
pub mod memsections;
#[macro_use]
mod cfg;
pub(crate) mod cfg;
#[allow(unused_imports)] // if there are no constants, then it gives a warning
pub use constants::*;