refactor: fix warnings, add docs

This commit is contained in:
2022-03-23 18:36:56 +02:00
parent 38c9fa783d
commit d50cc08f86
34 changed files with 202 additions and 513 deletions
-422
View File
@@ -1,422 +0,0 @@
//
// #[no_mangle]
// static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty();
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Entry(u64);
/// Structure describing a single level of translation mappings
#[repr(C, align(0x1000))]
pub struct Table {
entries: [Entry; 512],
}
/// Wrapper for top-most level of address translation tables
#[repr(transparent)]
pub struct Space(Table);
bitflags! {
/// Attributes attached to each translation [Entry]
pub struct MapAttributes: u64 {
// TODO use 2 lower bits to determine mapping size?
/// nG bit -- determines whether a TLB entry associated with this mapping
/// applies only to current ASID or all ASIDs.
const NOT_GLOBAL = 1 << 11;
/// AF bit -- must be set by software, otherwise Access Error exception is
/// generated when the page is accessed
const ACCESS = 1 << 10;
/// The memory region is outer-shareable
const SH_OUTER = 2 << 8;
/// This page is used for device-MMIO mapping and uses MAIR attribute #1
const DEVICE = 1 << 2;
/// Pages marked with this bit are Copy-on-Write
const EX_COW = 1 << 55;
/// UXN bit -- if set, page may not be used for instruction fetching from EL0
const UXN = 1 << 54;
/// PXN bit -- if set, page may not be used for instruction fetching from EL1
const PXN = 1 << 53;
// AP field
// Default behavior is: read-write for EL1, no access for EL0
/// If set, the page referred to by this entry is read-only for both EL0/EL1
const AP_BOTH_READONLY = 3 << 6;
/// If set, the page referred to by this entry is read-write for both EL0/EL1
const AP_BOTH_READWRITE = 1 << 6;
}
}
impl Table {
/// Returns next-level translation table reference for `index`, if one is present.
/// If `index` represents a `Block`-type mapping, will return an error.
/// If `index` does not map to any translation table, will try to allocate, init and
/// map a new one, returning it after doing so.
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Table, Errno> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
return Err(Errno::InvalidArgument);
}
Ok(unsafe { &mut *(mem::virtualize(entry.address_unchecked()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = Entry::table(phys, MapAttributes::empty());
res.entries.fill(Entry::invalid());
Ok(res)
}
}
/// Returns next-level translation table reference for `index`, if one is present.
/// Same as [next_level_table_or_alloc], but returns `None` if no table is mapped.
pub fn next_level_table(&mut self, index: usize) -> Option<&'static mut Table> {
let entry = self[index];
if entry.is_present() {
if !entry.is_table() {
panic!("Entry is not a table: idx={}", index);
}
Some(unsafe { &mut *(mem::virtualize(entry.address_unchecked()) as *mut _) })
} else {
None
}
}
/// Constructs and fills a [Table] with non-present mappings
pub const fn empty() -> Table {
Table {
entries: [Entry::invalid(); 512],
}
}
}
impl Index<usize> for Table {
type Output = Entry;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl IndexMut<usize> for Table {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.entries[index]
}
}
impl Entry {
const PRESENT: u64 = 1 << 0;
const TABLE: u64 = 1 << 1;
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
/// Constructs a single non-present mapping
pub const fn invalid() -> Self {
Self(0)
}
/// Constructs a `Block`-type memory mapping
pub const fn block(phys: usize, attrs: MapAttributes) -> Self {
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT)
}
/// Constructs a `Table` or `Page`-type mapping depending on translation level
/// this entry is used at
pub const fn table(phys: usize, attrs: MapAttributes) -> Self {
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT | Self::TABLE)
}
/// Returns `true` if this entry is not invalid
pub const fn is_present(self) -> bool {
self.0 & Self::PRESENT != 0
}
/// Returns `true` if this entry is a `Table` or `Page`-type mapping
pub const fn is_table(self) -> bool {
self.0 & Self::TABLE != 0
}
/// Returns the target address of this translation entry.
///
/// # Safety
///
/// Does not check if the entry is actually valid.
pub const unsafe fn address_unchecked(self) -> usize {
(self.0 & Self::PHYS_MASK) as usize
}
unsafe fn set_address(&mut self, address: usize) {
self.0 &= !Self::PHYS_MASK;
self.0 |= (address as u64) & Self::PHYS_MASK;
}
unsafe fn fork_flags(self) -> MapAttributes {
MapAttributes::from_bits_unchecked(self.0 & !Self::PHYS_MASK)
}
fn set_cow(&mut self) {
self.0 |= (MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
}
fn clear_cow(&mut self) {
self.0 &= !(MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
self.0 |= MapAttributes::AP_BOTH_READWRITE.bits();
}
#[inline]
fn is_cow(self) -> bool {
let attrs = (MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW).bits();
self.0 & attrs == attrs
}
}
impl Space {
/// Creates a new virtual address space and fills it with [Entry::invalid()]
/// mappings. Does physical memory page allocation.
pub fn alloc_empty() -> Result<&'static mut Self, Errno> {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
res.0.entries.fill(Entry::invalid());
Ok(res)
}
/// Inserts a single `virt` -> `phys` translation entry to this address space.
///
/// TODO: only works with 4K-sized pages at this moment.
pub fn map(&mut self, virt: usize, phys: usize, flags: MapAttributes) -> Result<(), Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table_or_alloc(l0i)?;
let l2_table = l1_table.next_level_table_or_alloc(l1i)?;
if l2_table[l2i].is_present() {
Err(Errno::AlreadyExists)
} else {
l2_table[l2i] = Entry::table(phys, flags | MapAttributes::ACCESS);
#[cfg(feature = "verbose")]
debugln!("{:#p} Map {:#x} -> {:#x}, {:?}", self, virt, phys, flags);
Ok(())
}
}
/// Translates a virtual address into a corresponding physical one.
///
/// Only works for 4K pages atm.
// TODO extract attributes
pub fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if entry.is_present() {
Ok(unsafe { entry.address_unchecked() })
} else {
Err(Errno::DoesNotExist)
}
}
/// Attempts to resolve a page fault at `virt` address by copying the
/// underlying Copy-on-Write mapping (if any is present)
pub fn try_cow_copy(&mut self, virt: usize) -> Result<(), Errno> {
let virt = virt & !0xFFF;
let l0i = virt >> 30;
let l1i = (virt >> 21) & 0x1FF;
let l2i = (virt >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if !entry.is_present() {
warnln!("Entry is not present: {:#x}", virt);
return Err(Errno::DoesNotExist);
}
let src_phys = unsafe { entry.address_unchecked() };
if !entry.is_cow() {
warnln!(
"Entry is not marked as CoW: {:#x}, points to {:#x}",
virt,
src_phys
);
return Err(Errno::DoesNotExist);
}
let dst_phys = unsafe { phys::copy_cow_page(src_phys)? };
unsafe {
l2_table[l2i].set_address(dst_phys);
}
l2_table[l2i].clear_cow();
Ok(())
}
/// Allocates a contiguous region from the address space and maps
/// physical pages to it
pub fn allocate(
&mut self,
start: usize,
end: usize,
len: usize,
flags: MapAttributes,
usage: PageUsage,
) -> Result<usize, Errno> {
'l0: for page in (start..end).step_by(0x1000) {
for i in 0..len {
if self.translate(page + i * 0x1000).is_ok() {
continue 'l0;
}
}
for i in 0..len {
let phys = phys::alloc_page(usage).unwrap();
self.map(page + i * 0x1000, phys, flags).unwrap();
}
return Ok(page);
}
Err(Errno::OutOfMemory)
}
/// Removes a single 4K page mapping from the table and
/// releases the underlying physical memory
pub fn unmap_single(&mut self, page: usize) -> Result<(), Errno> {
let l0i = page >> 30;
let l1i = (page >> 21) & 0x1FF;
let l2i = (page >> 12) & 0x1FF;
let l1_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l2i];
if !entry.is_present() {
return Err(Errno::DoesNotExist);
}
let phys = unsafe { entry.address_unchecked() };
unsafe {
phys::free_page(phys)?;
}
l2_table[l2i] = Entry::invalid();
unsafe {
asm!("tlbi vaae1, {}", in(reg) page);
}
// TODO release paging structure memory
Ok(())
}
/// Releases a range of virtual pages and their corresponding physical pages
pub fn free(&mut self, start: usize, len: usize) -> Result<(), Errno> {
for i in 0..len {
self.unmap_single(start + i * 0x1000)?;
}
Ok(())
}
/// Performs a copy of the address space, cloning data owned by it
pub fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
for l0i in 0..512 {
if let Some(l1_table) = self.0.next_level_table(l0i) {
for l1i in 0..512 {
if let Some(l2_table) = l1_table.next_level_table(l1i) {
for l2i in 0..512 {
let entry = l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
let src_phys = unsafe { entry.address_unchecked() };
let virt_addr = (l0i << 30) | (l1i << 21) | (l2i << 12);
let dst_phys = unsafe { phys::fork_page(src_phys)? };
let mut flags = unsafe { entry.fork_flags() };
if dst_phys != src_phys {
todo!();
// res.map(virt_addr, dst_phys, flags)?;
} else {
let writable = flags & MapAttributes::AP_BOTH_READONLY
== MapAttributes::AP_BOTH_READWRITE;
if writable {
flags |=
MapAttributes::AP_BOTH_READONLY | MapAttributes::EX_COW;
l2_table[l2i].set_cow();
unsafe {
asm!("tlbi vaae1, {}", in(reg) virt_addr);
}
}
res.map(virt_addr, dst_phys, flags)?;
}
}
}
}
}
}
Ok(res)
}
/// Releases all the mappings from the address space. Frees all
/// memory pages referenced by this space as well as those used for
/// its paging tables.
///
/// # Safety
///
/// Unsafe: may invalidate currently active address space
pub unsafe fn release(space: &mut Self) {
for l0i in 0..512 {
let l0_entry = space.0[l0i];
if !l0_entry.is_present() {
continue;
}
assert!(l0_entry.is_table());
let l1_table = &mut *(mem::virtualize(l0_entry.address_unchecked()) as *mut Table);
for l1i in 0..512 {
let l1_entry = l1_table[l1i];
if !l1_entry.is_present() {
continue;
}
assert!(l1_entry.is_table());
let l2_table = &mut *(mem::virtualize(l1_entry.address_unchecked()) as *mut Table);
for l2i in 0..512 {
let entry = l2_table[l2i];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
phys::free_page(entry.address_unchecked()).unwrap();
}
phys::free_page(l1_entry.address_unchecked()).unwrap();
}
phys::free_page(l0_entry.address_unchecked()).unwrap();
}
memset(space as *mut Space as *mut u8, 0, 4096);
}
/// Returns the physical address of this structure
pub fn address_phys(&mut self) -> usize {
(self as *mut _ as usize) - mem::KERNEL_OFFSET
}
}
+5
View File
@@ -85,6 +85,11 @@ impl From<MapAttributes> for RawAttributesImpl {
}
}
/// Performs initialization of virtual memory control by kernel
///
/// # Safety
///
/// Only safe to be called once during virtual memory init.
pub unsafe fn enable() {
fixed::init_device_map();
+7 -4
View File
@@ -1,3 +1,4 @@
//! x86_64 common boot logic
use crate::arch::x86_64::{
self, gdt, idt, intc,
reg::{CR0, CR4},
@@ -7,22 +8,24 @@ use crate::config::{ConfigKey, CONFIG};
use crate::debug;
use crate::dev::{display::FramebufferInfo, irq::IntSource, pseudo, Device};
use crate::font;
use crate::fs::{devfs::{self, CharDeviceType}, sysfs};
use crate::fs::{
devfs::{self, CharDeviceType},
sysfs,
};
use crate::mem::{
self, heap,
phys::{self, MemoryRegion, PageUsage, ReservedRegion},
virt,
};
use crate::proc;
use core::arch::{asm, global_asm};
use core::arch::global_asm;
use core::mem::MaybeUninit;
use multiboot2::{BootInformation, MemoryArea};
use tock_registers::interfaces::ReadWriteable;
static mut RESERVED_REGION_MB2: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
#[no_mangle]
extern "C" fn __x86_64_bsp_main(mb_checksum: u32, mb_info_ptr: u32) -> ! {
extern "C" fn __x86_64_bsp_main(_mb_checksum: u32, mb_info_ptr: u32) -> ! {
CR4.modify(CR4::OSXMMEXCPT::SET + CR4::OSFXSR::SET);
CR0.modify(CR0::EM::CLEAR + CR0::MP::SET);
+1
View File
@@ -1,3 +1,4 @@
//! Thread context
use crate::arch::platform::ForkFrame;
use crate::mem::{
self,
+9 -4
View File
@@ -1,11 +1,12 @@
use crate::arch::{intrin, x86_64};
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use core::arch::{asm, global_asm};
use libsys::{error::Errno, signal::Signal};
use crate::mem::{self, virt::table::Space};
use crate::proc::{Thread, sched};
use crate::proc::{sched, Thread};
use core::arch::asm;
use libsys::{error::Errno, signal::Signal};
#[allow(dead_code)]
#[derive(Debug)]
struct ExceptionFrame {
r15: u64,
@@ -80,7 +81,11 @@ extern "C" fn __x86_64_exception_handler(frame: &mut ExceptionFrame) {
});
if res.is_err() {
errorln!("Page fault at {:#x} in user {:?}", frame.rip, thread.owner_id());
errorln!(
"Page fault at {:#x} in user {:?}",
frame.rip,
thread.owner_id()
);
pfault_dump(Level::Error, frame, cr2);
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
+12 -6
View File
@@ -1,6 +1,7 @@
use core::arch::asm;
use core::mem::size_of_val;
#[allow(dead_code)]
#[repr(packed)]
struct Entry {
limit_lo: u16,
@@ -11,6 +12,7 @@ struct Entry {
base_hi: u8,
}
#[allow(dead_code)]
#[repr(packed)]
struct Tss {
__res0: u32,
@@ -30,6 +32,7 @@ struct Tss {
iopb_base: u16,
}
#[allow(dead_code)]
#[repr(packed)]
struct Pointer {
size: u16,
@@ -123,6 +126,14 @@ static mut GDT: [Entry; SIZE] = [
Entry::null(),
];
#[inline]
unsafe fn load_gdt(ptr: &Pointer, tr: u16) {
asm!(r#"
lgdt ({})
ltr %ax
"#, in(reg) ptr, in("ax") tr, options(att_syntax));
}
pub unsafe fn init() {
let tss_addr = &TSS as *const _ as usize;
@@ -138,10 +149,5 @@ pub unsafe fn init() {
size: size_of_val(&GDT) as u16 - 1,
offset: &GDT as *const _ as usize,
};
asm!(r#"
lgdt ({})
mov $0x28, %ax
ltr %ax
"#, in(reg) &gdtr, options(att_syntax));
load_gdt(&gdtr, 0x28);
}
+9 -2
View File
@@ -1,6 +1,7 @@
use core::arch::{asm, global_asm};
use core::mem::size_of_val;
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct Entry {
@@ -13,6 +14,7 @@ pub struct Entry {
__res1: u32,
}
#[allow(dead_code)]
#[repr(packed)]
struct Pointer {
limit: u16,
@@ -52,7 +54,12 @@ impl Entry {
static mut IDT: [Entry; SIZE] = [Entry::empty(); SIZE];
pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
#[inline]
unsafe fn load_idt(ptr: &Pointer) {
asm!("lidt ({})", in(reg) ptr, options(att_syntax));
}
pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE])>(f: F) {
extern "C" {
static __x86_64_exception_vectors: [usize; 32];
}
@@ -67,7 +74,7 @@ pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
limit: size_of_val(&IDT) as u16 - 1,
offset: &IDT as *const _ as usize,
};
asm!("lidt ({})", in(reg) &idtr, options(att_syntax));
load_idt(&idtr);
}
global_asm!(include_str!("idt.S"), options(att_syntax));
+3
View File
@@ -24,13 +24,16 @@ pub(super) struct I8259 {
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; 15]>,
}
/// Interrupt line number wrapper struct
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct IrqNumber(u32);
impl IrqNumber {
/// IRQ line number limit
pub const MAX: u32 = 16;
/// Constructs a wrapped IRQ line number
pub const fn new(u: u32) -> Self {
if u > Self::MAX {
panic!();
+11 -1
View File
@@ -28,8 +28,13 @@ pub unsafe fn flush_tlb_virt(addr: usize) {
/// Only safe to use for known [Process]es and their ASIDs
// TODO actually implement this on x86-64
#[inline(always)]
pub unsafe fn flush_tlb_asid(asid: usize) {}
pub unsafe fn flush_tlb_asid(_asid: usize) {}
/// Read a value from a model-specific register
///
/// # Safety
///
/// Unsafe: arbitrary MSR reads, may cause CPU exceptions
#[inline(always)]
pub unsafe fn rdmsr(a: u32) -> u64 {
let mut eax: u32;
@@ -38,6 +43,11 @@ pub unsafe fn rdmsr(a: u32) -> u64 {
(eax as u64) | ((edx as u64) << 32)
}
/// Writes a value to a model-specific register
///
/// # Safety
///
/// Unsafe: arbitrary MSR writes
#[inline(always)]
pub unsafe fn wrmsr(a: u32, b: u64) {
let eax = b as u32;
+4
View File
@@ -1,3 +1,4 @@
//! x86_64 platform implementation details
use crate::dev::{display::StaticFramebuffer, irq::IntController, serial::SerialDevice};
use core::arch::asm;
@@ -49,14 +50,17 @@ pub unsafe fn irq_restore(state: u64) {
}
}
/// Returns a reference to interrupt controller device
pub fn intc() -> &'static impl IntController {
&INTC
}
/// Returns a reference to primary console device
pub fn console() -> &'static impl SerialDevice {
&COM1
}
/// Returns a reference to CPU-local timer device
pub fn local_timer() -> &'static Timer {
&TIMER
}
+17
View File
@@ -1,10 +1,14 @@
//! x86_64 model-specific and control register interfaces
macro_rules! wrap_msr {
($struct_name:ident, $name:ident, $address:expr, $fields:tt) => {
register_bitfields! {
u64,
#[allow(missing_docs)]
pub $name $fields
}
#[allow(missing_docs)]
pub struct $struct_name;
impl Readable for $struct_name {
@@ -31,6 +35,7 @@ macro_rules! wrap_msr {
}
}
#[allow(missing_docs)]
pub const $name: $struct_name = $struct_name;
}
}
@@ -45,21 +50,31 @@ use crate::arch::x86_64::intrin::{rdmsr, wrmsr};
// CRn registers
register_bitfields! {
u64,
/// Control register CR4 fields
#[allow(missing_docs)]
pub CR4 [
/// Indicates OS support for FXSR/FXRSTOR instructions
OSFXSR OFFSET(9) NUMBITS(1) [],
/// Indicates OS support for unmasked SIMD exceptions
OSXMMEXCPT OFFSET(10) NUMBITS(1) []
]
}
register_bitfields! {
u64,
/// Control register CR0 fields
#[allow(missing_docs)]
pub CR0 [
/// Indicates requirement for x87 emulation
EM OFFSET(2) NUMBITS(1) [],
/// Controls x87 exception handling
MP OFFSET(1) NUMBITS(1) []
]
}
#[allow(missing_docs)]
pub struct Cr4;
#[allow(missing_docs)]
pub struct Cr0;
impl Readable for Cr4 {
@@ -114,7 +129,9 @@ impl Writeable for Cr0 {
}
}
/// Control register CR4
pub const CR4: Cr4 = Cr4;
/// Control register CR0
pub const CR0: Cr0 = Cr0;
wrap_msr!(MsrIa32Efer, MSR_IA32_EFER, 0xC0000080, [
+7 -1
View File
@@ -4,12 +4,18 @@ use tock_registers::interfaces::{ReadWriteable, Writeable};
use libsys::abi::SystemCall;
use crate::syscall;
/// Syscall registers
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct SyscallFrame {
/// General-purpose registers
pub x: [usize; 13],
/// Caller (ring 3) saved stack pointer
pub saved_rsp: usize,
/// Caller (ring 3) saved processor flags
pub saved_rflags: usize,
/// Caller (ring 3) return address
pub saved_rip: usize,
}
@@ -19,7 +25,7 @@ pub(super) fn init() {
}
MSR_IA32_SFMASK.write(MSR_IA32_SFMASK::IF::SET);
MSR_IA32_LSTAR.set(__x86_64_syscall_entry as u64);
MSR_IA32_LSTAR.set(__x86_64_syscall_entry as usize as u64);
MSR_IA32_STAR
.write(MSR_IA32_STAR::SYSRET_CS_SS.val(0x1B - 8) + MSR_IA32_STAR::SYSCALL_CS_SS.val(0x08));
MSR_IA32_EFER.modify(MSR_IA32_EFER::SCE::SET);
+5 -7
View File
@@ -8,19 +8,17 @@ use crate::dev::{
Device,
};
use crate::proc;
use core::sync::atomic::{AtomicU64, Ordering};
use core::time::Duration;
use libsys::error::Errno;
use tock_registers::interfaces::{Readable, Writeable};
use core::sync::atomic::{AtomicU64, Ordering};
// 1.1931816666 MHz base freq
/// Generic timer struct
pub struct Timer {
data0: PortIo<u8>,
cmd: PortIo<u8>,
counter: AtomicU64,
irq: IrqNumber
irq: IrqNumber,
}
impl Device for Timer {
@@ -45,8 +43,9 @@ impl TimestampSource for Timer {
impl IntSource for Timer {
fn handle_irq(&self) -> Result<(), Errno> {
self.counter.fetch_add(1, Ordering::Relaxed);
let value = self.counter.fetch_add(1, Ordering::Relaxed);
proc::wait::tick();
pseudo::RANDOM.set_state(value as u32);
proc::switch();
Ok(())
}
@@ -63,9 +62,8 @@ impl Timer {
pub const fn new(irq: IrqNumber) -> Self {
Self {
data0: unsafe { PortIo::new(0x40) },
cmd: unsafe { PortIo::new(0x43) },
counter: AtomicU64::new(0),
irq
irq,
}
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ impl IntSource for Uart {
fn init_irqs(&'static self) -> Result<(), Errno> {
// TODO shared IRQs between COM# ports
x86_64::INTC.register_handler(self.irq, self)?;
self.ier.write((1 << 0));
self.ier.write(1 << 0);
x86_64::INTC.enable_irq(self.irq)?;
Ok(())
+3 -3
View File
@@ -1,10 +1,10 @@
use super::{table::TableImpl, SpaceImpl};
use super::table::TableImpl;
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
pub pml4: TableImpl,
pub pdpt: TableImpl,
pub pd: [TableImpl; 16]
pub pd: [TableImpl; 16],
}
// Upper mappings
@@ -12,5 +12,5 @@ pub struct FixedTableGroup {
pub(super) static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
pml4: TableImpl::empty(),
pdpt: TableImpl::empty(),
pd: [TableImpl::empty(); 16]
pd: [TableImpl::empty(); 16],
};
+16 -1
View File
@@ -1,3 +1,4 @@
//! x86_64 virtual memory management implementation
use crate::mem::virt::table::{MapAttributes, Entry};
use core::arch::asm;
use libsys::error::Errno;
@@ -8,12 +9,19 @@ pub use table::{EntryImpl, SpaceImpl};
use fixed::KERNEL_FIXED;
bitflags! {
/// Raw attributes for x86_64 [Entry] implementation
pub struct RawAttributesImpl: u64 {
/// Entry is valid and mapped
const PRESENT = EntryImpl::PRESENT;
/// Entry is writable by user processes
const WRITE = EntryImpl::WRITE;
/// Entry is accessible (readable) by user processes
const USER = EntryImpl::USER;
/// Entry points to a block instead of a next-level table
const BLOCK = EntryImpl::BLOCK;
/// Entry is global across virtual address spaces
const GLOBAL = 1 << 8;
/// Entry is marked as Copy-on-Write
const EX_COW = EntryImpl::EX_COW;
}
}
@@ -33,6 +41,11 @@ impl From<MapAttributes> for RawAttributesImpl {
}
}
/// Performs initialization of virtual memory control by kernel
///
/// # Safety
///
/// Only safe to be called once during virtual memory init.
pub unsafe fn enable() {
// Remove the lower mapping
KERNEL_FIXED.pml4[0] = EntryImpl::EMPTY;
@@ -41,6 +54,8 @@ pub unsafe fn enable() {
asm!("mov %cr3, %rax; mov %rax, %cr3", options(att_syntax));
}
pub fn map_device_memory(phys: usize, count: usize) -> Result<usize, Errno> {
/// Allocates a range of virtual memory of requested size and maps
/// it to specified device memory
pub fn map_device_memory(_phys: usize, _count: usize) -> Result<usize, Errno> {
todo!()
}
+27 -22
View File
@@ -1,3 +1,4 @@
use crate::arch::intrin;
use crate::mem::{
self,
phys::{self, PageUsage},
@@ -5,38 +6,44 @@ use crate::mem::{
};
use core::fmt;
use core::ops::{Index, IndexMut};
use libsys::{error::Errno, mem::memset};
use libsys::error::Errno;
use super::{RawAttributesImpl, KERNEL_FIXED};
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EntryImpl(u64);
/// Structure describing a single level of translation mappings
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
pub struct TableImpl {
entries: [EntryImpl; 512],
}
/// Top-level translation table wrapper
#[repr(transparent)]
pub struct SpaceImpl(TableImpl);
impl EntryImpl {
pub const PRESENT: u64 = 1 << 0;
pub const WRITE: u64 = 1 << 1;
pub const USER: u64 = 1 << 2;
pub const BLOCK: u64 = 1 << 7;
pub const EX_COW: u64 = 1 << 62;
pub(super) const PRESENT: u64 = 1 << 0;
pub(super) const WRITE: u64 = 1 << 1;
pub(super) const USER: u64 = 1 << 2;
pub(super) const BLOCK: u64 = 1 << 7;
pub(super) const EX_COW: u64 = 1 << 62;
pub const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
}
impl fmt::Debug for EntryImpl {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EntryImpl")
.field("address", &self.address())
.field("flags", &unsafe { RawAttributesImpl::from_bits_unchecked(self.0 & 0xFFF) })
.field("flags", &unsafe {
RawAttributesImpl::from_bits_unchecked(self.0 & 0xFFF)
})
.finish_non_exhaustive()
}
}
@@ -47,38 +54,39 @@ impl Entry for EntryImpl {
#[inline]
fn normal(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | EntryImpl::PRESENT)
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | Self::PRESENT)
}
#[inline]
fn block(addr: usize, attrs: MapAttributes) -> Self {
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | EntryImpl::BLOCK | EntryImpl::PRESENT)
Self((addr as u64) | RawAttributesImpl::from(attrs).bits() | Self::BLOCK | Self::PRESENT)
}
#[inline]
fn address(self) -> usize {
(self.0 & EntryImpl::PHYS_MASK) as usize
(self.0 & Self::PHYS_MASK) as usize
}
#[inline]
fn set_address(&mut self, virt: usize) {
todo!()
self.0 &= !Self::PHYS_MASK;
self.0 |= (virt as u64) & Self::PHYS_MASK;
}
#[inline]
fn is_present(self) -> bool {
self.0 & EntryImpl::PRESENT != 0
self.0 & Self::PRESENT != 0
}
#[inline]
fn is_normal(self) -> bool {
self.0 & EntryImpl::BLOCK == 0
self.0 & Self::BLOCK == 0
}
#[inline]
fn fork_with_cow(&mut self) -> Self {
self.0 &= !EntryImpl::WRITE;
self.0 |= EntryImpl::EX_COW;
self.0 &= !Self::WRITE;
self.0 |= Self::EX_COW;
*self
}
@@ -90,7 +98,7 @@ impl Entry for EntryImpl {
#[inline]
fn is_cow(self) -> bool {
self.0 & EntryImpl::EX_COW != 0
self.0 & Self::EX_COW != 0
}
#[inline]
@@ -202,9 +210,8 @@ impl Space for SpaceImpl {
};
unsafe {
use core::arch::asm;
res.write_last_level(virt_addr, new_entry, true, false)?;
asm!("invlpg ({})", in(reg) virt_addr, options(att_syntax));
intrin::flush_tlb_virt(virt_addr);
}
}
}
@@ -240,9 +247,7 @@ impl Space for SpaceImpl {
Err(Errno::AlreadyExists)
} else {
l2_table[l3i] = entry;
unsafe {
core::arch::asm!("invlpg ({})", in(reg) virt, options(att_syntax));
}
intrin::flush_tlb_virt(virt);
Ok(())
}
}
+4 -1
View File
@@ -64,13 +64,14 @@ impl fmt::Write for FramebufferOutput {
let fb = self.display.unwrap().framebuffer().unwrap();
for ch in s.chars() {
self.putc(&fb, ch);
self.putc(fb, ch);
}
Ok(())
}
}
/// Sets active display for debug console output
pub fn set_display(disp: &'static dyn Display) {
DISPLAY.lock().display = Some(disp);
}
@@ -236,6 +237,8 @@ impl FramebufferOutput {
}
pub fn putc(&mut self, fb: &FramebufferInfo, ch: char) {
#![allow(clippy::single_match)]
match self.esc {
EscapeState::None => {
match ch {
+16 -2
View File
@@ -1,24 +1,36 @@
//! Graphical display interfaces
use crate::dev::Device;
use libsys::error::Errno;
use crate::util::InitOnce;
/// Description of a framebuffer
pub struct FramebufferInfo {
/// Width in pixels
pub width: usize,
/// Height in pixels
pub height: usize,
/// Physical start address
pub phys_base: usize,
/// Virtual address where the framebuffer is mapped
pub virt_base: usize
}
/// Generic display interface
pub trait Display: Device {
/// Changes currently active display mode
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno>;
fn framebuffer<'a>(&'a self) -> Result<&'a FramebufferInfo, Errno>;
/// Returns currently active framebuffer information
fn framebuffer(&self) -> Result<&FramebufferInfo, Errno>;
}
/// Display configuration details
#[allow(dead_code)]
pub struct DisplayMode {
width: u16,
height: u16,
}
/// Generic single-mode framebuffer
pub struct StaticFramebuffer {
framebuffer: InitOnce<FramebufferInfo>
}
@@ -34,7 +46,7 @@ impl Device for StaticFramebuffer {
}
impl Display for StaticFramebuffer {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno> {
fn set_mode(&self, _mode: DisplayMode) -> Result<(), Errno> {
Err(Errno::InvalidOperation)
}
@@ -48,10 +60,12 @@ impl Display for StaticFramebuffer {
}
impl StaticFramebuffer {
/// Constructs an empty [StaticFramebuffer] object
pub const fn uninit() -> Self {
Self { framebuffer: InitOnce::new() }
}
/// Initializes the device from existing framebuffer object
pub fn set_framebuffer(&self, framebuffer: FramebufferInfo) {
self.framebuffer.init(framebuffer);
}
+1
View File
@@ -50,6 +50,7 @@ impl<'q> IrqContext<'q> {
Self { token, _0: PhantomData }
}
/// Returns the value this object was initialized with
pub const fn token(&self) -> usize {
self.token
}
+8 -2
View File
@@ -1,3 +1,5 @@
//! Text drawing routines and font support
use crate::util::InitOnce;
use libsys::mem::read_le32;
use crate::dev::display::FramebufferInfo;
@@ -5,6 +7,7 @@ use crate::dev::display::FramebufferInfo;
static FONT_DATA: &[u8] = include_bytes!("../../etc/default8x16.psfu");
static FONT: InitOnce<Font> = InitOnce::new();
/// Font data description structure
pub struct Font {
char_width: usize,
char_height: usize,
@@ -13,8 +16,9 @@ pub struct Font {
}
impl Font {
/// Renders a glyph onto the framebuffer
pub fn draw(&self, fb: &FramebufferInfo, bx: usize, by: usize, ch: char, fg: u32, bg: u32) {
if ch >= ' ' && ch < '\x7B' {
if (' ' .. '\x7B').contains(&ch) {
let char_data = &self.data[ch as usize * self.bytes_per_glyph..];
for iy in 0..self.char_height {
@@ -33,8 +37,9 @@ impl Font {
}
}
/// Sets up the global [Font] object from PSF
pub fn init() {
assert_eq!(read_le32(&FONT_DATA[..]), 0x864ab572);
assert_eq!(read_le32(FONT_DATA), 0x864ab572);
FONT.init(Font {
char_width: read_le32(&FONT_DATA[28..]) as usize,
@@ -44,6 +49,7 @@ pub fn init() {
});
}
/// Returns a reference to global [Font] object
pub fn get() -> &'static Font {
FONT.get()
}
+3 -2
View File
@@ -224,8 +224,9 @@ pub fn init() {
use crate::dev::timer::TimestampSource;
let mut writer = BufferWriter::new(buf);
// let time = machine::local_timer().timestamp()?;
// write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
let time = machine::local_timer().timestamp()?;
writeln!(&mut writer, "{} {}", time.as_secs(), time.subsec_nanos())
.map_err(|_| Errno::InvalidArgument)?;
Ok(writer.count())
});
}
-1
View File
@@ -11,7 +11,6 @@
alloc_error_handler,
asm_const,
core_intrinsics,
const_generics_defaults,
)]
#![no_std]
#![no_main]
+2
View File
@@ -6,8 +6,10 @@ pub mod virt;
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
/// 8-byte padding for x86_64 for user stack alignment
pub const USTACK_PADDING: usize = 8;
} else {
/// No stack alignment required
pub const USTACK_PADDING: usize = 0;
}
}
+1 -1
View File
@@ -17,7 +17,6 @@ use libsys::{
signal::Signal,
ProgramArgs,
};
use core::arch::asm;
/// Wrapper type for a process struct reference
pub type ProcessRef = Rc<Process>;
@@ -123,6 +122,7 @@ impl Process {
}
}
/// Sets a pending signal for process
pub fn set_signal(&self, signal: Signal) {
let mut lock = self.inner.lock();
let table = Self::space_phys(&mut lock);
+1 -1
View File
@@ -151,7 +151,7 @@ pub fn is_ready() -> bool {
#[inline(never)]
extern "C" fn idle_fn(_a: usize) -> ! {
loop {
// cortex_a::asm::wfi();
core::hint::spin_loop();
}
}
+4 -4
View File
@@ -73,10 +73,10 @@ pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
/// Suspends current process until some file descriptor
/// signals data available
pub fn select(
thread: ThreadRef,
mut rfds: Option<&mut FdSet>,
mut wfds: Option<&mut FdSet>,
timeout: Option<Duration>,
_thread: ThreadRef,
_rfds: Option<&mut FdSet>,
_wfds: Option<&mut FdSet>,
_timeout: Option<Duration>,
) -> Result<usize, Errno> {
todo!();
}
+11 -9
View File
@@ -1,7 +1,7 @@
//! System call argument ABI helpers
use crate::arch::intrin;
use crate::mem::{self, virt::table::{Entry, Space, SpaceImpl}};
use crate::mem::{self, virt::table::Space};
use crate::proc::Process;
use core::alloc::Layout;
use libsys::error::Errno;
@@ -13,13 +13,12 @@ macro_rules! invalid_memory {
warnln!($($args)+);
#[cfg(feature = "aggressive_syscall")]
{
todo!()
// use libsys::signal::Signal;
// use crate::proc::Thread;
use libsys::signal::Signal;
use crate::proc::Thread;
// let thread = Thread::current();
// let proc = thread.owner().unwrap();
// proc.enter_fault_signal(thread, Signal::SegmentationFault);
let thread = Thread::current();
let proc = thread.owner().unwrap();
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
return Err(Errno::InvalidArgument);
}
@@ -43,6 +42,7 @@ cfg_if! {
} else {
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
use crate::mem::virt::table::Entry;
let proc = Process::current();
proc.manipulate_space(|space| {
let entry = space.read_last_level(virt & !0xFFF);
@@ -132,7 +132,10 @@ pub fn option_struct_mut<'a, T>(base: usize) -> Result<Option<&'a mut T>, Errno>
}
/// Checks given argument and interprets it as a `Option<&'a mut [T]>`
pub fn option_struct_buf_mut<'a, T>(base: usize, count: usize) -> Result<Option<&'a mut [T]>, Errno> {
pub fn option_struct_buf_mut<'a, T>(
base: usize,
count: usize,
) -> Result<Option<&'a mut [T]>, Errno> {
if base == 0 {
Ok(None)
} else {
@@ -152,7 +155,6 @@ pub fn validate_ptr(base: usize, len: usize, write: bool) -> Result<(), Errno> {
}
let process = Process::current();
let asid = process.asid();
for i in (base / mem::PAGE_SIZE)..((base + len + mem::PAGE_SIZE - 1) / mem::PAGE_SIZE) {
if !is_el0_accessible(i * mem::PAGE_SIZE, write) {
+2
View File
@@ -20,6 +20,8 @@ impl<T> InitOnce<T> {
}
}
/// Returns a reference to the initialized value, if one is present.
/// Returns [None] otherwise.
pub fn as_ref_option(&self) -> Option<&T> {
if self.is_initialized() {
Some(self.get())