feat: change font and release greate tty output
This commit is contained in:
@@ -53,17 +53,7 @@ impl InterruptDescriptorTable {
|
|||||||
|
|
||||||
pub fn set_handler(&mut self, vector: u8, handler: u64) {
|
pub fn set_handler(&mut self, vector: u8, handler: u64) {
|
||||||
// 0x8E = Interrupt Gate, Ring 0, Present
|
// 0x8E = Interrupt Gate, Ring 0, Present
|
||||||
self.entries[vector as usize].set_handler(handler, 0x28, 0x8E); // 0x28 - Kernel CS в Limine (по стандарту 5-й сегмент)
|
self.entries[vector as usize].set_handler(handler, 0x28, 0x8E);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn load(&'static self) {
|
|
||||||
let ptr = IdtPtr {
|
|
||||||
limit: (core::mem::size_of::<Self>() - 1) as u16,
|
|
||||||
base: self as *const _ as u64,
|
|
||||||
};
|
|
||||||
// Исправление для Rust 2024: явный unsafe блок внутри unsafe fn
|
|
||||||
unsafe {
|
|
||||||
asm!("lidt [{}]", in(reg) &ptr, options(readonly, nostack, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
use core::arch::global_asm;
|
use core::arch::global_asm;
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use crate::mem::vmm::KERNEL_SPACE; // Оставили только один импорт
|
use crate::mem::vmm::KERNEL_SPACE;
|
||||||
use crate::mem::address::VirtAddr;
|
use crate::mem::address::VirtAddr;
|
||||||
|
|
||||||
// Глобальная статическая таблица дескрипторов прерываний (IDT)
|
|
||||||
pub static mut IDT: crate::cpu::idt::InterruptDescriptorTable = crate::cpu::idt::InterruptDescriptorTable::new();
|
pub static mut IDT: crate::cpu::idt::InterruptDescriptorTable = crate::cpu::idt::InterruptDescriptorTable::new();
|
||||||
|
|
||||||
// Низкоуровневый ассемблерный трамплин (Context Save & Restore)
|
|
||||||
global_asm!(
|
global_asm!(
|
||||||
".global page_fault_stub",
|
".global page_fault_stub",
|
||||||
"page_fault_stub:",
|
"page_fault_stub:",
|
||||||
@@ -80,10 +78,8 @@ pub extern "C" fn rust_page_fault_handler(error_code: u64) {
|
|||||||
let present = (error_code & 0x1) != 0;
|
let present = (error_code & 0x1) != 0;
|
||||||
let virt_addr = VirtAddr(fault_addr);
|
let virt_addr = VirtAddr(fault_addr);
|
||||||
|
|
||||||
// 1. Сначала применяем все отложенные отзывы прав из других Actor'ов (чтобы избежать Deadlock)
|
|
||||||
process_deferred_mmu_events();
|
process_deferred_mmu_events();
|
||||||
|
|
||||||
// 2. Теперь захватываем VMM для обработки текущего сбоя
|
|
||||||
let mut vmm_guard = KERNEL_SPACE.lock();
|
let mut vmm_guard = KERNEL_SPACE.lock();
|
||||||
|
|
||||||
if let Some(space) = vmm_guard.as_mut() {
|
if let Some(space) = vmm_guard.as_mut() {
|
||||||
@@ -91,11 +87,11 @@ pub extern "C" fn rust_page_fault_handler(error_code: u64) {
|
|||||||
Ok(_) => return,
|
Ok(_) => return,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!(
|
panic!(
|
||||||
"KERNEL PANIC: Необработанный сбой виртуальной памяти (Page Fault)!\n\
|
"KERNEL PANIC: Unprocessed failure of virtual memory (Page Fault)!\n\
|
||||||
Адрес: {:#X}\n\
|
Address: {:#X}\n\
|
||||||
Режим: {}\n\
|
Mode: {}\n\
|
||||||
Присутствие: {}\n\
|
Presence: {}\n\
|
||||||
Причина VMM: {:?}",
|
Cause VMM: {:?}",
|
||||||
fault_addr,
|
fault_addr,
|
||||||
if write { "WRITE" } else { "READ" },
|
if write { "WRITE" } else { "READ" },
|
||||||
if present { "YES (Protection Violation)" } else { "NO (Not Present)" },
|
if present { "YES (Protection Violation)" } else { "NO (Not Present)" },
|
||||||
@@ -105,7 +101,7 @@ pub extern "C" fn rust_page_fault_handler(error_code: u64) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"KERNEL PANIC: Критический Page Fault до аллокации глобального KERNEL_SPACE!\n\
|
"KERNEL PANIC: Critical Page Fault before alocate global KERNEL_SPACE!\n\
|
||||||
Адрес сбоя: {:#X}",
|
Адрес сбоя: {:#X}",
|
||||||
fault_addr
|
fault_addr
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
pub mod serial;
|
pub mod serial;
|
||||||
|
|
||||||
use embedded_graphics::pixelcolor::Rgb888;
|
|
||||||
use embedded_graphics::prelude::RgbColor;
|
|
||||||
|
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
Info, Warn, Error,
|
Info, Warn, Error,
|
||||||
}
|
}
|
||||||
@@ -16,11 +13,11 @@ impl LogLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn console_color(&self) -> Rgb888 {
|
pub fn console_color(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
LogLevel::Info => Rgb888::GREEN,
|
LogLevel::Info => 0x00FF00, // GREEN
|
||||||
LogLevel::Warn => Rgb888::YELLOW,
|
LogLevel::Warn => 0xFFFF00, // YELLOW
|
||||||
LogLevel::Error => Rgb888::RED,
|
LogLevel::Error => 0xFF0000, // RED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,13 +26,12 @@ impl LogLevel {
|
|||||||
macro_rules! log {
|
macro_rules! log {
|
||||||
($console:expr, $level:expr, $module:expr, $($arg:tt)*) => {{
|
($console:expr, $level:expr, $module:expr, $($arg:tt)*) => {{
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use embedded_graphics::pixelcolor::Rgb888;
|
|
||||||
|
|
||||||
// Visual screen output
|
// Visual screen output
|
||||||
$console.set_color($level.console_color());
|
$console.set_color($level.console_color());
|
||||||
let _ = write!($console, "[ LOG ] ");
|
let _ = write!($console, "[ LOG ] ");
|
||||||
|
|
||||||
$console.set_color(Rgb888::WHITE);
|
$console.set_color(0xFFFFFF); // WHITE
|
||||||
let _ = write!($console, "{:<6} | ", $module);
|
let _ = write!($console, "{:<6} | ", $module);
|
||||||
let _ = writeln!($console, $($arg)*);
|
let _ = writeln!($console, $($arg)*);
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||||
|
|
||||||
const QUEUE_SIZE: usize = 1024; // Должно быть степенью двойки для быстрой битовой маски
|
const QUEUE_SIZE: usize = 1024;
|
||||||
const QUEUE_MASK: usize = QUEUE_SIZE - 1;
|
const QUEUE_MASK: usize = QUEUE_SIZE - 1;
|
||||||
|
|
||||||
/// Lock-free MPSC (Multi-Producer, Single-Consumer) очередь.
|
|
||||||
/// Оптимизирована для передачи токенов инвалидации без блокировок.
|
|
||||||
pub struct RevocationQueue {
|
pub struct RevocationQueue {
|
||||||
buffer: [AtomicU64; QUEUE_SIZE],
|
buffer: [AtomicU64; QUEUE_SIZE],
|
||||||
// Используем паддинг (cache line size = 64 bytes) для предотвращения False Sharing
|
|
||||||
// между head (изменяется VMM) и tail (изменяется Cap-системой).
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
_pad0: [u8; 64],
|
_pad0: [u8; 64],
|
||||||
head: AtomicUsize,
|
head: AtomicUsize,
|
||||||
@@ -29,15 +25,12 @@ impl RevocationQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Вызывается со стороны Actor'ов и подсистемы Capabilities (Multi-Producer)
|
|
||||||
pub fn push(&self, token_sig: u64) -> Result<(), &'static str> {
|
pub fn push(&self, token_sig: u64) -> Result<(), &'static str> {
|
||||||
let mut tail = self.tail.load(Ordering::Relaxed);
|
let mut tail = self.tail.load(Ordering::Relaxed);
|
||||||
loop {
|
loop {
|
||||||
let head = self.head.load(Ordering::Acquire);
|
let head = self.head.load(Ordering::Acquire);
|
||||||
|
|
||||||
if tail.wrapping_sub(head) >= QUEUE_SIZE {
|
if tail.wrapping_sub(head) >= QUEUE_SIZE {
|
||||||
// Очередь переполнена. В проде здесь должен быть триггер ballooning'а
|
|
||||||
// или принудительный yield для VMM, но пока возвращаем ошибку.
|
|
||||||
return Err("Revocation queue overflow");
|
return Err("Revocation queue overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,13 +49,12 @@ impl RevocationQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Вызывается только из активного VMM контекста (Single-Consumer)
|
|
||||||
pub fn pop(&self) -> Option<u64> {
|
pub fn pop(&self) -> Option<u64> {
|
||||||
let head = self.head.load(Ordering::Relaxed);
|
let head = self.head.load(Ordering::Relaxed);
|
||||||
let tail = self.tail.load(Ordering::Acquire);
|
let tail = self.tail.load(Ordering::Acquire);
|
||||||
|
|
||||||
if head == tail {
|
if head == tail {
|
||||||
return None; // Очередь пуста
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = self.buffer[head & QUEUE_MASK].load(Ordering::Acquire);
|
let token = self.buffer[head & QUEUE_MASK].load(Ordering::Acquire);
|
||||||
|
|||||||
BIN
kernel/src/font.psf
Normal file
BIN
kernel/src/font.psf
Normal file
Binary file not shown.
@@ -18,8 +18,12 @@ use crate::mem::pm_manages::{PMActor, PMRequest, PMResult};
|
|||||||
pub mod cap;
|
pub mod cap;
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod tty;
|
||||||
|
|
||||||
mod mem;
|
mod mem;
|
||||||
|
|
||||||
|
static KERNEL_FONT: &[u8] = include_bytes!("font.psf");
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
|
||||||
@@ -164,6 +168,7 @@ static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn kmain() -> ! {
|
unsafe extern "C" fn kmain() -> ! {
|
||||||
|
|
||||||
assert!(BASE_REVISION.is_supported());
|
assert!(BASE_REVISION.is_supported());
|
||||||
|
|
||||||
let fb_res = FRAMEBUFFER_REQUEST.get_response().expect("Limine: No Framebuffer");
|
let fb_res = FRAMEBUFFER_REQUEST.get_response().expect("Limine: No Framebuffer");
|
||||||
@@ -173,7 +178,8 @@ unsafe extern "C" fn kmain() -> ! {
|
|||||||
let hhdm_offset = hhdm_res.offset();
|
let hhdm_offset = hhdm_res.offset();
|
||||||
|
|
||||||
let fb = fb_res.framebuffers().next().expect("Limine: No active framebuffer found");
|
let fb = fb_res.framebuffers().next().expect("Limine: No active framebuffer found");
|
||||||
let mut console = Console::new(FramebufferDisplay { framebuffer: &fb });
|
|
||||||
|
let mut console = tty::Console::new(&fb, KERNEL_FONT);
|
||||||
console.clear();
|
console.clear();
|
||||||
|
|
||||||
info!(console, "BOOT", "LISA Kernel Starting...");
|
info!(console, "BOOT", "LISA Kernel Starting...");
|
||||||
@@ -224,7 +230,7 @@ unsafe extern "C" fn kmain() -> ! {
|
|||||||
|
|
||||||
unsafe { core::arch::asm!("sti", options(nomem, nostack, preserves_flags)); }
|
unsafe { core::arch::asm!("sti", options(nomem, nostack, preserves_flags)); }
|
||||||
|
|
||||||
// --- Тестирование подсистемы Capability ---
|
// Capability test
|
||||||
let root_cnode = cap::CNode::new(256);
|
let root_cnode = cap::CNode::new(256);
|
||||||
|
|
||||||
if let Some(frame) = mem::pmm::alloc_frame() {
|
if let Some(frame) = mem::pmm::alloc_frame() {
|
||||||
@@ -252,7 +258,7 @@ unsafe extern "C" fn kmain() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Тестирование PMActor (Lock-Free Очереди) ---
|
// PMActor test
|
||||||
info!(console, "PM", "--- PMActor Buddy Test ---");
|
info!(console, "PM", "--- PMActor Buddy Test ---");
|
||||||
|
|
||||||
let actor_base_phys = PhysAddr(0x4000_0000);
|
let actor_base_phys = PhysAddr(0x4000_0000);
|
||||||
@@ -323,7 +329,6 @@ unsafe extern "C" fn kmain() -> ! {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Логотип ---
|
|
||||||
let logo = r#"
|
let logo = r#"
|
||||||
###########
|
###########
|
||||||
##################
|
##################
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl<A> Locked<A> {
|
|||||||
|
|
||||||
pub fn lock(&self) -> LockedGuard<'_, A> {
|
pub fn lock(&self) -> LockedGuard<'_, A> {
|
||||||
while self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
while self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
||||||
core::hint::spin_loop(); // Уступка в spinlock для снижения нагрузки на шину
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
LockedGuard {
|
LockedGuard {
|
||||||
lock: &self.lock,
|
lock: &self.lock,
|
||||||
@@ -49,14 +49,12 @@ impl<A> DerefMut for LockedGuard<'_, A> {
|
|||||||
fn deref_mut(&mut self) -> &mut Self::Target { self.data }
|
fn deref_mut(&mut self) -> &mut Self::Target { self.data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Узел односвязного списка свободных блоков
|
|
||||||
struct ListNode {
|
struct ListNode {
|
||||||
next: Option<&'static mut ListNode>,
|
next: Option<&'static mut ListNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
|
const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
|
||||||
|
|
||||||
/// Slab аллокатор для гранулярного выделения памяти
|
|
||||||
pub struct SlabAllocator {
|
pub struct SlabAllocator {
|
||||||
list_heads: [Option<&'static mut ListNode>; BLOCK_SIZES.len()],
|
list_heads: [Option<&'static mut ListNode>; BLOCK_SIZES.len()],
|
||||||
heap_start: usize,
|
heap_start: usize,
|
||||||
@@ -80,13 +78,11 @@ impl SlabAllocator {
|
|||||||
self.heap_end = start + size;
|
self.heap_end = start + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Поиск индекса блока под требуемый размер
|
|
||||||
fn list_index(layout: &Layout) -> Option<usize> {
|
fn list_index(layout: &Layout) -> Option<usize> {
|
||||||
let required_block_size = layout.size().max(layout.align());
|
let required_block_size = layout.size().max(layout.align());
|
||||||
BLOCK_SIZES.iter().position(|&s| s >= required_block_size)
|
BLOCK_SIZES.iter().position(|&s| s >= required_block_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Резервный Bump-аллокатор для нарезки новых Slab-блоков
|
|
||||||
fn fallback_alloc(&mut self, layout: Layout) -> *mut u8 {
|
fn fallback_alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||||
let alloc_start = (self.next_bump + layout.align() - 1) & !(layout.align() - 1);
|
let alloc_start = (self.next_bump + layout.align() - 1) & !(layout.align() - 1);
|
||||||
let alloc_end = alloc_start.checked_add(layout.size()).unwrap_or(self.heap_end + 1);
|
let alloc_end = alloc_start.checked_add(layout.size()).unwrap_or(self.heap_end + 1);
|
||||||
@@ -143,8 +139,6 @@ unsafe impl GlobalAlloc for Locked<SlabAllocator> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Крупные регионы освобождаются через вызовы дескрипторов VMM/PMM,
|
|
||||||
// глобальный аллокатор ядра их не трекает.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,12 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Constants & helpers
|
// Constants & helpers
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Maximum allocation order.
|
/// Maximum allocation order.
|
||||||
/// `2^11 × 4 096 bytes = 8 MiB` per single allocation.
|
/// `2^11 × 4 096 bytes = 8 MiB` per single allocation.
|
||||||
pub const MAX_ORDER: usize = 11;
|
pub const MAX_ORDER: usize = 11;
|
||||||
|
|
||||||
/// ⌈log₂(n)⌉ — the minimum order whose block size covers `page_count` pages.
|
/// ⌈log2(n)⌉ — the minimum order whose block size covers `page_count` pages.
|
||||||
///
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// order_for(1) = 0 (2^0 = 1)
|
/// order_for(1) = 0 (2^0 = 1)
|
||||||
@@ -63,10 +60,7 @@ pub fn order_for(page_count: usize) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// BuddyAllocator
|
// BuddyAllocator
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Buddy allocator over a contiguous, pre-committed range of physical pages.
|
/// Buddy allocator over a contiguous, pre-committed range of physical pages.
|
||||||
///
|
///
|
||||||
/// All page indices stored in free lists are **relative to the start of the
|
/// All page indices stored in free lists are **relative to the start of the
|
||||||
@@ -89,8 +83,7 @@ pub struct BuddyAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BuddyAllocator {
|
impl BuddyAllocator {
|
||||||
// ─── Construction ─────────────────────────────────────────────────────────
|
//Construction
|
||||||
|
|
||||||
/// Create a new allocator over `total_pages` pages, **all initially free**.
|
/// Create a new allocator over `total_pages` pages, **all initially free**.
|
||||||
///
|
///
|
||||||
/// Uses a greedy largest-first decomposition to build the initial free lists
|
/// Uses a greedy largest-first decomposition to build the initial free lists
|
||||||
@@ -127,7 +120,7 @@ impl BuddyAllocator {
|
|||||||
|
|
||||||
// Size constraint: 2^order ≤ remaining → order ≤ ⌊log₂(remaining)⌋.
|
// Size constraint: 2^order ≤ remaining → order ≤ ⌊log₂(remaining)⌋.
|
||||||
let size_order = (usize::BITS as usize - 1)
|
let size_order = (usize::BITS as usize - 1)
|
||||||
- remaining.leading_zeros() as usize; // ⌊log₂(remaining)⌋
|
- remaining.leading_zeros() as usize; // ⌊log2(remaining)⌋
|
||||||
|
|
||||||
let order = MAX_ORDER.min(align_order).min(size_order);
|
let order = MAX_ORDER.min(align_order).min(size_order);
|
||||||
let block_size = 1usize << order;
|
let block_size = 1usize << order;
|
||||||
@@ -140,8 +133,7 @@ impl BuddyAllocator {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Allocation ───────────────────────────────────────────────────────────
|
//Allocation
|
||||||
|
|
||||||
/// Allocate a 2^`order`-page block.
|
/// Allocate a 2^`order`-page block.
|
||||||
///
|
///
|
||||||
/// Returns the **relative** page index of the block's first page, or `None`
|
/// Returns the **relative** page index of the block's first page, or `None`
|
||||||
@@ -206,7 +198,7 @@ impl BuddyAllocator {
|
|||||||
self.alloc(order).map(|idx| (idx, order))
|
self.alloc(order).map(|idx| (idx, order))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Deallocation ─────────────────────────────────────────────────────────
|
//Deallocation
|
||||||
|
|
||||||
/// Return a 2^`order`-page block at **relative** index `block_idx` to the
|
/// Return a 2^`order`-page block at **relative** index `block_idx` to the
|
||||||
/// free pool, coalescing with free buddies up the order chain.
|
/// free pool, coalescing with free buddies up the order chain.
|
||||||
@@ -269,7 +261,7 @@ impl BuddyAllocator {
|
|||||||
self.free_lists[order].push(block_idx);
|
self.free_lists[order].push(block_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Introspection ────────────────────────────────────────────────────────
|
//Introspection
|
||||||
|
|
||||||
/// Number of pages currently available for allocation.
|
/// Number of pages currently available for allocation.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub struct PageTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PageTable {
|
impl PageTable {
|
||||||
// ── Bulk mapping ─────────────────────────────────────────────────────────
|
//Bulk mapping
|
||||||
|
|
||||||
/// Map a contiguous physical range to a contiguous virtual range.
|
/// Map a contiguous physical range to a contiguous virtual range.
|
||||||
/// Allocates intermediate page-table pages from the PMM as needed.
|
/// Allocates intermediate page-table pages from the PMM as needed.
|
||||||
@@ -48,7 +48,7 @@ impl PageTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Single-page operations ────────────────────────────────────────────────
|
//Single-page operations
|
||||||
|
|
||||||
/// Map a single 4 KiB page.
|
/// Map a single 4 KiB page.
|
||||||
/// Allocates intermediate PT pages from the PMM if they do not exist.
|
/// Allocates intermediate PT pages from the PMM if they do not exist.
|
||||||
@@ -134,7 +134,7 @@ impl PageTable {
|
|||||||
Some(PhysAddr((p1e & PTE_ADDR_MASK) | (virt.0 & 0xFFF)))
|
Some(PhysAddr((p1e & PTE_ADDR_MASK) | (virt.0 & 0xFFF)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── CR3 ──────────────────────────────────────────────────────────────────
|
// CR3
|
||||||
|
|
||||||
/// Load this page table into CR3 (full TLB flush, no PCID).
|
/// Load this page table into CR3 (full TLB flush, no PCID).
|
||||||
///
|
///
|
||||||
@@ -151,7 +151,7 @@ impl PageTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private walk helpers ──────────────────────────────────────────────────────
|
//Private walk helpers
|
||||||
|
|
||||||
impl PageTable {
|
impl PageTable {
|
||||||
/// Walk (or create) the path P4 → P3 → P2 → P1, returning a mutable
|
/// Walk (or create) the path P4 → P3 → P2 → P1, returning a mutable
|
||||||
@@ -201,7 +201,7 @@ impl PageTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PMM shim ─────────────────────────────────────────────────────────────────
|
//PMM shim
|
||||||
|
|
||||||
/// Allocate a single physical frame for page-table use.
|
/// Allocate a single physical frame for page-table use.
|
||||||
/// This thin wrapper avoids a direct dependency cycle between paging ↔ pmm.
|
/// This thin wrapper avoids a direct dependency cycle between paging ↔ pmm.
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ use crate::mem::pmm::PAGE_SIZE;
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Message packing constants
|
// Message packing constants
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
const QUEUE_SIZE: usize = 1024; // power-of-two
|
const QUEUE_SIZE: usize = 1024; // power-of-two
|
||||||
const QUEUE_MASK: usize = QUEUE_SIZE - 1;
|
const QUEUE_MASK: usize = QUEUE_SIZE - 1;
|
||||||
@@ -67,10 +65,7 @@ mod packing {
|
|||||||
pub const ARG_MASK: u64 = 0x000F_FFFF; // 20 bits
|
pub const ARG_MASK: u64 = 0x000F_FFFF; // 20 bits
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Request type
|
// Request type
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Asynchronous request submitted to a `PMActor` inbox.
|
/// Asynchronous request submitted to a `PMActor` inbox.
|
||||||
///
|
///
|
||||||
/// `channel_id` identifies where the response should be routed.
|
/// `channel_id` identifies where the response should be routed.
|
||||||
@@ -163,9 +158,7 @@ impl PMRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Response type
|
// Response type
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Result returned by `PMActor::process_messages()` for each completed request.
|
/// Result returned by `PMActor::process_messages()` for each completed request.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -194,10 +187,7 @@ pub enum PMResult {
|
|||||||
Freed { pages_returned: usize },
|
Freed { pages_returned: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Lock-free MPSC inbox
|
// Lock-free MPSC inbox
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// MPSC ring buffer for PMRequest values.
|
/// MPSC ring buffer for PMRequest values.
|
||||||
///
|
///
|
||||||
/// Producers (any core, any context) call `send`; the owning PMActor calls
|
/// Producers (any core, any context) call `send`; the owning PMActor calls
|
||||||
@@ -268,10 +258,7 @@ impl PMActorQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// PM Actor
|
// PM Actor
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// An autonomous physical-memory actor.
|
/// An autonomous physical-memory actor.
|
||||||
///
|
///
|
||||||
/// Owns a `BuddyAllocator` over its capital range and a lock-free MPSC inbox.
|
/// Owns a `BuddyAllocator` over its capital range and a lock-free MPSC inbox.
|
||||||
@@ -284,16 +271,12 @@ impl PMActorQueue {
|
|||||||
pub struct PMActor {
|
pub struct PMActor {
|
||||||
/// Unique identity within the actor federation.
|
/// Unique identity within the actor federation.
|
||||||
pub actor_id: u64,
|
pub actor_id: u64,
|
||||||
|
|
||||||
/// Root strong capability over the entire managed physical range.
|
/// Root strong capability over the entire managed physical range.
|
||||||
pub root_untyped: Capability,
|
pub root_untyped: Capability,
|
||||||
|
|
||||||
/// `(inclusive_start, exclusive_end)` physical addresses.
|
/// `(inclusive_start, exclusive_end)` physical addresses.
|
||||||
pub managed_range: (PhysAddr, PhysAddr),
|
pub managed_range: (PhysAddr, PhysAddr),
|
||||||
|
|
||||||
/// Inbox — producers write here, actor reads here.
|
/// Inbox — producers write here, actor reads here.
|
||||||
queue: PMActorQueue,
|
queue: PMActorQueue,
|
||||||
|
|
||||||
/// Local buddy allocator. Only ever touched in `process_messages`.
|
/// Local buddy allocator. Only ever touched in `process_messages`.
|
||||||
buddy: BuddyAllocator,
|
buddy: BuddyAllocator,
|
||||||
}
|
}
|
||||||
@@ -319,7 +302,7 @@ impl PMActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Producer API (callable from any context) ──────────────────────────
|
//Producer API (callable from any context)
|
||||||
|
|
||||||
/// Submit a request to this actor's inbox.
|
/// Submit a request to this actor's inbox.
|
||||||
///
|
///
|
||||||
@@ -329,7 +312,7 @@ impl PMActor {
|
|||||||
self.queue.send(req)
|
self.queue.send(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Consumer API (actor's own scheduled context) ─────────────────────
|
//Consumer API (actor's own scheduled context)
|
||||||
|
|
||||||
/// Drain the inbox and execute all pending requests.
|
/// Drain the inbox and execute all pending requests.
|
||||||
///
|
///
|
||||||
@@ -370,7 +353,7 @@ impl PMActor {
|
|||||||
responses
|
responses
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Introspection ────────────────────────────────────────────────────
|
//Introspection
|
||||||
|
|
||||||
/// Free pages remaining in this actor's buddy pool.
|
/// Free pages remaining in this actor's buddy pool.
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -385,7 +368,7 @@ impl PMActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private handler implementations ──────────────────────────────────────────
|
//Private handler implementations
|
||||||
|
|
||||||
impl PMActor {
|
impl PMActor {
|
||||||
fn handle_allocate(
|
fn handle_allocate(
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl BitmapPMM {
|
|||||||
*PMM.lock() = Some(pmm);
|
*PMM.lock() = Some(pmm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Core operations ───────────────────────────────────────────────────────
|
//Core operations
|
||||||
|
|
||||||
/// Mark a frame as free. Idempotent (double-free is a no-op, not UB).
|
/// Mark a frame as free. Idempotent (double-free is a no-op, not UB).
|
||||||
pub fn free_frame(&mut self, phys_addr: PhysAddr) {
|
pub fn free_frame(&mut self, phys_addr: PhysAddr) {
|
||||||
@@ -175,7 +175,7 @@ impl BitmapPMM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Module-level convenience functions ───────────────────────────────────────
|
//Module-level convenience functions
|
||||||
|
|
||||||
pub fn alloc_frame() -> Option<PhysAddr> {
|
pub fn alloc_frame() -> Option<PhysAddr> {
|
||||||
PMM.lock().as_mut()?.alloc_frame()
|
PMM.lock().as_mut()?.alloc_frame()
|
||||||
|
|||||||
@@ -37,10 +37,7 @@ use crate::events::MMU_REVOCATION_QUEUE;
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Error type
|
// Error type
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
/// PMM returned `None` — no physical frames available.
|
/// PMM returned `None` — no physical frames available.
|
||||||
@@ -80,9 +77,7 @@ impl core::fmt::Display for VmError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// VMA flags
|
// VMA flags
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -113,9 +108,7 @@ impl VmaFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// VMA backing
|
// VMA backing
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VmaBacking {
|
pub enum VmaBacking {
|
||||||
@@ -159,9 +152,7 @@ impl VmaBacking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// VMA region
|
// VMA region
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VmaRegion {
|
pub struct VmaRegion {
|
||||||
@@ -180,9 +171,7 @@ impl VmaRegion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// ASID / PCID allocator (bitmap-based, const-initializable, O(1) amortised)
|
// ASID / PCID allocator (bitmap-based, const-initializable, O(1) amortised)
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// x86-64 PCIDs: 0 (kernel, no PCID tagging) and 4095 (reserved by spec).
|
/// x86-64 PCIDs: 0 (kernel, no PCID tagging) and 4095 (reserved by spec).
|
||||||
/// Valid user ASIDs: 1 – 4094 inclusive.
|
/// Valid user ASIDs: 1 – 4094 inclusive.
|
||||||
@@ -262,9 +251,7 @@ fn free_asid(asid: u16) {
|
|||||||
ASID_ALLOC.lock().free(asid);
|
ASID_ALLOC.lock().free(asid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// CPU feature detection (call once during boot, before first activate())
|
// CPU feature detection (call once during boot, before first activate())
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Set to `true` at boot if CPUID.07H:EBX[10] = 1 (INVPCID supported).
|
/// Set to `true` at boot if CPUID.07H:EBX[10] = 1 (INVPCID supported).
|
||||||
static INVPCID_SUPPORTED: AtomicBool = AtomicBool::new(false);
|
static INVPCID_SUPPORTED: AtomicBool = AtomicBool::new(false);
|
||||||
@@ -302,9 +289,7 @@ pub fn init_cpu_features() {
|
|||||||
INVPCID_SUPPORTED.store(supported, Ordering::Relaxed);
|
INVPCID_SUPPORTED.store(supported, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Address space
|
// Address space
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
pub struct AddressSpace {
|
pub struct AddressSpace {
|
||||||
/// Hardware PCID written to CR3 bits 11:0.
|
/// Hardware PCID written to CR3 bits 11:0.
|
||||||
@@ -317,8 +302,6 @@ pub struct AddressSpace {
|
|||||||
hhdm: u64,
|
hhdm: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private helpers ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
impl AddressSpace {
|
impl AddressSpace {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn pml4_raw(&self) -> *mut PageTable {
|
unsafe fn pml4_raw(&self) -> *mut PageTable {
|
||||||
@@ -392,11 +375,7 @@ impl AddressSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Public API ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
impl AddressSpace {
|
impl AddressSpace {
|
||||||
// ─── Construction ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Allocate a fresh, empty address space with a zeroed PML4.
|
/// Allocate a fresh, empty address space with a zeroed PML4.
|
||||||
pub fn new(hhdm: u64) -> Result<Self, VmError> {
|
pub fn new(hhdm: u64) -> Result<Self, VmError> {
|
||||||
let pml4_phys = pmm::alloc_frame().ok_or(VmError::OutOfMemory)?;
|
let pml4_phys = pmm::alloc_frame().ok_or(VmError::OutOfMemory)?;
|
||||||
@@ -480,8 +459,6 @@ impl AddressSpace {
|
|||||||
Ok(virt)
|
Ok(virt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Zero-copy shared mapping (new) ───────────────────────────────────
|
|
||||||
|
|
||||||
/// Map `page_count` pages of physical memory owned by `owner_cap` into
|
/// Map `page_count` pages of physical memory owned by `owner_cap` into
|
||||||
/// this address space at `virt`.
|
/// this address space at `virt`.
|
||||||
///
|
///
|
||||||
@@ -533,8 +510,6 @@ impl AddressSpace {
|
|||||||
Ok(virt)
|
Ok(virt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Demand paging ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Handle a hardware page fault at `fault_addr`.
|
/// Handle a hardware page fault at `fault_addr`.
|
||||||
///
|
///
|
||||||
/// Returns `Ok(())` if the fault was a valid lazy demand-page (caller should
|
/// Returns `Ok(())` if the fault was a valid lazy demand-page (caller should
|
||||||
@@ -579,8 +554,6 @@ impl AddressSpace {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Unmapping ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Unmap the VMA containing `virt`, free its frames (if owned), flush TLB.
|
/// Unmap the VMA containing `virt`, free its frames (if owned), flush TLB.
|
||||||
pub fn unmap_region(&mut self, virt: VirtAddr) -> Result<(), VmError> {
|
pub fn unmap_region(&mut self, virt: VirtAddr) -> Result<(), VmError> {
|
||||||
let idx = self.find_idx(virt).ok_or(VmError::RegionNotFound)?;
|
let idx = self.find_idx(virt).ok_or(VmError::RegionNotFound)?;
|
||||||
@@ -591,8 +564,6 @@ impl AddressSpace {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Capability revocation ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Atomically unmap all VMAs associated with `cap_token` (skip PINNED).
|
/// Atomically unmap all VMAs associated with `cap_token` (skip PINNED).
|
||||||
///
|
///
|
||||||
/// Hardware access is terminated before this function returns.
|
/// Hardware access is terminated before this function returns.
|
||||||
@@ -618,14 +589,14 @@ impl AddressSpace {
|
|||||||
tlb_flush_asid(self.asid);
|
tlb_flush_asid(self.asid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Address translation ───────────────────────────────────────────────
|
//Address translation
|
||||||
|
|
||||||
/// Walk the live page table to translate `virt` → physical address.
|
/// Walk the live page table to translate `virt` → physical address.
|
||||||
pub fn translate(&self, virt: VirtAddr) -> Option<PhysAddr> {
|
pub fn translate(&self, virt: VirtAddr) -> Option<PhysAddr> {
|
||||||
unsafe { (*self.pml4_raw()).translate(virt, self.hhdm) }
|
unsafe { (*self.pml4_raw()).translate(virt, self.hhdm) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Activation ────────────────────────────────────────────────────────
|
//Activation
|
||||||
|
|
||||||
/// Load this address space into the CPU (context switch).
|
/// Load this address space into the CPU (context switch).
|
||||||
///
|
///
|
||||||
@@ -646,8 +617,6 @@ impl AddressSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Introspection ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
#[inline] pub fn regions(&self) -> &[VmaRegion] { &self.regions }
|
#[inline] pub fn regions(&self) -> &[VmaRegion] { &self.regions }
|
||||||
#[inline] pub fn region_count(&self) -> usize { self.regions.len() }
|
#[inline] pub fn region_count(&self) -> usize { self.regions.len() }
|
||||||
|
|
||||||
@@ -670,10 +639,7 @@ impl Drop for AddressSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// TLB management
|
// TLB management
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// Flush all TLB entries tagged with `asid` (PCID) on the current core.
|
/// Flush all TLB entries tagged with `asid` (PCID) on the current core.
|
||||||
///
|
///
|
||||||
/// Uses `INVPCID` type-1 (single-context flush) when available (Broadwell+,
|
/// Uses `INVPCID` type-1 (single-context flush) when available (Broadwell+,
|
||||||
@@ -714,10 +680,7 @@ pub fn tlb_flush_all() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
// Global kernel address space
|
// Global kernel address space
|
||||||
// ══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
/// The one kernel address space. Initialised once during boot.
|
/// The one kernel address space. Initialised once during boot.
|
||||||
pub static KERNEL_SPACE: Locked<Option<AddressSpace>> = Locked::new(None);
|
pub static KERNEL_SPACE: Locked<Option<AddressSpace>> = Locked::new(None);
|
||||||
|
|
||||||
|
|||||||
133
kernel/src/tty.rs
Normal file
133
kernel/src/tty.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use limine::framebuffer::Framebuffer;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct Psf2Header {
|
||||||
|
magic: u32,
|
||||||
|
version: u32,
|
||||||
|
header_size: u32,
|
||||||
|
flags: u32,
|
||||||
|
num_glyphs: u32,
|
||||||
|
bytes_per_glyph: u32,
|
||||||
|
height: u32,
|
||||||
|
width: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Console<'a> {
|
||||||
|
framebuffer: &'a Framebuffer<'a>,
|
||||||
|
font: &'static [u8],
|
||||||
|
pub x: usize,
|
||||||
|
pub y: usize,
|
||||||
|
pub fg_color: u32,
|
||||||
|
pub bg_color: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Console<'a> {
|
||||||
|
pub fn new(framebuffer: &'a Framebuffer<'a>, font: &'static [u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
framebuffer,
|
||||||
|
font,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
fg_color: 0xFFFFFF, // White
|
||||||
|
bg_color: 0x000000, // Black
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_color(&mut self, fg: u32) {
|
||||||
|
self.fg_color = fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
let fb = self.framebuffer;
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_bytes(fb.addr(), 0, (fb.pitch() * fb.height()) as usize);
|
||||||
|
}
|
||||||
|
self.x = 0;
|
||||||
|
self.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header(&self) -> &Psf2Header {
|
||||||
|
unsafe { &*(self.font.as_ptr() as *const Psf2Header) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll(&mut self) {
|
||||||
|
let header = self.header();
|
||||||
|
let font_height = header.height as usize;
|
||||||
|
let fb = self.framebuffer;
|
||||||
|
let pitch = fb.pitch() as usize;
|
||||||
|
let height = fb.height() as usize;
|
||||||
|
|
||||||
|
let shift = font_height * pitch;
|
||||||
|
let size = pitch * (height - font_height);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let addr = fb.addr();
|
||||||
|
core::ptr::copy(addr.add(shift), addr, size);
|
||||||
|
core::ptr::write_bytes(addr.add(size), 0, shift);
|
||||||
|
}
|
||||||
|
self.y -= font_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_glyph(&mut self, glyph_index: u32, x: usize, y: usize) {
|
||||||
|
let header = self.header();
|
||||||
|
let bytes_per_line = (header.width + 7) / 8;
|
||||||
|
let glyph_offset = header.header_size + (glyph_index * header.bytes_per_glyph);
|
||||||
|
|
||||||
|
let fb = self.framebuffer;
|
||||||
|
let fb_pitch = fb.pitch() as usize;
|
||||||
|
let fb_addr = fb.addr();
|
||||||
|
|
||||||
|
for cy in 0..header.height {
|
||||||
|
let glyph_row = self.font[(glyph_offset + cy * bytes_per_line) as usize];
|
||||||
|
for cx in 0..header.width {
|
||||||
|
if (glyph_row & (0x80 >> cx)) != 0 {
|
||||||
|
let offset = ((y + cy as usize) * fb_pitch) + ((x + cx as usize) * 4);
|
||||||
|
unsafe {
|
||||||
|
fb_addr.add(offset).cast::<u32>().write_volatile(self.fg_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_char(&mut self, c: char) {
|
||||||
|
let (font_width, font_height, num_glyphs) = {
|
||||||
|
let h = self.header();
|
||||||
|
(h.width as usize, h.height as usize, h.num_glyphs)
|
||||||
|
};
|
||||||
|
|
||||||
|
if c == '\n' {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += font_height;
|
||||||
|
} else {
|
||||||
|
if self.x + font_width > self.framebuffer.width() as usize {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += font_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
let glyph_index = if (c as u32) < num_glyphs {
|
||||||
|
c as u32
|
||||||
|
} else {
|
||||||
|
0 // symbol placeholder
|
||||||
|
};
|
||||||
|
|
||||||
|
self.draw_glyph(glyph_index, self.x, self.y);
|
||||||
|
self.x += font_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.y + font_height > self.framebuffer.height() as usize {
|
||||||
|
self.scroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for Console<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for c in s.chars() {
|
||||||
|
self.write_char(c);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user