Merging with origin/unified

This commit is contained in:
2025-03-05 21:04:58 +00:00
38 changed files with 674 additions and 154 deletions
+2
View File
@@ -1,3 +1,5 @@
#![expect(unused)]
use core::arch::x86_64::__cpuid;
use crate::arch::x86_64::memory::mapping::PHYSICAL_MEMORY_OFFSET;
+1
View File
@@ -1,3 +1,4 @@
#![expect(unused)]
use core::arch::x86_64::__cpuid;
use spin::Lazy;
use x86_64::registers::model_specific::Msr;
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::missing_safety_doc)]
use x86_64::instructions::port::Port;
const CMD_INIT: u8 = 0x11;
+24 -11
View File
@@ -2,14 +2,17 @@ use core::fmt;
use spin::{Lazy, Mutex};
use x86_64::instructions::interrupts;
use crate::arch::x86_64::drivers::framebuffer::{colour::Colour, display::FRAMEBUFFER_WRITER};
use crate::arch::x86_64::drivers::framebuffer::{
colour::Colour, display::FRAMEBUFFER_WRITER,
};
use crate::resources::font::{FONT_SPLEEN_8X16, Font};
static FONT_WIDTH: u32 = 8;
static FONT_HEIGHT: u32 = 16;
pub static WRITER: Lazy<Mutex<Writer>> = Lazy::new(|| Mutex::new(Writer::new()));
pub static WRITER: Lazy<Mutex<Writer>> =
Lazy::new(|| Mutex::new(Writer::new()));
pub fn screensize_chars() -> (u32, u32) {
let writer = WRITER.lock();
@@ -71,21 +74,31 @@ impl Writer {
return;
}
// Get the character data from the font array. -- each byte is a row of pixels
// Get the character data from the font array. -- each byte is a row of
// pixels
let data: &[u8] = self.font.glyph_for(c as u16);
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
for (row, line) in data.iter().enumerate().take(16) {
for col in 0..8 {
let pixel_x: u32 = self.text_col * FONT_WIDTH + col;
let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32;
let pixel_y: u32 =
self.text_line * FONT_HEIGHT + row as u32;
if line & (0x80 >> col) != 0 {
// Write the foreground color
writer.write_pixel(pixel_x as usize, pixel_y as usize, self.fg_color);
writer.write_pixel(
pixel_x as usize,
pixel_y as usize,
self.fg_color,
);
} else {
// Write the background color
writer.write_pixel(pixel_x as usize, pixel_y as usize, self.bg_color);
writer.write_pixel(
pixel_x as usize,
pixel_y as usize,
self.bg_color,
);
}
}
}
@@ -117,8 +130,8 @@ impl Writer {
}
}
/// Handles the backspace character. TODO: Implement VT-100 style terminal control
/// codes alongside a shell. Not simple.
/// Handles the backspace character. TODO: Implement VT-100 style terminal
/// control codes alongside a shell. Not simple.
pub fn backspace(&mut self) {
if self.text_col > 0 {
self.text_col -= 1;
@@ -224,12 +237,12 @@ macro_rules! print {
}
#[macro_export]
macro_rules! printlnerr {
macro_rules! eprintln {
() => ($crate::printerr!("\n"));
($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*)));
($($arg:tt)*) => ($crate::eprint!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! printerr {
macro_rules! eprint {
($($arg:tt)*) => ($crate::prelude::_print_err(format_args!($($arg)*)));
}
@@ -19,9 +19,14 @@ impl From<Colour> for u32 {
fn from(val: Colour) -> Self {
match val {
Colour::ARGB(a, r, g, b) => {
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32)
(a as u32) << 24
| (r as u32) << 16
| (g as u32) << 8
| (b as u32)
}
Colour::RGB(r, g, b) => {
((r as u32) << 16) | (g as u32) << 8 | (b as u32)
}
Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32),
Colour::HexARGB(hex) => hex,
Colour::Black => 0xFF000000,
Colour::Blue => 0xFF0000FF,
@@ -38,7 +43,9 @@ impl From<Colour> for u32 {
impl core::fmt::Display for Colour {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
Self::ARGB(r, g, b, a) => {
write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a)
}
Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
Self::Black => write!(f, "Black"),
@@ -10,17 +10,19 @@ use core::panic;
use limine::framebuffer::Framebuffer;
use spin::{Lazy, Mutex};
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(|| {
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|| {
panic!("Framebuffer request failed");
},
|framebuffer_response| {
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
Some(FramebufferWriter::new(framebuffer))
},
))
});
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> =
Lazy::new(|| {
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|| {
panic!("Framebuffer request failed");
},
|framebuffer_response| {
let framebuffer =
framebuffer_response.framebuffers().next().unwrap();
Some(FramebufferWriter::new(framebuffer))
},
))
});
/// The updated writer stores necessary fields from the [Framebuffer].
/// This ensures that the contained types are Send, as Framebuffer was
@@ -28,7 +30,8 @@ pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::ne
///
/// It also avoids the requirement for lifetimes.
///
/// Note this does not implement Writer as these functions only handle drawing pixels.
/// Note this does not implement Writer as these functions only handle drawing
/// pixels.
pub struct FramebufferWriter {
pitch: u64,
bpp: u16,
+17 -10
View File
@@ -6,20 +6,24 @@ use core::{
use crate::println;
use crossbeam::queue::ArrayQueue;
use futures_util::{Stream, StreamExt, task::AtomicWaker};
use pc_keyboard::{DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1, layouts::Uk105Key};
use pc_keyboard::{
DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1,
layouts::Uk105Key,
};
use spin::{Lazy, Mutex, Once};
static KBD_QUEUE: Once<ArrayQueue<u8>> = Once::new();
static WAKER: AtomicWaker = AtomicWaker::new();
pub static KEYBOARD: Lazy<Mutex<Keyboard<Uk105Key, ScancodeSet1>>> = Lazy::new(|| {
Mutex::new(Keyboard::new(
ScancodeSet1::new(),
// TODO: Expose an API to change the default KB layout.
Uk105Key,
HandleControl::Ignore,
))
});
pub static KEYBOARD: Lazy<Mutex<Keyboard<Uk105Key, ScancodeSet1>>> =
Lazy::new(|| {
Mutex::new(Keyboard::new(
ScancodeSet1::new(),
// TODO: Expose an API to change the default KB layout.
Uk105Key,
HandleControl::Ignore,
))
});
pub static SCANCODE_STREAM: Lazy<Mutex<ScancodeStream>> =
Lazy::new(|| Mutex::new(ScancodeStream::new()));
@@ -59,7 +63,10 @@ impl Default for ScancodeStream {
impl Stream for ScancodeStream {
type Item = u8;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
let queue = KBD_QUEUE.get().unwrap();
if let Some(scancode) = queue.pop() {
+2 -1
View File
@@ -138,7 +138,8 @@ extern "x86-interrupt" fn page_fault_handler(
let frame = f.allocate_frame().unwrap();
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
let page: Page<Size4KiB> = Page::containing_address(Cr2::read().unwrap());
let page: Page<Size4KiB> =
Page::containing_address(Cr2::read().unwrap());
unsafe {
let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock();
@@ -1,3 +1,3 @@
pub mod heap_alloc;
pub mod stack_alloc;
pub(crate) mod page_alloc;
pub mod stack_alloc;
@@ -14,14 +14,17 @@ pub struct FoundryOSFrameAllocator {
impl FoundryOSFrameAllocator {
/// Creates a new `FoundryOSFrameAllocator` from a memory map.
///
/// This function takes a reference to a `MemoryMapResponse` and initializes a
/// `FoundryOSFrameAllocator` with it. The `next` field is set to 0, indicating that
/// the first frame to be allocated is the first frame in the memory map.
/// This function takes a reference to a `MemoryMapResponse` and initializes
/// a `FoundryOSFrameAllocator` with it. The `next` field is set to 0,
/// indicating that the first frame to be allocated is the first frame
/// in the memory map.
pub fn init(memory_map: &'static MemoryMapResponse) {
FRAME_ALLOCATOR.call_once(|| Mutex::new(Self {
memory_map,
next: 0,
}));
FRAME_ALLOCATOR.call_once(|| {
Mutex::new(Self {
memory_map,
next: 0,
})
});
}
pub fn count_usable_frames(&self) -> u64 {
@@ -70,9 +73,9 @@ impl FoundryOSFrameAllocator {
unsafe impl FrameAllocator<Size4KiB> for FoundryOSFrameAllocator {
/// Allocates a frame from the list of usable frames.
///
/// This function returns the next available `PhysFrame` from the memory map,
/// if one exists. Once a frame is allocated, the internal counter is incremented
/// to point to the next frame for future allocations.
/// This function returns the next available `PhysFrame` from the memory
/// map, if one exists. Once a frame is allocated, the internal counter
/// is incremented to point to the next frame for future allocations.
///
/// # Returns
///
@@ -80,9 +83,6 @@ unsafe impl FrameAllocator<Size4KiB> for FoundryOSFrameAllocator {
/// - `None`: If there are no more usable frames to allocate.
fn allocate_frame(&mut self) -> Option<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
if frame.is_none() {
panic!("this shouldnt happen!");
}
self.next += 1;
frame
}
@@ -1,33 +1,39 @@
use x86_64::structures::paging::{mapper, FrameAllocator, Mapper, Page, Size4KiB};
use x86_64::VirtAddr;
use crate::arch::x86_64::memory::STACK_VIRTUAL_SPACE;
use x86_64::VirtAddr;
use x86_64::structures::paging::{
FrameAllocator, Mapper, Page, Size4KiB, mapper,
};
fn reserve_stack_memory(size_in_pages: u64) -> Page {
use core::sync::atomic::{AtomicU64, Ordering};
static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(STACK_VIRTUAL_SPACE as u64);
let start_addr = VirtAddr::new(STACK_ALLOC_NEXT.fetch_add(
size_in_pages * Page::<Size4KiB>::SIZE,
Ordering::Relaxed,
));
static STACK_ALLOC_NEXT: AtomicU64 =
AtomicU64::new(STACK_VIRTUAL_SPACE as u64);
let start_addr =
VirtAddr::new(STACK_ALLOC_NEXT.fetch_add(
size_in_pages * Page::<Size4KiB>::SIZE,
Ordering::Relaxed,
));
Page::from_start_address(start_addr)
.expect("`STACK_ALLOC_NEXT` not page aligned")
}
/// Allocates a stack in the virtual address space, mapped to physical pages.
///
/// This function allocates a stack in the virtual address space, mapped to physical pages.
/// The stack is allocated as a sequence of pages, with the first page allocated as a guard page.
/// The stack is then mapped to the allocated physical frame.
/// This function allocates a stack in the virtual address space, mapped to
/// physical pages. The stack is allocated as a sequence of pages, with the
/// first page allocated as a guard page. The stack is then mapped to the
/// allocated physical frame.
///
/// The function takes the size of the stack in pages, a mutable reference to a mapper, and a
/// mutable reference to a frame allocator. It returns a `Result` containing a `StackBounds`
/// struct, which contains the start and end virtual addresses of the allocated stack.
/// The function takes the size of the stack in pages, a mutable reference to a
/// mapper, and a mutable reference to a frame allocator. It returns a `Result`
/// containing a `StackBounds` struct, which contains the start and end virtual
/// addresses of the allocated stack.
///
/// # Safety
///
/// This function is unsafe because it maps physical frames to virtual addresses without any
/// protection. This can lead to bugs if the physical frames are not correctly allocated, or if the
/// virtual addresses are not correctly aligned.
/// This function is unsafe because it maps physical frames to virtual addresses
/// without any protection. This can lead to bugs if the physical frames are not
/// correctly allocated, or if the virtual addresses are not correctly aligned.
///
/// # Panics
///
@@ -36,27 +42,28 @@ pub unsafe fn alloc_stack(
size_in_pages: u64,
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<StackBounds, mapper::MapToError<Size4KiB>> { unsafe {
use x86_64::structures::paging::PageTableFlags as Flags;
let guard_page = reserve_stack_memory(size_in_pages + 1);
let stack_start = guard_page + 1;
let stack_end = stack_start + size_in_pages;
) -> Result<StackBounds, mapper::MapToError<Size4KiB>> {
unsafe {
use x86_64::structures::paging::PageTableFlags as Flags;
for page in Page::range(stack_start, stack_end) {
let frame = frame_allocator
.allocate_frame()
.ok_or(mapper::MapToError::FrameAllocationFailed)?;
let flags = Flags::PRESENT | Flags::WRITABLE;
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
let guard_page = reserve_stack_memory(size_in_pages + 1);
let stack_start = guard_page + 1;
let stack_end = stack_start + size_in_pages;
for page in Page::range(stack_start, stack_end) {
let frame = frame_allocator
.allocate_frame()
.ok_or(mapper::MapToError::FrameAllocationFailed)?;
let flags = Flags::PRESENT | Flags::WRITABLE;
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
}
Ok(StackBounds {
start: stack_start.start_address(),
end: stack_end.start_address(),
})
}
Ok(StackBounds {
start: stack_start.start_address(),
end: stack_end.start_address(),
})
}}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct StackBounds {
@@ -76,4 +83,4 @@ impl StackBounds {
pub const fn end(&self) -> VirtAddr {
self.end
}
}
}
+2 -1
View File
@@ -16,7 +16,8 @@ static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new();
#[used]
#[unsafe(link_section = ".requests")]
static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new();
pub static KERNEL_ADDRESS_REQUEST: KernelAddressRequest =
KernelAddressRequest::new();
/// ```rs
/// let virt_addr = phys_addr + offset;
+11 -10
View File
@@ -2,20 +2,19 @@ pub mod allocation;
pub mod mapping;
pub mod units;
use spin::{Mutex, Once};
use x86_64::{
registers::control::Cr3, structures::paging::{FrameAllocator, OffsetPageTable, PageTable}
,
VirtAddr,
};
use allocation::page_alloc::FoundryOSFrameAllocator;
use spin::{Mutex, Once};
use units::MemoryUnits::*;
use x86_64::{
VirtAddr,
registers::control::Cr3,
structures::paging::{OffsetPageTable, PageTable},
};
pub const STACK_VIRTUAL_SPACE: usize = 0x5555_5555_0000; // start address of the memory space where we store allocated stacks
pub const HEAP_VIRTUAL_SPACE: usize = 0x4444_4444_0000; // start address of heap allocated memory
pub const HEAP_SIZE: usize = MiB(1).to_bytes();
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
/// Returns a mutable reference to the current level 4 page table.
@@ -25,7 +24,9 @@ pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
/// The caller must ensure that the level 4 page table is not modified
/// simultaneously. The caller must also ensure that the physical memory offset
/// is correct, to ensure that the correct virtual address is constructed.
unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
unsafe fn active_l4_table(
physical_memory_offset: VirtAddr,
) -> &'static mut PageTable {
let (level_4_frame, _) = Cr3::read();
let phys_addr = level_4_frame.start_address();
@@ -54,8 +55,8 @@ unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut Page
pub fn init_page_table(physical_memory_offset: VirtAddr) {
unsafe {
let l4_table = active_l4_table(physical_memory_offset);
let offset_table = OffsetPageTable::new(l4_table, physical_memory_offset);
let offset_table =
OffsetPageTable::new(l4_table, physical_memory_offset);
OFFSET_PAGE_TABLE.call_once(|| Mutex::new(offset_table));
}
}
+2 -2
View File
@@ -30,7 +30,7 @@ impl MemoryUnits {
pub const fn convert(&mut self) {
match self {
Self::B(b) if *b > 1024 => *self = Self::KiB(*b / 1024),
Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024),
Self::KiB(kib) if *kib > 1024 => *self = Self::MiB(*kib / 1024),
Self::MiB(mib) if *mib > 1024 => *self = Self::GiB(*mib / 1024),
_ => (),
}
@@ -46,4 +46,4 @@ impl core::fmt::Display for MemoryUnits {
Self::GiB(gib) => write!(f, "{} GiB", gib),
}
}
}
}
@@ -2,7 +2,6 @@
//!
//! Written by @zxq5 for the most part with code from
//! [here](https://github.com/phil-opp/blog_os/).
//!
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
@@ -81,9 +80,9 @@ impl Executor {
Some(task) => task,
None => continue, // task no longer exists
};
let waker = waker_cache
.entry(task_id)
.or_insert_with(|| TaskWaker::new_waker(task_id, task_queue.clone()));
let waker = waker_cache.entry(task_id).or_insert_with(|| {
TaskWaker::new_waker(task_id, task_queue.clone())
});
let mut context = Context::from_waker(waker);
match task.poll(&mut context) {
Poll::Ready(()) => {
@@ -129,7 +128,10 @@ impl TaskWaker {
self.task_queue.push(self.task_id).expect("task_queue full");
}
fn new_waker(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
fn new_waker(
task_id: TaskId,
task_queue: Arc<ArrayQueue<TaskId>>,
) -> Waker {
Waker::from(Arc::new(Self {
task_id,
task_queue,
+2 -1
View File
@@ -1,3 +1,4 @@
#![expect(unused)]
pub mod async_io;
pub mod threading;
mod taskrunner;
pub mod threading;
@@ -6,7 +6,7 @@ pub struct Task {
}
impl Task {
pub fn new() {}
// pub const fn new() {}
pub fn run() {}
}
pub const fn run() {}
}
@@ -1,5 +1,5 @@
use x86_64::VirtAddr;
use crate::arch::x86_64::memory::allocation::stack_alloc::StackBounds;
use x86_64::VirtAddr;
mod switch;
@@ -10,8 +10,6 @@ pub struct Thread {
stack_bounds: Option<StackBounds>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ThreadId(u64);