diff --git a/kernel/src/arch/x86_64/apic/mod.rs b/kernel/src/arch/x86_64/apic/mod.rs index 9199a09..0d6f4ad 100644 --- a/kernel/src/arch/x86_64/apic/mod.rs +++ b/kernel/src/arch/x86_64/apic/mod.rs @@ -1,19 +1,14 @@ +#![allow(unused)] // TODO: Remove this when ready. use core::arch::x86_64::__cpuid; -use libk::drivers::memory::FoundryOSFrameAllocator; -use spin::Lazy; use x86_64::{ PhysAddr, VirtAddr, - instructions::port::Port, - registers::model_specific::Msr, - structures::paging::{ - FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, Translate, - }, + structures::paging::{Mapper, Size4KiB}, }; use crate::serial_print; -use super::{cpu::model_specific_registers::*, memmap::PHYSICAL_MEMORY_OFFSET}; +use super::{cpu::model_specific_registers::*, mem::memmap::PHYSICAL_MEMORY_OFFSET}; const IA32_APIC_BASE_MSR: u32 = 0x1b; const IA32_APIC_BASE_MSR_BSP: u64 = 0x100; @@ -68,27 +63,30 @@ pub fn check_apic() -> bool { #[inline(always)] unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr { let phys = phys.as_u64(); - match phys.checked_add(*PHYSICAL_MEMORY_OFFSET) { - Some(virt) => { - serial_print!("map worked!"); - VirtAddr::new(virt) - } - None => { + phys.checked_add(*PHYSICAL_MEMORY_OFFSET).map_or_else( + || { serial_print!("THIS IS A PROBLEM"); panic!("overflow") - } - } + }, + |virt| { + serial_print!("map worked!"); + VirtAddr::new(virt) + }, + ) } pub fn enable_apic( - mapper: &mut impl Mapper, - frame_allocator: &mut FoundryOSFrameAllocator, + _mapper: &mut impl Mapper, + // TODO: Fix this function. + // frame_allocator: &mut FoundryOSFrameAllocator, ) { - let apic_phys_addr = get_apic_base(); - set_apic_base_enable(apic_phys_addr); + unimplemented!(); + + // let apic_phys_addr = get_apic_base(); + // set_apic_base_enable(apic_phys_addr); // map virt address of apic - let apic_virt = unsafe { phys_to_virt(apic_phys_addr) }; + // let apic_virt = unsafe { phys_to_virt(apic_phys_addr) }; // let page: Page = Page::containing_address(apic_virt); // let frame: PhysFrame = PhysFrame::containing_address(apic_phys_addr); // let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; @@ -103,11 +101,11 @@ pub fn enable_apic( // // FIXME: this causes a page fault // // TODO: map to virtual memor - let reg = read_apic_register(&apic_virt, 0xF0); + // let reg = read_apic_register(&apic_virt, 0xF0); - serial_print!("ok2"); + // serial_print!("ok2"); - write_apic_register(&apic_virt, 0xF0, reg | 0x100); + // write_apic_register(&apic_virt, 0xF0, reg | 0x100); } pub struct Apic {} diff --git a/kernel/src/arch/x86_64/cpu.rs b/kernel/src/arch/x86_64/cpu.rs index 96416dc..b6036ef 100644 --- a/kernel/src/arch/x86_64/cpu.rs +++ b/kernel/src/arch/x86_64/cpu.rs @@ -3,11 +3,11 @@ pub mod model_specific_registers { use spin::Lazy; use x86_64::registers::model_specific::Msr; - const CPUID_FLAG_MSR: u32 = 1 << 5; - static EDX: Lazy = Lazy::new(|| unsafe { __cpuid(1).edx }); + const _CPUID_FLAG_MSR: u32 = 1 << 5; + static _EDX: Lazy = Lazy::new(|| unsafe { __cpuid(1).edx }); - pub fn cpu_has_msr() -> bool { - *EDX & CPUID_FLAG_MSR != 0 + pub fn _cpu_has_msr() -> bool { + *_EDX & _CPUID_FLAG_MSR != 0 } pub fn cpu_get_msr(msr: u32, value: &mut u64) { diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 7476eb6..7f7205a 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,14 +1,10 @@ -use libk::drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; +// use libk::drivers::mem::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; use libk::prelude::*; use pic8259::ChainedPics; -use x86_64::registers::control::Cr2; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; -use x86_64::structures::paging::mapper::MapperFlushAll; -use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB}; use spin::{Lazy, Mutex}; -use super::apic::enable_apic; use super::gdt; static IDT: Lazy = Lazy::new(|| { @@ -64,6 +60,7 @@ pub fn enable_pic() { } } +#[expect(unused)] pub fn disable_pic() { unsafe { PICS.lock().disable(); @@ -125,31 +122,32 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr } extern "x86-interrupt" fn page_fault_handler( - stack_frame: InterruptStackFrame, - error_code: PageFaultErrorCode, + _stack_frame: InterruptStackFrame, + _error_code: PageFaultErrorCode, ) { + todo!("Get this working again.") // serial_println!("Exception: Page Fault"); // serial_println!("Accessed Address: {:?}", Cr2::read()); // serial_println!("Error Code: {:?}", error_code); // serial_println!("{:#?}", stack_frame); - if let Some(frame_allocator) = FRAME_ALLOCATOR.get() { - let mut f = frame_allocator.lock(); + // if let Some(frame_allocator) = FRAME_ALLOCATOR.get() { + // let mut f = frame_allocator.lock(); - let frame = f.allocate_frame().unwrap(); - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - let page: Page = Page::containing_address(Cr2::read().unwrap()); + // let frame = f.allocate_frame().unwrap(); + // let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + // let page: Page = Page::containing_address(Cr2::read().unwrap()); - unsafe { - let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); + // unsafe { + // let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); - match mapper.map_to(page, frame, flags, &mut *f) { - Ok(_) => {} - Err(why) => panic!("failed to map page: {:?}", why), - } - } - MapperFlushAll::new().flush_all(); - } else { - panic!("failed to get frame allocator"); - } + // match mapper.map_to(page, frame, flags, &mut *f) { + // Ok(_) => {} + // Err(why) => panic!("failed to map page: {:?}", why), + // } + // } + // MapperFlushAll::new().flush_all(); + // } else { + // panic!("failed to get frame allocator"); + // } } diff --git a/kernel/src/arch/x86_64/memmap.rs b/kernel/src/arch/x86_64/mem/memmap.rs similarity index 100% rename from kernel/src/arch/x86_64/memmap.rs rename to kernel/src/arch/x86_64/mem/memmap.rs diff --git a/kernel/src/arch/x86_64/mem/mod.rs b/kernel/src/arch/x86_64/mem/mod.rs new file mode 100644 index 0000000..73e015a --- /dev/null +++ b/kernel/src/arch/x86_64/mem/mod.rs @@ -0,0 +1,2 @@ +pub mod memmap; +pub mod pmm; diff --git a/kernel/src/arch/x86_64/mem/pmm.rs b/kernel/src/arch/x86_64/mem/pmm.rs new file mode 100644 index 0000000..e69de29 diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 7ffe935..f27a450 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,9 +1,5 @@ -pub mod gdt; - -pub mod interrupts; - -pub mod memmap; - pub mod apic; - pub mod cpu; +pub mod gdt; +pub mod interrupts; +pub mod mem; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 48d4c75..3b8b079 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -13,16 +13,14 @@ extern crate alloc; -use arch::x86_64::apic::enable_apic; +// use arch::x86_64::apic::enable_apic; use core::arch::asm; -use libk::drivers::memory; +// use libk::drivers::mem::pmm; use limine::BaseRevision; -use libk::drivers::kalloc::allocator::init_heap; +// use libk::drivers::alloc::allocator::init_heap; use libk::prelude::*; -use x86_64::VirtAddr; - mod arch; /// Sets the base revision to the latest revision supported by the crate. @@ -58,9 +56,9 @@ pub fn boot() -> Result<(), &'static str> { return Err("base revision not supported"); } - use arch::x86_64::{gdt, interrupts, memmap}; + use arch::x86_64::{gdt, interrupts, mem::memmap}; - let memory_map = memmap::get_memory_map(); + let _memory_map = memmap::get_memory_map(); print_log!(" Initialising Serial... "); if libk::drivers::io::serial::init().is_err() { @@ -78,18 +76,19 @@ pub fn boot() -> Result<(), &'static str> { println_log!("[Success]"); print_log!(" Initialising Memory Subsystem... "); - let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); - let mut l4_table = memory::init_page_table(physical_memory_offset); + // let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); + // pmm::init_page_table(physical_memory_offset); println_log!("[Success]"); print_log!(" Setting Up Page Table... "); - memory::init_frame_allocator(memory_map); + // pmm::init_frame_allocator(memory_map); println_log!("[Success]"); print_log!(" Initialising Heap... "); - if init_heap().is_err() { - return Err("Failed to initialise heap: error"); - } + // TODO: Reenable the heap. + // if init_heap().is_err() { + // return Err("Failed to initialise heap: error"); + // } println_log!("[Success]"); print_log!(" Enabling PICs... "); diff --git a/libk/src/drivers/io/ascii/mod.rs b/libk/src/drivers/io/ascii/mod.rs index e57ab5d..b588750 100644 --- a/libk/src/drivers/io/ascii/mod.rs +++ b/libk/src/drivers/io/ascii/mod.rs @@ -72,7 +72,7 @@ impl Writer { } // Get the character data from the font array. -- each byte is a row of pixels - let data: &[u8] = &self.font.0[c as usize]; + let data: &[u8] = &self.font.glyphs[c as usize]; if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { for (row, line) in data.iter().enumerate().take(16) { diff --git a/libk/src/drivers/kalloc/allocator.rs b/libk/src/drivers/kalloc/allocator.rs deleted file mode 100644 index 5f8949b..0000000 --- a/libk/src/drivers/kalloc/allocator.rs +++ /dev/null @@ -1,73 +0,0 @@ -use linked_list_allocator::LockedHeap; -use x86_64::{ - VirtAddr, - structures::paging::{ - FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, - mapper::{MapToError, MapperFlushAll}, - }, -}; - -use crate::{ - drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}, - serial_print, serial_println, -}; - -/// We are currently using a linked list heap allocator which uses our underlying page allocator. -pub type FoundryAllocator = LockedHeap; - -#[global_allocator] -/// This is now Rust's global allocator, so we can use stuff requiring heap allocations. -static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty(); - -pub const HEAP_START: usize = 0x4444_4444_0000; -pub const HEAP_SIZE: usize = 1024 * 1024 * 1024; - -/// Sets up the heap using the backing page frame allocator. -pub fn init_heap() -> Result<(), MapToError> { - // let mut frame_allocator = if let Some(f) = FRAME_ALLOCATOR.get() { - // f.lock() - // } else { - // return Err(MapToError::FrameAllocationFailed); - // }; - - // let mut mapper = if let Some(m) = OFFSET_PAGE_TABLE.get() { - // m.lock() - // } else { - // return Err(MapToError::FrameAllocationFailed); - // }; - - // let range = { - // let heap_start = VirtAddr::new(HEAP_START as u64); - // let heap_end = heap_start + HEAP_SIZE as u64 - 1u64; - // let heap_start_page = Page::::containing_address(heap_start); - // let heap_end_page = Page::::containing_address(heap_end); - // Page::range_inclusive(heap_start_page, heap_end_page) - // }; - - // let usable_frames = frame_allocator.count_usable_frames(); - // serial_println!("usable frames: {}", usable_frames); - - // let mut i = 0; - // for page in range { - // i += 1; - // if i % 128 == 0 { - // serial_println!("allocated {} pages", i); - // } - // let frame = frame_allocator - // .allocate_frame() - // .ok_or(MapToError::FrameAllocationFailed)?; - // let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - // unsafe { - // // IMPORTANT: make sure to flush the mapper!!!! - // let _ = mapper.map_to(page, frame, flags, &mut *frame_allocator)?; - // } - // } - - // MapperFlushAll::new().flush_all(); - - unsafe { - ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE); - } - - Ok(()) -} diff --git a/libk/src/drivers/kalloc/mod.rs b/libk/src/drivers/kalloc/mod.rs deleted file mode 100644 index 98fe5c3..0000000 --- a/libk/src/drivers/kalloc/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod allocator; diff --git a/libk/src/drivers/memory.rs b/libk/src/drivers/memory.rs deleted file mode 100644 index 2e53a01..0000000 --- a/libk/src/drivers/memory.rs +++ /dev/null @@ -1,149 +0,0 @@ -// use lib_alloc::allocator::FoundryAllocator; -use limine::{memory_map::EntryType, response::MemoryMapResponse}; -use spin::{Mutex, Once}; -use x86_64::{ - PhysAddr, VirtAddr, - registers::control::Cr3, - structures::paging::{ - FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB, - page_table::FrameError, - }, -}; - -pub static FRAME_ALLOCATOR: Once> = Once::new(); -pub static OFFSET_PAGE_TABLE: Once> = Once::new(); -/// Returns a mutable reference to the current level 4 page table. -/// -/// # Safety -/// -/// 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 { - let (level_4_frame, _) = Cr3::read(); - - let phys_addr = level_4_frame.start_address(); - let virt = phys_addr.as_u64() + physical_memory_offset.as_u64(); - unsafe { &mut *(virt as *mut PageTable) } -} - -/// Initializes the `OffsetPageTable` for the current CPU architecture. -/// -/// # Safety -/// -/// This function must be called only once and should be called before any -/// memory operations are performed that rely on virtual memory management. -/// The provided `physical_memory_offset` must be accurate to ensure correct -/// translation of physical addresses. -/// -/// # Parameters -/// -/// - `physical_memory_offset`: The offset to convert physical addresses to -/// virtual addresses in the higher-half direct map. -/// -/// # Returns -/// -/// Returns an `OffsetPageTable` that allows for manipulation of the page -/// tables for the current CPU architecture. -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); - OFFSET_PAGE_TABLE.call_once(|| Mutex::new(offset_table)); - } -} - -pub struct FoundryOSFrameAllocator { - memory_map: &'static MemoryMapResponse, - next: usize, -} - -pub fn init_frame_allocator(memory_map: &'static MemoryMapResponse) { - unsafe { - FRAME_ALLOCATOR.call_once(|| Mutex::new(FoundryOSFrameAllocator::init(memory_map))); - } -} - -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. - pub const unsafe fn init(memory_map: &'static MemoryMapResponse) -> Self { - Self { - memory_map, - next: 0, - } - } - - pub fn count_usable_frames(&self) -> u32 { - self.usable_frames().count() as u32 - } - - /// An iterator over all usable frames in the memory map. - /// - /// Yields one `PhysFrame` for each available 4KiB frame in the memory map. - /// - /// This function is used to allocate frames for the pagemap. - fn usable_frames(&self) -> impl Iterator + use<> { - let regions = self.memory_map.entries().iter(); - let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE); - - let addr_ranges = usable_regions.map(|region| region.base..region.base + region.length); - - let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096)); - - frame_addresses.map(|addr| PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap()) - } -} - -unsafe impl FrameAllocator 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. - /// - /// # Returns - /// - /// - `Some(PhysFrame)`: If a usable frame is available. - /// - `None`: If there are no more usable frames to allocate. - fn allocate_frame(&mut self) -> Option { - let frame = self.usable_frames().nth(self.next); - self.next += 1; - frame - } -} - -// pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { -// translate_addr_inner(addr, physical_memory_offset) -// } - -// fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option { -// let (l4_table_frame, _) = Cr3::read(); - -// let table_indexes = [ -// addr.p4_index(), -// addr.p3_index(), -// addr.p2_index(), -// addr.p1_index(), -// ]; -// let mut frame = l4_table_frame; - -// for &i in &table_indexes { -// let virt = physical_memory_offset + frame.start_address().as_u64(); -// let table_ptr: *const PageTable = virt.as_ptr(); -// let table = unsafe { &*table_ptr }; - -// let entry = &table[i]; - -// frame = match entry.frame() { -// Ok(frame) => frame, -// Err(FrameError::FrameNotPresent) => return None, -// Err(FrameError::HugeFrame) => panic!("huge frames are not supported!"), -// }; -// } - -// Some(frame.start_address() + u64::from(addr.page_offset())) -// } diff --git a/libk/src/drivers/mod.rs b/libk/src/drivers/mod.rs index c3e42d1..e0ff423 100644 --- a/libk/src/drivers/mod.rs +++ b/libk/src/drivers/mod.rs @@ -1,6 +1,3 @@ -pub mod io; -pub mod kalloc; - pub mod async_io; -pub mod memory; +pub mod io; pub mod pic; diff --git a/libk/src/drivers/pic.rs b/libk/src/drivers/pic.rs index 9c710f8..5dc1a2b 100644 --- a/libk/src/drivers/pic.rs +++ b/libk/src/drivers/pic.rs @@ -40,8 +40,8 @@ pub struct ChainedPics { } impl ChainedPics { - pub const unsafe fn new(offset1: u8, offset2: u8) -> Self { - ChainedPics { + pub const fn new(offset1: u8, offset2: u8) -> Self { + Self { pics: [ Pic { offset: offset1, @@ -57,20 +57,14 @@ impl ChainedPics { } } - /// . - /// - /// # Safety - /// - /// . - pub const unsafe fn new_contiguous(primary_offset: u8) -> Self { - unsafe { Self::new(primary_offset, primary_offset + 8) } + pub const fn new_contiguous(primary_offset: u8) -> Self { + Self::new(primary_offset, primary_offset + 8) } - /// Returns the initialize of this [`ChainedPics`]. - /// /// # Safety /// - /// . + /// This should be safe if called just once on initialisation, however + /// sending data to IO ports tends to have side-effects. pub unsafe fn initialize(&mut self) { unsafe { let mut wait_port: Port = Port::new(0x80); @@ -112,12 +106,12 @@ impl ChainedPics { } /// Reads the interrupt masks of both PICs. - pub unsafe fn read_masks(&mut self) -> [u8; 2] { + pub fn read_masks(&mut self) -> [u8; 2] { unsafe { [self.pics[0].read_mask(), self.pics[1].read_mask()] } } /// Writes the interrupt masks of both PICs. - pub unsafe fn write_masks(&mut self, mask1: u8, mask2: u8) { + pub fn write_masks(&mut self, mask1: u8, mask2: u8) { unsafe { self.pics[0].write_mask(mask1); self.pics[1].write_mask(mask2); @@ -125,8 +119,8 @@ impl ChainedPics { } /// Disables both PICs by masking all interrupts. - pub unsafe fn disable(&mut self) { - unsafe { self.write_masks(u8::MAX, u8::MAX) } + pub fn disable(&mut self) { + self.write_masks(u8::MAX, u8::MAX) } /// Do we handle this interrupt? @@ -137,7 +131,7 @@ impl ChainedPics { /// Figure out which (if any) PICs in our chain need to know about this /// interrupt. This is tricky, because all interrupts from `pics[1]` /// get chained through `pics[0]`. - pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) { + pub fn notify_end_of_interrupt(&mut self, interrupt_id: u8) { if self.handles_interrupt(interrupt_id) { if self.pics[1].handles_interrupt(interrupt_id) { unsafe { diff --git a/libk/src/lib.rs b/libk/src/lib.rs index 57f60cb..3147a8e 100644 --- a/libk/src/lib.rs +++ b/libk/src/lib.rs @@ -36,3 +36,12 @@ pub use crate::drivers::io::{ ascii::{_print, _print_log}, serial::_serial_write, }; + +pub type FoundryAllocator = linked_list_allocator::LockedHeap; + +#[global_allocator] +/// This is now Rust's global allocator, so we can use stuff requiring heap allocations. +static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty(); + +pub const HEAP_START: usize = 0x4444_4444_0000; +pub const HEAP_SIZE: usize = 1024 * 1024 * 1024; diff --git a/libk/src/resources/font/mod.rs b/libk/src/resources/font/mod.rs index e6d8246..83ddfc7 100644 --- a/libk/src/resources/font/mod.rs +++ b/libk/src/resources/font/mod.rs @@ -1,13 +1,26 @@ +// use libm::include_font; + use libm::include_font; pub mod ibm_vga_8x16; -pub static FONT_SPLEEN_8X16: Font = Font(include_font!( - "./libk/resources/font/spleen-8x16.psf" -)); +pub static FONT_SPLEEN_8X16: Font = + Font::new(include_font!("./libk/resources/font/spleen-8x16.psf")); -pub static FONT_CP850_8X16: Font = Font(include_font!( - "./libk/resources/font/cp850-8x16.psf" -)); +pub static FONT_CP850_8X16: Font = Font::new(include_font!("./libk/resources/font/cp850-8x16.psf")); -pub struct Font(pub [[u8; 16]; 512]); +pub struct Font { + pub glyphs: [[u8; 16]; 512], +} + +impl Default for Font { + fn default() -> Self { + Self::new([[0; 16]; 512]) + } +} + +impl Font { + pub const fn new(glyphs: [[u8; 16]; 512]) -> Self { + Self { glyphs } + } +}