Compare commits

...

3 commits

Author SHA1 Message Date
11f8ac03c6
Modified bochsrc
Some checks failed
Continuous Integration / ci (push) Failing after 3m36s
Format code / Format the kernel (push) Failing after 26m6s
2025-04-19 09:08:01 -05:00
59c9daf02f
I've been doing a lot of work on this and GDT is almost but not quite working 2025-04-19 09:07:34 -05:00
842a2b23fe
Removed proc macros(for now) as kernel items are pretty much useless, added untested interrupts stuff 2025-04-05 17:32:51 -05:00
32 changed files with 350 additions and 402 deletions

View file

@ -2,6 +2,7 @@ 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

View file

@ -2,6 +2,7 @@ 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

View file

@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
paste = "1.0.15"
aphrodite_proc_macros = { path = "./aphrodite_proc_macros"}
[profile.release]
opt-level = "z"

View file

@ -1,12 +0,0 @@
[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"

View file

@ -1,14 +0,0 @@
[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"

View file

@ -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 2>/dev/null
cargo build --target "$real_target" --release -Zbuild-std=core,alloc --bin entrypoint_$target
cp "target/$(echo $target_json | sed 's/\.json//')/release/entrypoint_$target" kernel-$target
if [[ "$CONFIG_BUILD_GRUB" = "true" ]]; then

View file

@ -1,4 +1,6 @@
fn main() {
fn main() -> Result<(), std::io::Error> {
println!("cargo:rerun-if-changed=src/kernel/arch/x86/change_code_segment.s");
let env = std::env::vars();
// Begin checks
@ -49,6 +51,10 @@ fn main() {
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
@ -62,4 +68,20 @@ fn main() {
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(())
}

View file

@ -19,7 +19,7 @@
real_check=false
if [[ "$HAVE_GETOPT" = "true" ]]; then
LONGOPTS=real_check
LONGOPTS=real_check,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)
-c|--real_check|--real-check)
real_check=true
shift
;;

View file

@ -14,6 +14,7 @@ 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
@ -44,4 +45,7 @@ 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

View file

@ -27,7 +27,7 @@ if [[ "$1" = "x86" ]]; then
sed -i "s@%{BUILT_FILE}@aphrodite-$1.iso@g" bochsrc
bochs -q
bochs -q -debugger
else
if [[ "$TARGETS" =~ "$1" ]]; then
echo "[ERROR] Cannot emulate specified architecture \"$1\"."

View file

@ -6,8 +6,11 @@ insmod gfxterm
insmod efi_uga
insmod efi_gop
menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' {
menuentry "Aphrodite (default)" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-%{VERSION}' {
echo 'Loading Aphrodite aphrodite-%{VERSION}...'
multiboot2 /boot/aphrodite.kernel
if multiboot2 /boot/aphrodite.kernel; then
boot
else
echo 'Error loading kernel; not attempting to boot'
fi
}

View file

@ -17,11 +17,9 @@ 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")]
@ -34,7 +32,7 @@ static MULTIBOOT2_HEADER: [u8; 48] = [
0x0A, 0x00, // Relocatable tag
0x00, 0x00, // Flags,
0x18, 0x00, 0x00, 0x00, // Size of tag
0x00, 0x00, 0x00, 0xB0, // Starting minimum location
0x00, 0x00, 0x00, 0x00, // 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
@ -70,7 +68,6 @@ 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
@ -313,7 +310,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() {
@ -332,20 +329,14 @@ 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::IndepBootEntry(Some(ega), &BI);
aphrodite::indep_boot_entry::indep_boot_entry(Some(ega), &BI);
}
}
aphrodite::indep_boot_entry::IndepBootEntry(None, &BI);
aphrodite::indep_boot_entry::indep_boot_entry(None, &BI);
}
#[unsafe(link_section = ".panic")]

View file

@ -1 +0,0 @@

View file

@ -23,28 +23,22 @@ 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.

View file

@ -4,39 +4,88 @@
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 {
base: *const u8,
size: usize,
// raw pointer to the GDT
base: u32,
// size of the GDT in bytes
size: u16,
}
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]) {
let gdtr = Gdtr {
base: ptr as *const u8,
size: ptr.len(),
};
unsafe { asm!("lgdt {}", in(reg) (&gdtr) as *const Gdtr as usize) }
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));
}
}
/// 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>,
entries: &[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 ())? }
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);
}
mem = (mem as usize + 8) as *mut u8;
}
Ok(core::ptr::from_raw_parts(mem, 8 * entries.len()))
}
const fn concat_arrays<T, const M: usize, const N: usize>(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 {
@ -59,35 +108,52 @@ pub const GDT_NULL_ENTRY: GDTEntry = GDTEntry {
/// An error returned by [GDTEntry::write_to_addr] when the limit is greater
/// than 0xFFFFF.
const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1;
pub const GDT_WRITE_ADDR_INVALID_LIMIT: i16 = -1;
impl GDTEntry {
const unsafe fn write_to_addr(self, ptr: *mut ()) -> Result<(), crate::Error<'static>> {
const fn serialize(self) -> Result<[u8; 8], 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();
let mut out = [0u8; 8];
serialized[0] = (self.limit & 0xFF) as u8;
serialized[1] = ((self.limit >> 8) & 0xFF) as u8;
serialized[6] = ((self.limit >> 16) & 0x0F) as u8;
out[0] = (self.limit & 0xFF) as u8;
out[1] = ((self.limit >> 8) & 0xFF) as u8;
out[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;
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[5] = self.access;
out[5] = self.access;
serialized[6] |= self.flags << 4;
out[6] |= self.flags << 4;
unsafe {
core::ptr::write(ptr as *mut [u8; 8], serialized);
Ok(out)
}
const fn serialize_panicing(self) -> [u8; 8] {
if self.limit > 0xFFFFF {
panic!("Invalid GDT entry limit(more than 0xFFFFF)");
}
let mut out = [0u8; 8];
Ok(())
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
}
}

View file

@ -0,0 +1,36 @@
//! 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
);

View file

@ -9,7 +9,6 @@ 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 {
@ -22,9 +21,11 @@ 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
@ -37,7 +38,6 @@ 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,7 +51,6 @@ 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 {
@ -71,12 +70,21 @@ struct Idtr {
/// Loads an interrupt descriptor table.
unsafe fn load_idt(base: *const u8, size: usize) {
let idtr = Idtr { base, size };
unsafe { asm!("lidt {}", in(reg) (&idtr) as *const Idtr as usize) }
static mut IDTR: Idtr = Idtr {
base: 0 as *const u8,
size: 0,
};
unsafe {
IDTR = Idtr {
base,
size,
};
}
unsafe { asm!("lidt {}", sym IDTR) }
}
#[derive(Clone, Copy)]
pub(super) struct IdtEntry {
pub struct IdtEntry {
pub offset_high: u16,
pub data: u16,
pub segment: u16,
@ -108,8 +116,7 @@ impl From<IdtEntry> for RawIdtEntry {
///
/// # Panics
/// Panics if the global allocator has not been setup
#[aphrodite_proc_macros::kernel_item(ActivateIDT)]
fn activate_idt(idt: Idt) {
pub unsafe fn activate_idt(idt: Idt) {
let mut entries = alloc::vec::Vec::new();
for i in 0..idt.len {
if idt.using_raw[i] {
@ -195,7 +202,7 @@ fn activate_idt(idt: Idt) {
#[derive(Clone, Copy)]
pub struct Idt {
vectors: [u16; 256],
funcs: [MaybeUninit<fn()>; 256],
funcs: [MaybeUninit<unsafe fn()>; 256],
user_callable: [bool; 256],
exception: [bool; 256],
raw_entries: [IdtEntry; 256],
@ -207,7 +214,7 @@ pub struct Idt {
#[derive(Clone, Copy)]
pub struct IdtBuilder {
vectors: [u16; 256],
funcs: [MaybeUninit<fn()>; 256],
funcs: [MaybeUninit<unsafe fn()>; 256],
user_callable: [bool; 256],
exception: [bool; 256],
raw_entries: [IdtEntry; 256],
@ -238,7 +245,7 @@ impl IdtBuilder {
pub fn add_fn(
&mut self,
vector: u16,
func: fn(),
func: unsafe fn(),
user_callable: bool,
exception: bool,
) -> &mut Self {

View file

@ -1,180 +0,0 @@
//! 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<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 {
/// 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,
}
}
}

View file

@ -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::{pop_irq, restore_irq};
use interrupts::{disable_interrupts, enable_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,27 +92,66 @@ pub fn initalize_rtc() {
unsafe { RTC_INITALIZED = true }
}
pub fn sleep(seconds: u32) { initalize_rtc(); }
pub fn alloc_available_boot() {
let irq = pop_irq();
let mut entries = vec![];
entries.push(gdt::GDT_NULL_ENTRY);
entries.push(GDTEntry {
limit: 0,
disable_interrupts();
{
// GDT
sdebugsln("Setting up GDT");
let entries = gdt::serialize_gdt_entries([
gdt::GDT_NULL_ENTRY,
GDTEntry { // kernel code segment, segment 0x08
limit: 0xFFFFF,
base: 0,
access: 0b10011011,
flags: 0b1100,
}); // kernel code segment
entries.push(GDTEntry {
limit: 0,
access: 0x9A,
flags: 0xC,
},
GDTEntry { // kernel data segment, segment 0x10
limit: 0xFFFFF,
base: 0,
access: 0b10010011,
flags: 0b1100,
}); //
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(gdt::write_gdt_entries(entries).unwrap());
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");
}
restore_irq(irq);
}

View file

@ -3,8 +3,6 @@
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 {
@ -120,11 +118,9 @@ 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!(

View file

@ -0,0 +1,24 @@
.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

View file

@ -127,7 +127,7 @@ impl core::iter::Iterator for MemoryMap {
self.reset_iter();
return None;
}
Some(self.sections[self.idx - 1].into())
Some(self.sections[self.idx - 1])
}
}

View file

@ -1,6 +1,6 @@
//! Config-related stuff.
/// C
/// Get configurations as a certain type
#[macro_export]
macro_rules! cfg_int {
($cfg:literal, $type:ident) => {

View file

@ -4,15 +4,12 @@
#![allow(static_mut_refs)]
use crate::arch::output::*;
use crate::display::{COLOR_DEFAULT, NoneTextDisplay};
use crate::display::NoneTextDisplay;
use crate::output::*;
use aphrodite_proc_macros::*;
/// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files
/// eventually call this.
#[kernel_item(IndepBootEntry)]
fn indep_boot_entry(
pub fn indep_boot_entry(
display: Option<&dyn crate::display::TextDisplay>,
#[allow(non_snake_case)] BI: &crate::boot::BootInfo,
) -> ! {
@ -22,13 +19,12 @@ 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::MemMapAllocInit(mem_map).unwrap();
crate::mem::memory_map_alloc_init(mem_map).unwrap();
crate::arch::alloc_available_boot();

View file

@ -9,8 +9,6 @@ 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
@ -65,7 +63,6 @@ static mut ALLOCATOR: MaybeMemoryMapAlloc<'static> = MaybeMemoryMapAlloc::new(No
static mut ALLOCATOR_MEMMAP: MaybeUninit<MemoryMap> = 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)]
@ -90,8 +87,7 @@ pub unsafe fn get_allocator_unchecked() -> &'static MemoryMapAlloc<'static> {
}
}
#[kernel_item(MemMapAllocInit)]
fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> {
pub fn memory_map_alloc_init(memmap: crate::boot::MemoryMap) -> Result<(), crate::Error<'static>> {
#[allow(static_mut_refs)]
unsafe {
ALLOCATOR_MEMMAP.write(memmap);
@ -203,7 +199,7 @@ impl<'a> MemoryMapAlloc<'a> {
}
}
}
if out.allocations == core::ptr::null_mut() {
if out.allocations.is_null() {
return Err(crate::Error::new(
"no free memory with space for 32 allocations",
ALLOCATIONS_NOT_ENOUGH_SPACE,
@ -345,7 +341,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<u64> {
for mapping in self.memory_map.clone() {
for mapping in *self.memory_map {
if mapping.len < size {
continue;
}

View file

@ -7,6 +7,7 @@
#![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)]
@ -20,6 +21,8 @@
#![allow(internal_features)]
#![feature(core_intrinsics)]
#![feature(vec_into_raw_parts)]
#![feature(naked_functions)]
#![feature(generic_const_exprs)]
extern crate alloc;

View file

@ -54,18 +54,18 @@ pub struct MemorySection {
reserved: u32,
}
impl Into<crate::boot::MemoryMapping> for MemorySection {
fn into(self) -> crate::boot::MemoryMapping {
impl From<MemorySection> for crate::boot::MemoryMapping {
fn from(val: MemorySection) -> Self {
MemoryMapping {
mem_type: match self.mem_type {
mem_type: match val.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: self.base_addr,
len: self.length,
start: val.base_addr,
len: val.length,
}
}
}

View file

@ -0,0 +1,19 @@
#![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();
}

View file

@ -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 let Err(_) = allocation {
if allocation.is_err() {
terrors("Failed to allocate: ", display).unwrap();
unsafe { crate::mem::LAST_MEMMAP_ERR.unwrap_err().display_np(display) }
panic!("Allocator test failure");
@ -51,6 +51,5 @@ pub fn run(display: &dyn TextDisplay) {
tdebugsln("Successfully deallocated!", display).unwrap();
}
}
tdebugsln("", display).unwrap();
}
}

View file

@ -3,8 +3,12 @@
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);
}

View file

@ -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 = value / 10;
value /= 10;
i -= 1;
}
buf
@ -33,7 +33,24 @@ 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 = value / 10;
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;
i -= 1;
}
buf
@ -48,15 +65,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 as u8;
let char = b'0' + digit;
buf[i] = char;
value = value / 10;
value /= 10;
i -= 1;
}
buf
}
/// Converts an usize(32 or 64 bit) to an [u8; 10].
/// Converts an usize(32 or 64 bit) to an [u8; 20].
pub const fn usize_as_u8_slice(mut value: usize) -> [u8; 20] {
let mut buf = [0u8; 20];
let mut i = 19;
@ -67,7 +84,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 = value / 10;
value /= 10;
i -= 1;
}
buf
@ -84,7 +101,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 = value / 10;
value /= 10;
i -= 1;
}
buf
@ -99,7 +116,7 @@ pub fn str_as_i16(mut value: &[u8]) -> i16 {
}
for byte in value {
let byte = *byte;
if byte < b'0' || byte > b'9' {
if !byte.is_ascii_digit() {
continue;
}
out *= 10;
@ -121,7 +138,7 @@ pub fn str_as_u32(value: &[u8]) -> u32 {
let mut out = 0u32;
for byte in value {
let byte = *byte;
if byte < b'0' || byte > b'9' {
if !byte.is_ascii_digit() {
continue;
}
out *= 10;
@ -143,7 +160,7 @@ pub fn str_as_u128(value: &[u8]) -> u128 {
let mut out = 0u128;
for byte in value {
let byte = *byte;
if byte < b'0' || byte > b'9' {
if !byte.is_ascii_digit() {
continue;
}
out *= 10;
@ -165,7 +182,7 @@ pub fn str_as_u64(value: &[u8]) -> u64 {
let mut out = 0u64;
for byte in value {
let byte = *byte;
if byte < b'0' || byte > b'9' {
if !byte.is_ascii_digit() {
continue;
}
out *= 10;

View file

@ -1,62 +0,0 @@
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<Self> {
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() {
<Token![,]>::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()
}