Implemented GDT and messed with some stuff. Going to try to fix the memory allocator next.
This commit is contained in:
parent
a5b94ff5a7
commit
7ae1677a87
11 changed files with 351 additions and 10 deletions
|
@ -1,6 +1,6 @@
|
||||||
[build]
|
[build]
|
||||||
target = "i686-unknown-none.json"
|
target = "i686-unknown-none.json"
|
||||||
rustflags = ["-Clink-arg=--script=link.x"]
|
rustflags = ["-Clink-arg=--script=link.x", "-Clink-arg=--gc-sections"]
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["core", "alloc"]
|
build-std = ["core", "alloc"]
|
|
@ -7,6 +7,10 @@
|
||||||
//! actual architecture, of course):
|
//! actual architecture, of course):
|
||||||
//! #![cfg(any(target_arch = "arch"))]
|
//! #![cfg(any(target_arch = "arch"))]
|
||||||
|
|
||||||
|
pub const fn get_arch() -> super::Architecture {
|
||||||
|
super::Architecture::ExampleDummy
|
||||||
|
}
|
||||||
|
|
||||||
pub mod interrupts {
|
pub mod interrupts {
|
||||||
//! Interrupt-related functions.
|
//! Interrupt-related functions.
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,14 @@ mod x86;
|
||||||
pub mod example_impl;
|
pub mod example_impl;
|
||||||
|
|
||||||
pub use x86::*;
|
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,
|
||||||
|
}
|
61
kernel/src/kernel/arch/x86/gdt.rs
Normal file
61
kernel/src/kernel/arch/x86/gdt.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
/// Disables interrupts and returns the value of them.
|
||||||
#[aphrodite_proc_macros::kernel_item(InterruptsPop)]
|
#[aphrodite_proc_macros::kernel_item(InterruptsPop)]
|
||||||
pub fn pop_irq() -> u64 {
|
pub fn pop_irq() -> PoppedInterrupts {
|
||||||
let flags: u32;
|
let flags: u32;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!(
|
asm!(
|
||||||
|
@ -39,13 +51,13 @@ pub fn pop_irq() -> u64 {
|
||||||
"pop {0:e}", out(reg) flags
|
"pop {0:e}", out(reg) flags
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
flags as u64
|
PoppedInterrupts(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restores interrupts after a [pop_irq] call.
|
/// Restores interrupts after a [pop_irq] call.
|
||||||
#[aphrodite_proc_macros::kernel_item(InterruptsRestore)]
|
#[aphrodite_proc_macros::kernel_item(InterruptsRestore)]
|
||||||
pub fn restore_irq(flags: u64) {
|
pub fn restore_irq(flags: PoppedInterrupts) {
|
||||||
let flags = flags as u32;
|
let flags = flags.0;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!(
|
asm!(
|
||||||
"push {0:e}", in(reg) flags
|
"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();
|
let mem = alloc.allocate(unsafe { Layout::from_size_align_unchecked(8*idt.len, 1) }).unwrap().as_mut_ptr();
|
||||||
for i in 0..idt.len {
|
for i in 0..idt.len {
|
||||||
let vector = idt.vectors[i];
|
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 user_callable = idt.user_callable[i];
|
||||||
let output: u64 = (func & 0b1111111111111111) as u64;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
174
kernel/src/kernel/arch/x86/memory.rs
Normal file
174
kernel/src/kernel/arch/x86/memory.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,8 @@ pub mod ports;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
pub mod egatext;
|
pub mod egatext;
|
||||||
pub mod paging;
|
pub mod paging;
|
||||||
|
mod gdt;
|
||||||
|
pub mod memory;
|
||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
|
|
||||||
|
@ -15,6 +17,10 @@ pub(self) use constants::*;
|
||||||
use interrupts::{pop_irq, restore_irq};
|
use interrupts::{pop_irq, restore_irq};
|
||||||
use ports::{inb, outb};
|
use ports::{inb, outb};
|
||||||
|
|
||||||
|
pub const fn get_arch() -> super::Architecture {
|
||||||
|
super::Architecture::X86
|
||||||
|
}
|
||||||
|
|
||||||
#[aphrodite_proc_macros::kernel_item(PagingAvailabe)]
|
#[aphrodite_proc_macros::kernel_item(PagingAvailabe)]
|
||||||
pub fn paging_available() -> bool {
|
pub fn paging_available() -> bool {
|
||||||
true
|
true
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub struct Error<'a> {
|
||||||
|
|
||||||
impl<'a> Error<'a> {
|
impl<'a> Error<'a> {
|
||||||
/// Creates a new error.
|
/// 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 }
|
Error { message, code }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ fn indep_boot_entry(
|
||||||
display: Option<&dyn crate::display::TextDisplay>,
|
display: Option<&dyn crate::display::TextDisplay>,
|
||||||
#[allow(non_snake_case)] BI: &crate::boot::BootInfo,
|
#[allow(non_snake_case)] BI: &crate::boot::BootInfo,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
|
assert_ne!(crate::arch::get_arch(), crate::arch::Architecture::ExampleDummy);
|
||||||
crate::arch::output::sdebugsln("IndepBootEntry called");
|
crate::arch::output::sdebugsln("IndepBootEntry called");
|
||||||
|
|
||||||
let display = display.unwrap_or(&NoneTextDisplay {});
|
let display = display.unwrap_or(&NoneTextDisplay {});
|
||||||
|
|
69
kernel/src/kernel/memsections.rs
Normal file
69
kernel/src/kernel/memsections.rs
Normal 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>>;
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
//! This provides raw methods for internal kernel usage for the Aphrodite kernel. See aphrodite_user for userspace.
|
//! This provides raw methods for internal kernel usage for the Aphrodite kernel. See aphrodite_user for userspace.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
#![warn(rustdoc::missing_crate_level_docs)]
|
||||||
|
#![deny(rustdoc::invalid_html_tags)]
|
||||||
|
#![deny(rustdoc::invalid_rust_codeblocks)]
|
||||||
// tidy-alphabetical-start
|
// tidy-alphabetical-start
|
||||||
#![feature(ptr_metadata)]
|
#![feature(ptr_metadata)]
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
|
@ -31,9 +34,10 @@ pub mod output;
|
||||||
pub mod psfont;
|
pub mod psfont;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod util;
|
mod util;
|
||||||
|
pub mod memsections;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod cfg;
|
pub(crate) mod cfg;
|
||||||
|
|
||||||
#[allow(unused_imports)] // if there are no constants, then it gives a warning
|
#[allow(unused_imports)] // if there are no constants, then it gives a warning
|
||||||
pub use constants::*;
|
pub use constants::*;
|
||||||
|
|
Loading…
Add table
Reference in a new issue