refactor: fix warnings, add docs
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
//! Thread context
|
||||
use crate::arch::platform::ForkFrame;
|
||||
use crate::mem::{
|
||||
self,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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!();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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, [
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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],
|
||||
};
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
alloc_error_handler,
|
||||
asm_const,
|
||||
core_intrinsics,
|
||||
const_generics_defaults,
|
||||
)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -2,40 +2,40 @@
|
||||
macro_rules! syscall {
|
||||
($num:expr) => {{
|
||||
let mut res: usize;
|
||||
asm!("svc #0", out("x0") res, in("x8") $num.repr(), options(nostack));
|
||||
core::arch::asm!("svc #0", out("x0") res, in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
core::arch::asm!("svc #0",
|
||||
inout("x0") res,
|
||||
in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
core::arch::asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1,
|
||||
in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
core::arch::asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1, in("x2") $a2,
|
||||
in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
core::arch::asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1, in("x2") $a2,
|
||||
in("x3") $a3, in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
|
||||
let mut res: usize = $a0;
|
||||
asm!("svc #0",
|
||||
core::arch::asm!("svc #0",
|
||||
inout("x0") res, in("x1") $a1, in("x2") $a2,
|
||||
in("x3") $a3, in("x4") $a4, in("x8") $num.repr(), options(nostack));
|
||||
res
|
||||
|
||||
@@ -10,7 +10,6 @@ use libsys::{
|
||||
OpenFlags, Stat, UserId,
|
||||
},
|
||||
};
|
||||
use core::arch::asm;
|
||||
use core::time::Duration;
|
||||
|
||||
// TODO document the syscall ABI
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::signal;
|
||||
use crate::sys::{
|
||||
sys_ex_clone, sys_ex_gettid, sys_ex_signal, sys_ex_thread_exit, sys_ex_thread_wait,
|
||||
sys_ex_clone, sys_ex_signal, sys_ex_thread_exit, sys_ex_thread_wait,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc, vec};
|
||||
use core::any::Any;
|
||||
use core::arch::asm;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// #![feature(asm)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
//
|
||||
// #![allow(unused_macros)]
|
||||
// #![allow(dead_code)]
|
||||
//
|
||||
|
||||
#![allow(unused_macros)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate libusr;
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#[macro_use]
|
||||
extern crate libusr;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitpid};
|
||||
|
||||
#[no_mangle]
|
||||
@@ -36,6 +34,7 @@ fn main() -> i32 {
|
||||
println!("Process {:?} exited with status {}", pid, status);
|
||||
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
} else {
|
||||
sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user