18 Commits

Author SHA1 Message Date
alnyan 5113516b28 Almost forking, UB 2022-01-23 20:19:43 +02:00
alnyan a799b71326 Better MSR/CR interface for x86_64 2022-01-12 16:48:48 +02:00
alnyan 9126f8ac68 tss rsp0 for proper isr entry 2022-01-12 15:34:51 +02:00
alnyan d14ca5ecdb Make x86_64 work (pre-syscalls) 2022-01-12 15:34:51 +02:00
alnyan c255c3fa08 feature: framebuffer console WIP 2022-01-12 15:34:47 +02:00
alnyan d4323e3c8f feature: framebuffer console WIP 2022-01-12 15:34:44 +02:00
alnyan ec5b5bc31b feature: initial support for x86_64 2021-12-11 13:09:41 +02:00
alnyan fcbe412732 feature: simple sysfs 2021-12-09 09:02:28 +02:00
alnyan b4b99915ef feature: verbose feature for libusr 2021-12-05 23:45:24 +02:00
alnyan afa7e4cecb feature: simple pseudo devices 2021-12-05 21:58:03 +02:00
alnyan 94450e6537 fix: use u128 for nanos in timer 2021-12-05 21:13:13 +02:00
alnyan 2985f1429e feature: update Orange Pi 3 code 2021-12-05 21:12:56 +02:00
alnyan 2b160343b7 fix: invalidate TLB on syscall CoW 2021-12-05 21:12:29 +02:00
alnyan b97db3a0c4 feature: better login program 2021-12-02 13:02:45 +02:00
alnyan bd3d4e964d fix: enter signal handler AFTER syscall abort 2021-12-01 12:37:49 +02:00
alnyan a577b2dcc4 refactor: fix clippy warnings 2021-11-30 20:34:17 +02:00
alnyan d0681eb589 refactor: replace u32 with Tid type 2021-11-30 18:35:10 +02:00
alnyan e965c25181 refactor: better Pid type 2021-11-30 18:07:39 +02:00
87 changed files with 4593 additions and 1511 deletions
Generated
+10
View File
@@ -93,9 +93,11 @@ dependencies = [
"cfg-if",
"cortex-a",
"fdt-rs",
"fs-macros",
"kernel-macros",
"libsys",
"memfs",
"multiboot2",
"tock-registers",
"vfs",
]
@@ -161,6 +163,14 @@ dependencies = [
"autocfg",
]
[[package]]
name = "multiboot2"
version = "0.12.2"
source = "git+https://github.com/alnyan/multiboot2?branch=expose-extra-traits-for-iters#7e86b55fa5ab82e54978021f8022068a1591166b"
dependencies = [
"bitflags",
]
[[package]]
name = "num-derive"
version = "0.3.3"
+25 -8
View File
@@ -17,19 +17,24 @@ CARGO_COMMON_OPTS=
ifeq ($(PROFILE),release)
CARGO_COMMON_OPTS+=--release
endif
ifeq ($(VERBOSE),1)
CARGO_COMMON_OPTS+=--features verbose
endif
CARGO_BUILD_OPTS=$(CARGO_COMMON_OPTS) \
--target=../etc/$(ARCH)-$(MACH).json
ifeq ($(VERBOSE),1)
CARGO_BUILD_OPTS+=--features verbose
endif
ifneq ($(MACH),)
CARGO_BUILD_OPTS+=--features mach_$(MACH)
endif
QEMU_OPTS=-s
ifeq ($(ARCH),x86_64)
$(error TODO)
MACH=none
QEMU_OPTS+=-cdrom $(O)/image.iso \
-M q35 \
-m 512 \
-serial mon:stdio \
-net none
else
ifeq ($(MACH),qemu)
QEMU_OPTS+=-kernel $(O)/kernel.bin \
@@ -65,7 +70,7 @@ endif
.PHONY: address error etc kernel src
all: kernel initrd
all: image
kernel:
cd kernel && cargo build $(CARGO_BUILD_OPTS)
@@ -87,16 +92,28 @@ ifeq ($(MACH),orangepi3)
$(O)/uImage
endif
image: kernel initrd
ifeq ($(ARCH),x86_64)
mkdir -p $(O)/image/boot/grub
cp etc/x86_64-none.grub $(O)/image/boot/grub/grub.cfg
cp $(O)/kernel $(O)/image/boot/kernel
cp $(O)/initrd.img $(O)/image/boot/initrd.img
grub-mkrescue -o $(O)/image.iso $(O)/image
endif
initrd:
cd user && cargo build \
--target=../etc/$(ARCH)-osdev5.json \
-Z build-std=core,alloc,compiler_builtins \
$(CARGO_COMMON_OPTS)
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev
touch $(O)/rootfs/dev/.do_no_remove
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev $(O)/rootfs/etc $(O)/rootfs/sys
cp etc/initrd/passwd $(O)/rootfs/etc
cp etc/initrd/shadow $(O)/rootfs/etc
touch $(O)/rootfs/dev/.do_not_remove
touch $(O)/rootfs/sys/.do_not_remove
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/cat $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/hexd $(O)/rootfs/bin
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
root:0:0:root:/:/bin/shell
alnyan:1000:1000:alnyan:/:/bin/shell
+2
View File
@@ -0,0 +1,2 @@
root:toor
alnyan:
+2 -1
View File
@@ -1,3 +1,4 @@
menuentry "OS" {
multiboot2 /boot/kernel.elf
multiboot2 /boot/kernel
module2 /boot/initrd.img
}
+5 -1
View File
@@ -6,7 +6,8 @@ SECTIONS {
. = 0x400000 + KERNEL_OFFSET;
.text : AT(. - KERNEL_OFFSET) {
KEEP(*(.multiboot))
KEEP(*(.text._multiboot))
*(.text._entry)
*(.text*)
}
@@ -21,5 +22,8 @@ SECTIONS {
.bss : AT(. - KERNEL_OFFSET) {
*(COMMON)
*(.bss*)
. = ALIGN(4K);
}
PROVIDE(__kernel_end = .);
}
+21
View File
@@ -0,0 +1,21 @@
{
"arch": "x86_64",
"cpu": "x86-64",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
"disable-redzone": true,
"executables": true,
"panic-strategy": "abort",
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-target": "x86_64-unknown-linux-gnu",
"max-atomic-width": 64,
"target-pointer-width": "64",
"os": "none",
"pre-link-args": {
"ld.lld": [ "-Tetc/x86_64-osdev5.ld" ]
}
}
+32
View File
@@ -0,0 +1,32 @@
ENTRY(_start);
PHDRS {
text PT_LOAD ;
rodata PT_LOAD ;
data PT_LOAD ;
}
SECTIONS {
. = 0x400000;
.text : {
*(.text._start)
*(.text*)
*(.eh_frame*)
} :text
. = ALIGN(0x1000);
.rodata : {
*(.rodata*)
} :rodata
. = ALIGN(0x1000);
.data : {
*(.data*)
} :data
.bss : {
*(COMMON)
*(.bss*)
} :data
}
+4 -3
View File
@@ -2,6 +2,7 @@ use crate::{VnodeKind, VnodeRef, Vnode};
use alloc::rc::Rc;
use core::cell::RefCell;
use core::cmp::min;
use core::str::FromStr;
use libsys::{
error::Errno,
stat::DirectoryEntry,
@@ -142,7 +143,7 @@ impl File {
return Ok(offset);
}
entries[offset] = DirectoryEntry::from_str(".");
entries[offset] = DirectoryEntry::from_str(".").unwrap();
inner.pos = Self::POS_CACHE_DOT_DOT;
offset += 1;
@@ -154,7 +155,7 @@ impl File {
return Ok(offset);
}
entries[offset] = DirectoryEntry::from_str("..");
entries[offset] = DirectoryEntry::from_str("..").unwrap();
inner.pos = 0;
offset += 1;
@@ -166,7 +167,7 @@ impl File {
}
let count = inner.vnode.for_each_entry(inner.pos, count, |i, e| {
entries[offset + i] = DirectoryEntry::from_str(e.name());
entries[offset + i] = DirectoryEntry::from_str(e.name()).unwrap();
});
inner.pos += count;
Ok(offset + count)
+19 -32
View File
@@ -1,6 +1,6 @@
use crate::{File, FileRef, Filesystem, Ioctx};
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
use core::cell::{RefCell, RefMut, Ref};
use core::cell::{Ref, RefCell, RefMut};
use core::fmt;
use libsys::{
error::Errno,
@@ -288,10 +288,8 @@ impl Vnode {
/// vnode and will load it from disk if it's missing.
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
if let Some(node) = self.lookup(name) {
return Ok(node);
}
if let Some(ref mut data) = *self.data() {
Ok(node)
} else if let Some(ref mut data) = *self.data() {
let vnode = data.lookup(self.clone(), name)?;
if let Some(fs) = self.fs() {
vnode.set_fs(fs);
@@ -386,13 +384,11 @@ impl Vnode {
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
} else if let Some(ref mut data) = *self.data() {
let pos = data.open(self.clone(), flags)?;
Ok(File::normal(self.clone(), pos, open_flags))
} else {
if let Some(ref mut data) = *self.data() {
let pos = data.open(self.clone(), flags)?;
Ok(File::normal(self.clone(), pos, open_flags))
} else {
Err(Errno::NotImplemented)
}
Err(Errno::NotImplemented)
}
}
@@ -400,22 +396,18 @@ impl Vnode {
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
Ok(())
} else if let Some(ref mut data) = *self.data() {
data.close(self.clone())
} else {
if let Some(ref mut data) = *self.data() {
data.close(self.clone())
} else {
Err(Errno::NotImplemented)
}
Err(Errno::NotImplemented)
}
}
/// Reads data from offset `pos` into `buf`
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Errno> {
if self.kind == VnodeKind::Directory {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.read(self.clone(), pos, buf)
} else {
Err(Errno::NotImplemented)
@@ -425,10 +417,8 @@ impl Vnode {
/// Writes data from `buf` to offset `pos`
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
if self.kind == VnodeKind::Directory {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.write(self.clone(), pos, buf)
} else {
Err(Errno::NotImplemented)
@@ -438,10 +428,8 @@ impl Vnode {
/// Resizes the vnode data
pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
}
if let Some(ref mut data) = *self.data() {
Err(Errno::IsADirectory)
} else if let Some(ref mut data) = *self.data() {
data.truncate(self.clone(), size)
} else {
Err(Errno::NotImplemented)
@@ -464,7 +452,7 @@ impl Vnode {
Ok(Stat {
blksize: 0,
size: 0,
mode: props.mode
mode: props.mode,
})
} else if let Some(ref mut data) = *self.data() {
data.stat(self.clone())
@@ -500,7 +488,6 @@ impl Vnode {
if access.intersects(AccessMode::R_OK | AccessMode::W_OK | AccessMode::X_OK) {
return Err(Errno::InvalidArgument);
}
return Ok(());
} else {
if access.contains(AccessMode::F_OK) {
return Err(Errno::InvalidArgument);
@@ -519,9 +506,9 @@ impl Vnode {
// TODO check group
// TODO check other
return Ok(());
}
Ok(())
}
}
+5 -1
View File
@@ -15,12 +15,16 @@ memfs = { path = "../fs/memfs" }
libsys = { path = "../libsys" }
cfg-if = "1.x.x"
tock-registers = "0.7.x"
fdt-rs = { version = "0.x.x", default-features = false }
bitflags = "^1.3.0"
kernel-macros = { path = "macros" }
fs-macros = { path = "../fs/macros" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
multiboot2 = { git = "https://github.com/alnyan/multiboot2", branch = "expose-extra-traits-for-iters" }
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "6.x.x" }
fdt-rs = { version = "0.x.x", default-features = false }
[features]
default = ["aggressive_syscall"]
+16 -11
View File
@@ -10,7 +10,8 @@ use crate::dev::{
irq::IntSource,
Device,
};
use crate::fs::devfs;
use crate::fs::{devfs, sysfs};
use crate::dev::pseudo;
use libsys::error::Errno;
//use crate::debug::Level;
use crate::mem::{
@@ -23,22 +24,16 @@ use cortex_a::asm::barrier::{self, dsb, isb};
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
use tock_registers::interfaces::{ReadWriteable, Writeable};
fn init_device_tree(fdt_base_phys: usize) -> Result<(), Errno> {
fn init_device_tree(fdt_base_phys: usize) -> Result<Option<DeviceTree>, Errno> {
use fdt_rs::prelude::*;
let fdt = if fdt_base_phys != 0 {
DeviceTree::from_phys(fdt_base_phys + 0xFFFFFF8000000000)?
} else {
warnln!("No FDT present");
return Ok(());
return Ok(None);
};
#[cfg(feature = "verbose")]
{
use crate::debug::Level;
fdt.dump(Level::Debug);
}
let mut cfg = CONFIG.lock();
if let Some(chosen) = fdt.node_by_path("/chosen") {
@@ -56,7 +51,7 @@ fn init_device_tree(fdt_base_phys: usize) -> Result<(), Errno> {
}
}
Ok(())
Ok(Some(fdt))
}
#[no_mangle]
@@ -87,7 +82,7 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
// Enable MMU
virt::enable().expect("Failed to initialize virtual memory");
init_device_tree(fdt_base).expect("Device tree init failed");
let fdt = init_device_tree(fdt_base).expect("Device tree init failed");
// Most basic machine init: initialize proper debug output
// physical memory
@@ -102,9 +97,19 @@ extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
}
devfs::init();
sysfs::init();
machine::init_board().unwrap();
#[cfg(feature = "verbose")]
if let Some(fdt) = fdt {
use crate::debug::Level;
fdt.dump(Level::Debug);
}
devfs::add_named_char_device(&pseudo::ZERO, "zero").unwrap();
devfs::add_named_char_device(&pseudo::RANDOM, "random").unwrap();
infoln!("Machine init finished");
unsafe {
+12 -15
View File
@@ -4,10 +4,10 @@ use crate::arch::machine;
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use crate::mem;
use crate::proc::{sched, Thread};
use crate::proc::{sched, Process, Thread};
use crate::syscall;
use cortex_a::registers::{ESR_EL1, FAR_EL1};
use libsys::{abi::SystemCall, signal::Signal};
use libsys::{abi::SystemCall, signal::Signal, error::Errno};
use tock_registers::interfaces::Readable;
/// Trapped SIMD/FP functionality
@@ -93,24 +93,21 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET && sched::is_ready() {
let thread = Thread::current();
let proc = thread.owner().unwrap();
let asid = proc.asid();
if proc
.manipulate_space(|space| space.try_cow_copy(far))
.is_err()
{
let res = proc.manipulate_space(|space| {
space.try_cow_copy(far)?;
Process::invalidate_asid(asid);
Result::<(), Errno>::Ok(())
});
if res.is_err() {
// Kill program
errorln!("Data abort from {:#x}", exc.elr_el1);
dump_data_abort(Level::Error, esr, far as u64);
proc.enter_fault_signal(thread, Signal::SegmentationFault);
}
unsafe {
use cortex_a::registers::TTBR0_EL1;
let ttbr = TTBR0_EL1.get() as usize;
let asid = (ttbr >> 48) & 0xFF;
asm!("tlbi aside1, {}", in(reg) (asid << 48));
}
return;
}
@@ -127,7 +124,7 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
if num == SystemCall::Fork {
match unsafe { syscall::sys_fork(exc) } {
Ok(pid) => exc.x[0] = pid.value() as usize,
Ok(pid) => exc.x[0] = u32::from(pid) as usize,
Err(err) => {
exc.x[0] = err.to_negative_isize() as usize;
}
@@ -150,7 +147,7 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
if sched::is_ready() {
let thread = Thread::current();
errorln!(
"Unhandled exception in thread {}, {:?}",
"Unhandled exception in thread {:?}, {:?}",
thread.id(),
thread.owner().map(|e| e.id())
);
@@ -96,4 +96,4 @@ static UART0: Uart = unsafe { Uart::new(UART0_BASE, IrqNumber::new(32)) };
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
pub(super) static GPIO: Gpio = unsafe { Gpio::new(PIO_BASE) };
static RTC: Rtc = unsafe { Rtc::new(RTC_BASE, RTC_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE, LOCAL_TIMER_IRQ) };
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
+422
View File
@@ -0,0 +1,422 @@
//
// #[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
}
}
+8 -4
View File
@@ -1,7 +1,9 @@
//! ARM generic timer implementation
use crate::arch::machine::{self, IrqNumber};
use crate::proc;
use crate::dev::{
pseudo,
irq::{IntController, IntSource},
timer::TimestampSource,
Device,
@@ -34,9 +36,9 @@ impl IntSource for GenericTimer {
fn handle_irq(&self) -> Result<(), Errno> {
CNTP_TVAL_EL0.set(TIMER_TICK);
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
use crate::proc;
proc::wait::tick();
proc::switch();
pseudo::RANDOM.set_state(CNTPCT_EL0.get() as u32);
Ok(())
}
@@ -50,9 +52,11 @@ impl IntSource for GenericTimer {
impl TimestampSource for GenericTimer {
fn timestamp(&self) -> Result<Duration, Errno> {
let cnt = CNTPCT_EL0.get() * 1_000_000_000;
let frq = CNTFRQ_EL0.get();
Ok(Duration::from_nanos(cnt / frq))
let cnt = (CNTPCT_EL0.get() as u128) * 1_000_000_000u128;
let frq = CNTFRQ_EL0.get() as u128;
let secs = ((cnt / frq) / 1_000_000_000) as u64;
let nanos = ((cnt / frq) % 1_000_000_000) as u32;
Ok(Duration::new(secs, nanos))
}
}
+5
View File
@@ -15,6 +15,11 @@ cfg_if! {
pub use aarch64 as platform;
pub use aarch64::machine;
} else if #[cfg(target_arch = "x86_64")] {
pub mod x86_64;
pub use x86_64 as platform;
pub use x86_64 as machine;
}
}
+53
View File
@@ -0,0 +1,53 @@
.section .text._multiboot
.set MAGIC, 0xE85250D6
.set ARCH, 0x0
.set HDRLEN, 16
.set CHKSUM, (-(MAGIC + ARCH + HDRLEN)) & 0xFFFFFFFF
.long MAGIC
.long ARCH
.long HDRLEN
.long CHKSUM
.short 5
.short 0
.long 20
.long 800
.long 600
.long 32
.short 0
.long 8
.section .text._entry
.global _entry
_entry:
.code32
cli
lea (multiboot_registers - KERNEL_OFFSET), %edi
mov %eax, 0(%edi)
mov %ebx, 4(%edi)
// Setup paging tables
lea (_entry_upper - KERNEL_OFFSET), %ebx
jmp __x86_64_enter_upper
.code64
_entry_upper:
movabsq $1f, %rax
jmp *%rax
1:
lea bsp_stack_top(%rip), %rax
mov %rax, %rsp
mov multiboot_registers(%rip), %edi
mov (4 + multiboot_registers)(%rip), %esi
call __x86_64_bsp_main
.section .bss
.align 16
bsp_stack_bottom:
.skip 65536
bsp_stack_top:
multiboot_registers:
.skip 8
+7
View File
@@ -0,0 +1,7 @@
.set KERNEL_OFFSET, 0xFFFFFF8000000000
.set PTE_PRESENT, 1 << 0
.set PTE_WRITABLE, 1 << 1
.set PTE_USERSPACE, 1 << 2
.set PTE_BLOCK, 1 << 7
+114
View File
@@ -0,0 +1,114 @@
use crate::arch::x86_64::{
self, gdt, idt, intc,
reg::{CR0, CR4},
syscall,
};
use crate::config::{ConfigKey, CONFIG};
use crate::debug;
use crate::dev::{display::FramebufferInfo, pseudo, Device};
use crate::font;
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::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) -> ! {
CR4.modify(CR4::OSXMMEXCPT::SET + CR4::OSFXSR::SET);
CR0.modify(CR0::EM::CLEAR + CR0::MP::SET);
unsafe {
// Setup a proper GDT
gdt::init();
idt::init(intc::map_isr_entries);
}
virt::enable().expect("Failed to initialize virtual memory");
let mb_info = unsafe {
multiboot2::load_with_offset(mb_info_ptr as usize, mem::KERNEL_OFFSET)
.expect("Failed to load multiboot info structure")
};
unsafe {
let mb_info_page = (mb_info_ptr & !0xFFF) as usize;
RESERVED_REGION_MB2.write(ReservedRegion::new(
mb_info_page,
mb_info_page + ((mb_info.total_size() + 0xFFF) & !0xFFF),
));
phys::reserve("multiboot2", RESERVED_REGION_MB2.as_mut_ptr());
phys::init_from_iter(
mb_info
.memory_map_tag()
.unwrap()
.memory_areas()
.map(|entry| MemoryRegion {
start: ((entry.start_address() + 0xFFF) & !0xFFF) as usize,
end: (entry.end_address() & !0xFFF) as usize,
}),
);
}
// Setup a heap
unsafe {
let heap_base_phys = phys::alloc_contiguous_pages(PageUsage::KernelHeap, 4096)
.expect("Failed to allocate memory for heap");
let heap_base_virt = mem::virtualize(heap_base_phys);
heap::init(heap_base_virt, 16 * 1024 * 1024);
}
let initrd_info = mb_info.module_tags().next().unwrap();
{
let mut cfg = CONFIG.lock();
cfg.set_usize(ConfigKey::InitrdBase, initrd_info.start_address() as usize);
cfg.set_usize(ConfigKey::InitrdSize, initrd_info.module_size() as usize);
}
// Setup hardware
unsafe {
x86_64::INTC.enable().ok();
}
let fb_info = mb_info.framebuffer_tag().unwrap();
let virt = mem::virtualize(fb_info.address as usize);
debugln!(
"Framebuffer base: phys={:#x}, virt={:#x}",
fb_info.address,
virt
);
x86_64::DISPLAY.set_framebuffer(FramebufferInfo {
width: fb_info.width as usize,
height: fb_info.height as usize,
phys_base: fb_info.address as usize,
virt_base: virt,
});
font::init();
debug::set_display(&x86_64::DISPLAY);
syscall::init();
devfs::init();
sysfs::init();
devfs::add_char_device(&x86_64::COM1, CharDeviceType::TtySerial).unwrap();
devfs::add_named_char_device(&pseudo::ZERO, "zero").unwrap();
devfs::add_named_char_device(&pseudo::RANDOM, "random").unwrap();
unsafe {
proc::enter();
}
}
global_asm!(include_str!("macros.S"), options(att_syntax));
global_asm!(include_str!("entry.S"), options(att_syntax));
global_asm!(include_str!("upper.S"), options(att_syntax));
+94
View File
@@ -0,0 +1,94 @@
.code32
.section .text._entry
__x86_64_enter_upper:
mov $(PTE_PRESENT | PTE_WRITABLE | PTE_USERSPACE), %edx
// Setup PML4
lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
lea (KERNEL_FIXED + 4096 - KERNEL_OFFSET), %esi
mov %edx, %eax
or %esi, %eax
// pml4[0] = %eax
mov %eax, (%edi)
// pml4[511] = %eax
mov %eax, 4088(%edi)
// Setup PDPT
mov %esi, %edi
lea (KERNEL_FIXED + 8192 - KERNEL_OFFSET), %esi
xor %ecx, %ecx
1:
// %eax = &table[%ecx] | attrs
mov %esi, %eax
or %edx, %eax
mov %eax, (%edi, %ecx, 8)
add $4096, %esi
inc %ecx
cmp $16, %ecx
jne 1b
// Setup PDs
lea (KERNEL_FIXED + 8192 - KERNEL_OFFSET), %edi
mov $(PTE_PRESENT | PTE_BLOCK | PTE_WRITABLE), %edx
mov $(512 * 16), %ecx
1:
dec %ecx
// %eax = attrs | (i << 21)
mov %ecx, %eax
shl $21, %eax
or %edx, %eax
mov %eax, (%edi, %ecx, 8)
test %ecx, %ecx
jnz 1b
// Enable PAE/PSE
mov %cr4, %eax
or $((1 << 5) | (1 << 4)), %eax
mov %eax, %cr4
// Enable EFER.LME
mov $0xC0000080, %ecx
rdmsr
or $(1 << 8), %eax
wrmsr
// Set CR3
lea (KERNEL_FIXED - KERNEL_OFFSET), %edi
mov %edi, %cr3
// Enable paging
mov %cr0, %eax
or $(1 << 31), %eax
mov %eax, %cr0
lgdt (gdtr64 - KERNEL_OFFSET)
ljmp $0x08, $(1f - KERNEL_OFFSET)
1:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
.code64
mov $KERNEL_OFFSET, %rax
add %rax, %rbx
jmp *%rbx
.section .rodata
.code32
.align 16
gdt64:
.quad 0
.quad 0x00209A0000000000
.quad 0x0000920000000000
gdt_end64:
.align 16
gdtr64:
.short gdt_end64 - gdt64 - 1
.long gdt64 - KERNEL_OFFSET
+78
View File
@@ -0,0 +1,78 @@
.section .text
.global __x86_64_ctx_switch
.global __x86_64_ctx_switch_to
.global __x86_64_ctx_enter_kernel
.global __x86_64_ctx_enter_from_fork
__x86_64_ctx_enter_user:
pop %rcx
pop %rdi
pop %rdi
pop %rdx
push $0x1B
push %rcx
push $0x200
push $0x23
push %rdx
iretq
__x86_64_ctx_enter_kernel:
pop %rdi
pop %rdx
mov %rsp, %rcx
push $0x10
push %rcx
push $0x200
push $0x08
push %rdx
iretq
__x86_64_ctx_enter_from_fork:
jmp .
__x86_64_ctx_switch:
// %rsi -- src ctx ptr
// %rdi -- dst ctx ptr
push %r15
push %r14
push %r13
push %r12
push %rbx
push %rbp
mov %cr3, %rax
push %rax
// TODO save gs_base
mov (4 + TSS)(%rip), %rax
push %rax
mov %rsp, (%rsi)
__x86_64_ctx_switch_to:
mov (%rdi), %rsp
pop %rbp
pop %rbx
pop %r12
pop %r13
pop %r14
pop %r15
pop %rax
test %rax, %rax
jz 1f
mov %rax, %cr3
1:
pop %rax
mov %rax, (4 + TSS)(%rip)
// TODO set gs_base = rax
ret
+151
View File
@@ -0,0 +1,151 @@
use crate::mem::{
self,
phys::{self, PageUsage},
};
use core::mem::size_of;
use core::arch::global_asm;
struct Stack {
bp: usize,
sp: usize,
}
/// Structure representing thread context
#[repr(C)]
pub struct Context {
/// Thread's kernel stack pointer
pub k_sp: usize, // 0x00
stack_base: usize,
stack_page_count: usize,
}
impl Context {
/// Constructs a new kernel-space thread context
pub fn kernel(entry: usize, arg: usize) -> Self {
let mut stack = Stack::new(8);
stack.push(entry);
stack.push(arg);
stack.setup_common(__x86_64_ctx_enter_kernel as usize, 0, 0);
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8,
}
}
/// Constructs a new user-space thread context
pub fn user(entry: usize, arg: usize, cr3: usize, ustack: usize) -> Self {
let cr3 = cr3 & 0xFFFFFFFF;
let mut stack = Stack::new(8);
let stack_top = stack.sp;
stack.push(entry);
stack.push(arg);
stack.push(0);
stack.push(ustack);
stack.setup_common(__x86_64_ctx_enter_user as usize, cr3, stack_top);
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8,
}
}
/// Constructs an uninitialized thread context
pub fn empty() -> Self {
let stack = Stack::new(8);
Self {
k_sp: stack.sp,
stack_base: stack.bp,
stack_page_count: 8
}
}
/// Sets up a context for signal entry
///
/// # Safety
///
/// Unsafe: may clobber an already active context
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, cr3: usize, ustack: usize) {
todo!()
}
/// Performs initial thread entry
///
/// # Safety
///
/// Unsafe: does not check if any context has already been activated
/// before, so must only be called once.
pub unsafe extern "C" fn enter(&mut self) -> ! {
__x86_64_ctx_switch_to(self);
panic!("This code should not run");
}
/// Performs context switch from `self` to `to`.
///
/// # Safety
///
/// Unsafe: does not check if `self` is actually an active context.
pub unsafe extern "C" fn switch(&mut self, to: &mut Context) {
__x86_64_ctx_switch(to, self);
}
}
impl Stack {
pub fn new(page_count: usize) -> Stack {
let phys = phys::alloc_contiguous_pages(PageUsage::Kernel, page_count).unwrap();
let bp = mem::virtualize(phys);
Stack {
bp,
sp: bp + page_count * mem::PAGE_SIZE,
}
}
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
Stack {
bp,
sp: bp + page_count * mem::PAGE_SIZE
}
}
pub fn setup_common(&mut self, entry: usize, cr3: usize, tss_rsp0: usize) {
self.push(entry); // return address
self.push(tss_rsp0); // gs_base
self.push(cr3);
self.push(0); // r15
self.push(0); // r14
self.push(0); // r13
self.push(0); // r12
self.push(0); // rbx
self.push(0); // rbp
}
pub fn push(&mut self, value: usize) {
if self.bp == self.sp {
panic!("Stack overflow");
}
self.sp -= size_of::<usize>();
unsafe {
*(self.sp as *mut usize) = value;
}
}
}
extern "C" {
fn __x86_64_ctx_enter_from_fork();
fn __x86_64_ctx_enter_kernel();
fn __x86_64_ctx_enter_user();
fn __x86_64_ctx_switch(dst: *mut Context, src: *mut Context);
fn __x86_64_ctx_switch_to(dst: *mut Context);
}
global_asm!(include_str!("context.S"), options(att_syntax));
+86
View File
@@ -0,0 +1,86 @@
use crate::arch::x86_64;
use crate::debug::Level;
use crate::dev::irq::{IntController, IrqContext};
use core::arch::{asm, global_asm};
#[derive(Debug)]
struct ExceptionFrame {
r15: u64,
r14: u64,
r13: u64,
r12: u64,
r11: u64,
r10: u64,
r9: u64,
r8: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rbx: u64,
rdx: u64,
rcx: u64,
rax: u64,
err_no: u64,
err_code: u64,
rip: u64,
cs: u64,
rflags: u64,
rsp: u64,
ss: u64,
}
fn pfault_read_cr2() -> u64 {
let mut res;
unsafe {
asm!("mov %cr2, {}", out(reg) res, options(att_syntax));
}
res
}
fn pfault_access_type(code: u64) -> &'static str {
if code & (1 << 4) != 0 {
"INSTRUCTION FETCH"
} else if code & (1 << 1) != 0 {
"WRITE"
} else {
"READ"
}
}
fn pfault_dump(level: Level, frame: &ExceptionFrame, cr2: u64) {
println!(level, "\x1B[41;1mPage fault:");
println!(
level,
" Illegal {} at {:#018x}\x1B[0m",
pfault_access_type(frame.err_code),
cr2
);
}
#[no_mangle]
extern "C" fn __x86_64_exception_handler(frame: &mut ExceptionFrame) {
if frame.err_no == 14 {
// TODO userspace page faults
let cr2 = pfault_read_cr2();
pfault_dump(Level::Error, frame, cr2);
}
errorln!(
"Exception occurred: err_no={}, err_code={:#x}",
frame.err_no,
frame.err_code,
);
errorln!("cs:rip = {:02x}:{:#x}", frame.cs, frame.rip);
errorln!("ss:rsp = {:02x}:{:#x}", frame.ss, frame.rsp);
panic!("Unhandled exception");
}
#[no_mangle]
extern "C" fn __x86_64_irq_handler(frame: &mut ExceptionFrame) {
unsafe {
let ic = IrqContext::new(frame.err_no as usize);
x86_64::intc().handle_pending_irqs(&ic);
}
}
+147
View File
@@ -0,0 +1,147 @@
use core::mem::size_of_val;
use core::arch::asm;
#[repr(packed)]
struct Entry {
limit_lo: u16,
base_lo: u16,
base_mi: u8,
access: u8,
flags: u8,
base_hi: u8,
}
#[repr(packed)]
struct Tss {
__res0: u32,
rsp0: u64,
rsp1: u64,
rsp2: u64,
__res1: u32,
ist1: u64,
ist2: u64,
ist3: u64,
ist4: u64,
ist5: u64,
ist6: u64,
ist7: u64,
__res2: u64,
__res3: u16,
iopb_base: u16,
}
#[repr(packed)]
struct Pointer {
size: u16,
offset: usize,
}
impl Entry {
const FLAG_LONG: u8 = 1 << 5;
const ACC_PRESENT: u8 = 1 << 7;
const ACC_SYSTEM: u8 = 1 << 4;
const ACC_EXECUTE: u8 = 1 << 3;
const ACC_WRITE: u8 = 1 << 1;
const ACC_RING3: u8 = 3 << 5;
const ACC_ACCESS: u8 = 1 << 0;
const fn new(base: u32, limit: u32, flags: u8, access: u8) -> Self {
Self {
base_lo: (base & 0xFFFF) as u16,
base_mi: ((base >> 16) & 0xFF) as u8,
base_hi: ((base >> 24) & 0xFF) as u8,
access,
flags: (flags & 0xF0) | (((limit >> 16) & 0xF) as u8),
limit_lo: (limit & 0xFFFF) as u16,
}
}
const fn null() -> Self {
Self {
base_lo: 0,
base_mi: 0,
base_hi: 0,
access: 0,
flags: 0,
limit_lo: 0,
}
}
}
impl Tss {
const fn new() -> Self {
Self {
__res0: 0,
rsp0: 0,
rsp1: 0,
rsp2: 0,
__res1: 0,
ist1: 0,
ist2: 0,
ist3: 0,
ist4: 0,
ist5: 0,
ist6: 0,
ist7: 0,
__res2: 0,
__res3: 0,
iopb_base: 0,
}
}
}
const SIZE: usize = 7;
#[no_mangle]
static mut TSS: Tss = Tss::new();
static mut GDT: [Entry; SIZE] = [
Entry::null(),
Entry::new(
0,
0,
Entry::FLAG_LONG,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE,
),
Entry::new(
0,
0,
0,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_WRITE,
),
Entry::new(
0,
0,
0,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_WRITE,
),
Entry::new(
0,
0,
Entry::FLAG_LONG,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_RING3 | Entry::ACC_EXECUTE,
),
Entry::null(),
Entry::null(),
];
pub unsafe fn init() {
let tss_addr = &TSS as *const _ as usize;
GDT[5] = Entry::new(
(tss_addr & 0xFFFFFFFF) as u32,
size_of_val(&TSS) as u32 - 1,
Entry::FLAG_LONG,
Entry::ACC_ACCESS | Entry::ACC_PRESENT | Entry::ACC_EXECUTE,
);
core::ptr::write(&mut GDT[6] as *mut _ as *mut u64, (tss_addr >> 32) as u64);
let gdtr = Pointer {
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));
}
+110
View File
@@ -0,0 +1,110 @@
.macro isr_nerr, n
exc_isr_\n:
cli
pushq $0
pushq $\n
jmp __x86_64_isr_common
.endm
// ISR for exception with an error code
.macro isr_yerr, n
exc_isr_\n:
cli
pushq $\n
jmp __x86_64_isr_common
.endm
.section .text
__x86_64_isr_common:
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
mov %rsp, %rdi
call __x86_64_exception_handler
1:
cli
hlt
jmp 1b
isr_nerr 0
isr_nerr 1
isr_nerr 2
isr_nerr 3
isr_nerr 4
isr_nerr 5
isr_nerr 6
isr_nerr 7
isr_yerr 8
isr_nerr 9
isr_yerr 10
isr_yerr 11
isr_yerr 12
isr_yerr 13
isr_yerr 14
isr_nerr 15
isr_nerr 16
isr_yerr 17
isr_nerr 18
isr_nerr 19
isr_nerr 20
isr_nerr 21
isr_nerr 22
isr_nerr 23
isr_nerr 24
isr_nerr 25
isr_nerr 26
isr_nerr 27
isr_nerr 28
isr_nerr 29
isr_yerr 30
isr_nerr 31
.section .rodata
.global __x86_64_exception_vectors
__x86_64_exception_vectors:
.quad exc_isr_0
.quad exc_isr_1
.quad exc_isr_2
.quad exc_isr_3
.quad exc_isr_4
.quad exc_isr_5
.quad exc_isr_6
.quad exc_isr_7
.quad exc_isr_8
.quad exc_isr_9
.quad exc_isr_10
.quad exc_isr_11
.quad exc_isr_12
.quad exc_isr_13
.quad exc_isr_14
.quad exc_isr_15
.quad exc_isr_16
.quad exc_isr_17
.quad exc_isr_18
.quad exc_isr_19
.quad exc_isr_20
.quad exc_isr_21
.quad exc_isr_22
.quad exc_isr_23
.quad exc_isr_24
.quad exc_isr_25
.quad exc_isr_26
.quad exc_isr_27
.quad exc_isr_28
.quad exc_isr_29
.quad exc_isr_30
.quad exc_isr_31
+73
View File
@@ -0,0 +1,73 @@
use core::arch::{asm, global_asm};
use core::mem::size_of_val;
#[derive(Clone, Copy)]
#[repr(packed)]
pub struct Entry {
base_lo: u16,
selector: u16,
__res0: u8,
flags: u8,
base_hi: u16,
base_ex: u32,
__res1: u32,
}
#[repr(packed)]
struct Pointer {
limit: u16,
offset: usize,
}
pub const SIZE: usize = 256;
impl Entry {
pub const PRESENT: u8 = 1 << 7;
pub const INT32: u8 = 0xE;
pub const fn new(base: usize, selector: u16, flags: u8) -> Self {
Self {
base_lo: (base & 0xFFFF) as u16,
base_hi: ((base >> 16) & 0xFFFF) as u16,
base_ex: (base >> 32) as u32,
selector,
flags,
__res0: 0,
__res1: 0,
}
}
const fn empty() -> Self {
Self {
base_lo: 0,
base_hi: 0,
base_ex: 0,
selector: 0,
flags: 0,
__res0: 0,
__res1: 0,
}
}
}
static mut IDT: [Entry; SIZE] = [Entry::empty(); SIZE];
pub unsafe fn init<F: FnOnce(&mut [Entry; SIZE]) -> ()>(f: F) {
extern "C" {
static __x86_64_exception_vectors: [usize; 32];
}
for (i, &entry) in __x86_64_exception_vectors.iter().enumerate() {
IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32);
}
f(&mut IDT);
let idtr = Pointer {
limit: size_of_val(&IDT) as u16 - 1,
offset: &IDT as *const _ as usize,
};
asm!("lidt ({})", in(reg) &idtr, options(att_syntax));
}
global_asm!(include_str!("idt.S"), options(att_syntax));
+145
View File
@@ -0,0 +1,145 @@
use crate::arch::x86_64::{
idt::{Entry as IdtEntry, SIZE as IDT_SIZE},
PortIo,
};
use crate::dev::{
irq::{IntController, IntSource, IrqContext},
Device,
};
use crate::sync::IrqSafeSpinLock;
use libsys::error::Errno;
use core::arch::global_asm;
const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
const ICW4_8086: u8 = 0x01;
pub(super) struct I8259 {
cmd_a: PortIo<u8>,
cmd_b: PortIo<u8>,
data_a: PortIo<u8>,
data_b: PortIo<u8>,
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; 15]>,
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct IrqNumber(u32);
impl IrqNumber {
pub const MAX: u32 = 16;
pub const fn new(u: u32) -> Self {
if u > Self::MAX {
panic!();
}
Self(u)
}
}
impl Device for I8259 {
fn name(&self) -> &'static str {
"i8259-compatible IRQ controller"
}
unsafe fn enable(&self) -> Result<(), Errno> {
self.cmd_a.write(ICW1_INIT | ICW1_ICW4);
self.cmd_b.write(ICW1_INIT | ICW1_ICW4);
self.data_a.write(32);
self.data_b.write(32 + 8);
self.data_a.write(4);
self.data_b.write(2);
self.data_a.write(ICW4_8086);
self.data_b.write(ICW4_8086);
self.data_a.write(0xFE);
self.data_b.write(0xFF);
Ok(())
}
}
impl IntController for I8259 {
type IrqNumber = IrqNumber;
fn register_handler(
&self,
irq: Self::IrqNumber,
handler: &'static (dyn IntSource + Sync),
) -> Result<(), Errno> {
if irq.0 == 0 {
return Err(Errno::InvalidArgument);
}
let index = (irq.0 - 1) as usize;
let mut lock = self.table.lock();
if lock[index].is_some() {
return Err(Errno::AlreadyExists);
}
lock[index] = Some(handler);
Ok(())
}
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno> {
let port = if irq.0 < 8 {
&self.data_a
} else {
&self.data_b
};
let mask = port.read() & !(1 << (irq.0 & 0x7));
port.write(mask);
Ok(())
}
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
let irq_number = ic.token();
assert!(irq_number > 0);
if irq_number > 8 {
self.cmd_b.write(0x20);
}
self.cmd_a.write(0x20);
{
let table = self.table.lock();
match table[irq_number - 1] {
None => panic!("No handler registered for irq{}", irq_number),
Some(handler) => {
drop(table);
handler.handle_irq().expect("irq handler failed")
}
}
}
}
}
impl I8259 {
pub const fn new() -> Self {
unsafe {
Self {
cmd_a: PortIo::new(0x20),
data_a: PortIo::new(0x21),
cmd_b: PortIo::new(0xA0),
data_b: PortIo::new(0xA1),
table: IrqSafeSpinLock::new([None; 15]),
}
}
}
}
pub fn map_isr_entries(entries: &mut [IdtEntry; IDT_SIZE]) {
extern "C" {
static __x86_64_irq_vectors: [usize; 16];
}
for (i, &entry) in unsafe { __x86_64_irq_vectors.iter().enumerate() } {
entries[i + 32] = IdtEntry::new(entry, 0x08, IdtEntry::PRESENT | IdtEntry::INT32);
}
}
global_asm!(include_str!("irq_vectors.S"), options(att_syntax));
+16
View File
@@ -0,0 +1,16 @@
use core::arch::asm;
#[inline(always)]
pub unsafe fn rdmsr(a: u32) -> u64 {
let mut eax: u32;
let mut edx: u32;
asm!("rdmsr", in("ecx") a, out("eax") eax, out("edx") edx);
(eax as u64) | ((edx as u64) << 32)
}
#[inline(always)]
pub unsafe fn wrmsr(a: u32, b: u64) {
let eax = b as u32;
let edx = (b >> 32) as u32;
asm!("wrmsr", in("ecx") a, in("eax") eax, in("edx") edx);
}
+29
View File
@@ -0,0 +1,29 @@
use core::marker::PhantomData;
use core::arch::asm;
pub struct PortIo<T> {
port: u16,
_pd: PhantomData<T>
}
impl<T> PortIo<T> {
pub const unsafe fn new(port: u16) -> Self {
Self { port, _pd: PhantomData }
}
}
impl PortIo<u8> {
pub fn read(&self) -> u8 {
let mut res: u8;
unsafe {
asm!("inb %dx, %al", in("dx") self.port, out("al") res, options(att_syntax));
}
res
}
pub fn write(&self, value: u8) {
unsafe {
asm!("outb %al, %dx", in("dx") self.port, in("al") value, options(att_syntax));
}
}
}
+115
View File
@@ -0,0 +1,115 @@
.macro irq_entry no
__x86_64_irq_\no:
cli
pushq $0
pushq $\no
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
mov %rsp, %rdi
call __x86_64_irq_handler
jmp .
.endm
.section .text
__x86_64_irq_0:
cli
push %rax
push %rcx
push %rdx
push %rbx
push %rbp
push %rsi
push %rdi
push %r8
push %r9
push %r10
push %r11
push %r12
push %r13
push %r14
push %r15
mov $0x3F8, %dx
mov $'T', %al
outb %al, %dx
mov $0x20, %al
mov $0x20, %dx
outb %al, %dx
call sched_yield
pop %r15
pop %r14
pop %r13
pop %r12
pop %r11
pop %r10
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rbp
pop %rbx
pop %rdx
pop %rcx
pop %rax
iretq
irq_entry 1
irq_entry 2
irq_entry 3
irq_entry 4
irq_entry 5
irq_entry 6
irq_entry 7
irq_entry 8
irq_entry 9
irq_entry 10
irq_entry 11
irq_entry 12
irq_entry 13
irq_entry 14
irq_entry 15
.section .rodata
.global __x86_64_irq_vectors
__x86_64_irq_vectors:
.quad __x86_64_irq_0
.quad __x86_64_irq_1
.quad __x86_64_irq_2
.quad __x86_64_irq_3
.quad __x86_64_irq_4
.quad __x86_64_irq_5
.quad __x86_64_irq_6
.quad __x86_64_irq_7
.quad __x86_64_irq_8
.quad __x86_64_irq_9
.quad __x86_64_irq_10
.quad __x86_64_irq_11
.quad __x86_64_irq_12
.quad __x86_64_irq_13
.quad __x86_64_irq_14
.quad __x86_64_irq_15
+59
View File
@@ -0,0 +1,59 @@
use crate::dev::{serial::SerialDevice, display::StaticFramebuffer, irq::IntController};
use core::arch::asm;
mod uart;
use uart::Uart;
mod intc;
use intc::I8259;
mod io;
pub(self) use io::PortIo;
pub mod boot;
pub mod table;
pub mod context;
pub mod intrin;
pub mod reg;
pub(self) mod syscall;
pub(self) mod gdt;
pub(self) mod idt;
pub(self) mod exception;
pub use syscall::SyscallFrame as ForkFrame;
/// Masks IRQs and returns previous IRQ mask state
///
/// # Safety
///
/// Unsafe: disables IRQ handling temporarily
#[inline(always)]
pub unsafe fn irq_mask_save() -> u64 {
let mut res;
asm!("pushf; cli; pop {}", out(reg) res, options(att_syntax));
res
}
/// Restores IRQ mask state
///
/// # Safety
///
/// Unsafe: modifies interrupt behavior. Must only be used in
/// conjunction with [irq_mask_save]
#[inline(always)]
pub unsafe fn irq_restore(state: u64) {
if state & (1 << 9) != 0 {
asm!("sti");
}
}
pub fn intc() -> &'static impl IntController {
&INTC
}
pub fn console() -> &'static impl SerialDevice {
&COM1
}
static COM1: Uart = unsafe { Uart::new(0x3F8) };
static INTC: I8259 = I8259::new();
pub(self) static DISPLAY: StaticFramebuffer = StaticFramebuffer::uninit();
+135
View File
@@ -0,0 +1,135 @@
macro_rules! wrap_msr {
($struct_name:ident, $name:ident, $address:expr, $fields:tt) => {
register_bitfields! {
u64,
pub $name $fields
}
pub struct $struct_name;
impl Readable for $struct_name {
type T = u64;
type R = $name::Register;
#[inline(always)]
fn get(&self) -> Self::T {
unsafe {
rdmsr($address)
}
}
}
impl Writeable for $struct_name {
type T = u64;
type R = $name::Register;
#[inline(always)]
fn set(&self, value: Self::T) {
unsafe {
wrmsr($address, value);
}
}
}
pub const $name: $struct_name = $struct_name;
}
}
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
};
use core::arch::asm;
use crate::arch::x86_64::intrin::{rdmsr, wrmsr};
// CRn registers
register_bitfields! {
u64,
pub CR4 [
OSFXSR OFFSET(9) NUMBITS(1) [],
OSXMMEXCPT OFFSET(10) NUMBITS(1) []
]
}
register_bitfields! {
u64,
pub CR0 [
EM OFFSET(2) NUMBITS(1) [],
MP OFFSET(1) NUMBITS(1) []
]
}
pub struct Cr4;
pub struct Cr0;
impl Readable for Cr4 {
type T = u64;
type R = CR4::Register;
#[inline(always)]
fn get(&self) -> Self::T {
let mut res: u64;
unsafe {
asm!("mov %cr4, {}", out(reg) res, options(att_syntax))
}
res
}
}
impl Writeable for Cr4 {
type T = u64;
type R = CR4::Register;
#[inline(always)]
fn set(&self, value: Self::T) {
unsafe {
asm!("mov {}, %cr4", in(reg) value, options(att_syntax));
}
}
}
impl Readable for Cr0 {
type T = u64;
type R = CR0::Register;
#[inline(always)]
fn get(&self) -> Self::T {
let mut res: u64;
unsafe {
asm!("mov %cr0, {}", out(reg) res, options(att_syntax))
}
res
}
}
impl Writeable for Cr0 {
type T = u64;
type R = CR0::Register;
#[inline(always)]
fn set(&self, value: Self::T) {
unsafe {
asm!("mov {}, %cr0", in(reg) value, options(att_syntax));
}
}
}
pub const CR4: Cr4 = Cr4;
pub const CR0: Cr0 = Cr0;
wrap_msr!(MsrIa32Efer, MSR_IA32_EFER, 0xC0000080, [
SCE OFFSET(0) NUMBITS(1) [],
LME OFFSET(8) NUMBITS(1) [],
LMA OFFSET(10) NUMBITS(1) [],
NXE OFFSET(11) NUMBITS(1) []
]);
wrap_msr!(MsrIa32Lstar, MSR_IA32_LSTAR, 0xC0000082, [
VALUE OFFSET(0) NUMBITS(64) []
]);
wrap_msr!(MsrIa32Star, MSR_IA32_STAR, 0xC0000081, [
SYSCALL_CS_SS OFFSET(32) NUMBITS(8) [],
SYSRET_CS_SS OFFSET(48) NUMBITS(8) []
]);
wrap_msr!(MsrIa32Sfmask, MSR_IA32_SFMASK, 0xC0000084, [
IF OFFSET(9) NUMBITS(1) []
]);
@@ -0,0 +1,13 @@
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields,
};
use crate::arch::x86_64::intrin::{rdmsr, wrmsr};
register_bitfields! {
u64,
pub MSR_IA32_EFER [
]
}
wrap_msr!(MSR_IA32_EFER, 0xC0000080);
+60
View File
@@ -0,0 +1,60 @@
.section .text
.global __x86_64_syscall_entry
__x86_64_syscall_entry:
// Syscalls only happen from user space, so
// relying on TSS.RSP0 is safe here I guess
mov %rsp, scratch(%rip)
mov (4 + TSS)(%rip), %rsp
// Now on kernel stack
// Push the whole state
push %rcx // saved %rip
push %r11 // saved %rflags
mov scratch(%rip), %r11
push %r11 // saved %rsp
push %r12
push %r13
push %r14
push %r15
push %rbp
push %rbx
push %rax
push %r9
push %r8
push %r10
push %rdx
push %rsi
push %rdi
mov %rsp, %rdi
call __x86_64_syscall
pop %rdi
pop %rsi
pop %rdx
pop %r10
pop %r8
pop %r9
pop %rax
pop %rbx
pop %rbp
pop %r15
pop %r14
pop %r13
pop %r12
pop %rdi
pop %r11
pop %rcx
mov %rdi, %rsp
sysretq
.section .bss
scratch:
.skip 8
+53
View File
@@ -0,0 +1,53 @@
use crate::arch::x86_64::reg::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR};
use core::arch::global_asm;
use tock_registers::interfaces::{ReadWriteable, Writeable};
use libsys::abi::SystemCall;
use crate::syscall;
#[derive(Clone, Debug)]
pub struct SyscallFrame {
x: [usize; 13],
saved_rsp: usize,
saved_rflags: usize,
saved_rip: usize,
}
pub(super) fn init() {
extern "C" {
fn __x86_64_syscall_entry();
}
MSR_IA32_SFMASK.write(MSR_IA32_SFMASK::IF::SET);
MSR_IA32_LSTAR.set(__x86_64_syscall_entry 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);
}
#[no_mangle]
extern "C" fn __x86_64_syscall(frame: &mut SyscallFrame) {
let num = SystemCall::from_repr(frame.x[6]);
if num.is_none() {
todo!();
}
let num = num.unwrap();
if num == SystemCall::Fork {
match unsafe { syscall::sys_fork(frame) } {
Ok(pid) => frame.x[6] = u32::from(pid) as usize,
Err(err) => {
frame.x[6] = err.to_negative_isize() as usize;
}
}
return;
}
match syscall::syscall(num, &frame.x[..6]) {
Ok(val) => frame.x[6] = val,
Err(err) => {
frame.x[6] = err.to_negative_isize() as usize;
}
}
}
global_asm!(include_str!("syscall.S"), options(att_syntax));
+286
View File
@@ -0,0 +1,286 @@
use crate::mem::{
self,
virt::{AddressSpace, table::{MapAttributes, Entry as AbstractEntry}},
phys::{self, PageUsage}
};
use core::ops::{Index, IndexMut};
use libsys::error::Errno;
use core::arch::asm;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Entry(u64);
#[derive(Clone, Copy)]
#[repr(C, align(0x1000))]
pub struct Table {
entries: [Entry; 512]
}
#[repr(C, align(0x1000))]
pub struct FixedTableGroup {
pml4: Table,
pdpt: Table,
pd: [Table; 16]
}
#[repr(transparent)]
pub struct Space(Table);
bitflags! {
/// Attributes attached to each translation [Entry]
pub struct RawAttributes: u64 {
const PRESENT = 1 << 0;
const WRITE = 1 << 1;
const USER = 1 << 2;
const BLOCK = 1 << 7;
const GLOBAL = 1 << 8;
}
}
// Upper mappings
#[no_mangle]
static mut KERNEL_FIXED: FixedTableGroup = FixedTableGroup {
pml4: Table::empty(),
pdpt: Table::empty(),
pd: [Table::empty(); 16]
};
impl TryFrom<MapAttributes> for RawAttributes {
type Error = Errno;
fn try_from(i: MapAttributes) -> Result<Self, Errno> {
let mut res = RawAttributes::empty();
if i.contains(MapAttributes::USER_READ) {
res |= RawAttributes::USER;
}
if i.contains(MapAttributes::USER_WRITE) || i.contains(MapAttributes::KERNEL_WRITE) {
res |= RawAttributes::WRITE;
}
Ok(res)
}
}
impl AbstractEntry for Entry {
fn from_parts(phys: usize, attrs: MapAttributes) -> Self {
let attrs = RawAttributes::try_from(attrs).unwrap();
Self((phys as u64) | attrs.bits() | 1)
}
fn is_present(self) -> bool {
self.0 & (1 << 0) != 0
}
fn is_table(self) -> bool {
self.0 & (1 << 7) == 0
}
fn target(self) -> usize {
(self.0 & !0xFFF) as usize
}
}
impl Entry {
const fn invalid() -> Self {
Self(0)
}
}
impl Table {
const fn empty() -> Self {
Self {
entries: [Entry::invalid(); 512]
}
}
/// 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.target()) as *mut _) })
} else {
let phys = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
self[index] = Entry::from_parts(phys, MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NOT_GLOBAL);
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.target()) as *mut _) })
} else {
None
}
}
}
impl Space {
const fn empty() -> Self {
Self(Table::empty())
}
}
impl AddressSpace for Space {
type Entry = Entry;
fn alloc_empty() -> Result<&'static mut Self, Errno> {
let pdpt_phys = unsafe {
&KERNEL_FIXED.pdpt as *const _ as usize - mem::KERNEL_OFFSET
};
let page = phys::alloc_page(PageUsage::Paging)?;
let res = unsafe { &mut *(mem::virtualize(page) as *mut Self) };
res.0.entries[..511].fill(Entry::invalid());
res.0.entries[511] = Entry::from_parts(pdpt_phys, MapAttributes::SHARE_OUTER | MapAttributes::KERNEL_EXEC | MapAttributes::KERNEL_WRITE | MapAttributes::NOT_GLOBAL);
Ok(res)
}
fn release(space: &mut Self) {
todo!()
}
fn address_phys(&mut self) -> usize {
todo!();
}
fn read_last_level_entry(&mut self, virt: usize) -> Result<Entry, Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table(l0i).ok_or(Errno::DoesNotExist)?;
let l1_table = l0_table.next_level_table(l1i).ok_or(Errno::DoesNotExist)?;
let l2_table = l1_table.next_level_table(l2i).ok_or(Errno::DoesNotExist)?;
let entry = l2_table[l3i];
if entry.is_present() {
Ok(entry)
} else {
Err(Errno::DoesNotExist)
}
}
fn write_last_level_entry(
&mut self,
virt: usize,
entry: Entry,
map_intermediate: bool,
) -> Result<(), Errno> {
let l0i = virt >> 39;
let l1i = (virt >> 30) & 0x1FF;
let l2i = (virt >> 21) & 0x1FF;
let l3i = (virt >> 12) & 0x1FF;
let l0_table = self.0.next_level_table_or_alloc(l0i)?;
let l1_table = l0_table.next_level_table_or_alloc(l1i)?;
let l2_table = l1_table.next_level_table_or_alloc(l2i)?;
if l2_table[l3i].is_present() {
warnln!("Entry already exists for address: virt={:#x}, prev={:#x}, new={:#x}", virt, l2_table[l3i].target(), entry.target());
Err(Errno::AlreadyExists)
} else {
l2_table[l3i] = entry;
unsafe {
core::arch::asm!("invlpg ({})", in(reg) virt, options(att_syntax));
}
#[cfg(feature = "verbose")]
debugln!("{:#p} Map {:#x} -> {:#x}", self, virt, entry.target());
Ok(())
}
}
/// Performs a copy of the address space, cloning data owned by it
fn fork(&mut self) -> Result<&'static mut Self, Errno> {
let res = Self::alloc_empty()?;
let pdpt0 = self.0.next_level_table(0).unwrap();
for pdpti in 0..512 {
if let Some(pd) = pdpt0.next_level_table(pdpti) {
for pdi in 0..512 {
if let Some(pt) = pd.next_level_table(pdi) {
for pti in 0..512 {
let entry = pt[pti];
if !entry.is_present() {
continue;
}
assert!(entry.is_table());
todo!();
// 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)
}
}
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]
}
}
pub fn enable() -> Result<(), Errno> {
unsafe {
// Remove the lower mapping
KERNEL_FIXED.pml4.entries[0] = Entry::invalid();
// Flush the TLB by reloading cr3
asm!("mov %cr3, %rax; mov %rax, %cr3", options(att_syntax));
}
Ok(())
}
+50
View File
@@ -0,0 +1,50 @@
use crate::arch::x86_64::PortIo;
use libsys::error::Errno;
use crate::dev::{
tty::{CharRing, TtyDevice},
irq::{IntController, IntSource},
serial::SerialDevice,
Device,
};
#[derive(TtyCharDevice)]
pub(super) struct Uart {
dr: PortIo<u8>,
ring: CharRing<16>
}
impl Device for Uart {
fn name(&self) -> &'static str {
"x86 COM-port"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl TtyDevice<16> for Uart {
fn ring(&self) -> &CharRing<16> {
&self.ring
}
}
impl SerialDevice for Uart {
fn send(&self, byte: u8) -> Result<(), Errno> {
self.dr.write(byte);
Ok(())
}
fn recv(&self, _blocking: bool) -> Result<u8, Errno> {
todo!()
}
}
impl Uart {
pub const unsafe fn new(base: u16) -> Self {
Self {
dr: PortIo::new(base),
ring: CharRing::new()
}
}
}
+191 -12
View File
@@ -11,21 +11,96 @@
//! * [warnln!]
//! * [errorln!]
use crate::dev::serial::SerialDevice;
use libsys::debug::TraceLevel;
use crate::dev::{
display::{Display, FramebufferInfo},
serial::SerialDevice,
};
use crate::font;
use crate::sync::IrqSafeSpinLock;
use core::convert::TryFrom;
use core::fmt;
use libsys::{debug::TraceLevel, error::Errno, mem::memcpy};
pub static LEVEL: Level = Level::Debug;
static COLOR_MAP: [u32; 16] = [
0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xAA5500, 0xAAAAAA, 0x555555,
0x5555FF, 0x55FF55, 0x55FFFF, 0xFF5555, 0xFF55FF, 0xFFFF55, 0xFFFFFF,
];
static ATTR_MAP: [usize; 10] = [0, 4, 2, 6, 1, 5, 3, 7, 7, 7];
static DISPLAY: IrqSafeSpinLock<FramebufferOutput> = IrqSafeSpinLock::new(FramebufferOutput {
display: None,
col: 0,
row: 0,
fg: 0xBBBBBB,
bg: 0x000000,
esc: EscapeState::None,
esc_argv: [0; 8],
esc_argc: 0,
});
enum EscapeState {
None,
Esc,
Data,
}
struct FramebufferOutput {
display: Option<&'static dyn Display>,
row: usize,
col: usize,
fg: u32,
bg: u32,
esc: EscapeState,
esc_argv: [usize; 8],
esc_argc: usize,
}
impl fmt::Write for FramebufferOutput {
fn write_str(&mut self, s: &str) -> fmt::Result {
if self.display.is_none() {
return Ok(());
}
let fb = self.display.unwrap().framebuffer().unwrap();
for ch in s.chars() {
self.putc(&fb, ch);
}
Ok(())
}
}
pub fn set_display(disp: &'static dyn Display) {
DISPLAY.lock().display = Some(disp);
}
/// Kernel logging levels
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(u32)]
pub enum Level {
/// Debugging information
Debug,
Debug = 1,
/// General informational messages
Info,
Info = 2,
/// Non-critical warnings
Warn,
Warn = 3,
/// Critical errors
Error,
Error = 4,
}
impl TryFrom<u32> for Level {
type Error = Errno;
#[inline(always)]
fn try_from(l: u32) -> Result<Level, Errno> {
match l {
1 => Ok(Level::Debug),
2 => Ok(Level::Info),
3 => Ok(Level::Warn),
4 => Ok(Level::Error),
_ => Err(Errno::InvalidArgument),
}
}
}
impl From<TraceLevel> for Level {
@@ -114,13 +189,117 @@ macro_rules! errorln {
}
#[doc(hidden)]
pub fn _debug(_level: Level, args: fmt::Arguments) {
pub fn _debug(level: Level, args: fmt::Arguments) {
use crate::arch::machine;
use fmt::Write;
SerialOutput {
inner: machine::console(),
if level > Level::Debug {
DISPLAY.lock().write_fmt(args).ok();
}
if level >= LEVEL {
SerialOutput {
inner: machine::console(),
}
.write_fmt(args)
.ok();
}
}
impl FramebufferOutput {
const CW: usize = 8;
const CH: usize = 12;
pub fn set_char(&mut self, fb: &FramebufferInfo, x: usize, y: usize, ch: char) {
if (x + 1) * Self::CW >= fb.width || (y + 1) * Self::CH >= fb.height {
return;
}
font::get().draw(fb, x * Self::CW, y * Self::CH, ch, self.fg, self.bg);
}
pub fn scroll(&mut self, fb: &FramebufferInfo) {
let stride = 4 * Self::CH * fb.width;
let h = fb.height / Self::CH - 1;
if self.row == h {
for y in 0..(h - 1) {
unsafe {
memcpy(
(fb.virt_base + stride * y) as *mut u8,
(fb.virt_base + (y + 1) * stride) as *const u8,
stride,
);
}
}
self.row = h - 1;
}
}
pub fn putc(&mut self, fb: &FramebufferInfo, ch: char) {
match self.esc {
EscapeState::None => {
match ch {
'\x1B' => {
self.esc = EscapeState::Esc;
self.esc_argv.fill(0);
self.esc_argc = 0;
}
' '..='\x7E' => {
self.set_char(fb, self.col, self.row, ch);
// Advance the cursor
self.col += 1;
if (self.col + 1) * Self::CW >= fb.width {
self.col = 0;
self.row += 1;
}
}
'\n' => {
self.col = 0;
self.row += 1;
}
_ => {}
}
}
EscapeState::Esc => match ch {
'[' => {
self.esc = EscapeState::Data;
}
_ => {
self.esc = EscapeState::None;
}
},
EscapeState::Data => {
match ch {
'0'..='9' => {
self.esc_argv[self.esc_argc] *= 10;
self.esc_argv[self.esc_argc] += (ch as u8 - b'0') as usize;
}
';' => {
self.esc_argc += 1;
}
_ => {
self.esc_argc += 1;
self.esc = EscapeState::None;
}
}
match ch {
'm' => {
for i in 0..self.esc_argc {
let item = self.esc_argv[i];
if item / 10 == 4 {
self.bg = COLOR_MAP[ATTR_MAP[(item % 10) as usize]];
}
if item / 10 == 3 {
self.fg = COLOR_MAP[ATTR_MAP[(item % 10) as usize]];
}
}
}
_ => {}
}
}
};
self.scroll(fb);
}
.write_fmt(args)
.ok();
}
+58
View File
@@ -0,0 +1,58 @@
use crate::dev::Device;
use libsys::error::Errno;
use crate::util::InitOnce;
pub struct FramebufferInfo {
pub width: usize,
pub height: usize,
pub phys_base: usize,
pub virt_base: usize
}
pub trait Display: Device {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno>;
fn framebuffer<'a>(&'a self) -> Result<&'a FramebufferInfo, Errno>;
}
pub struct DisplayMode {
width: u16,
height: u16,
}
pub struct StaticFramebuffer {
framebuffer: InitOnce<FramebufferInfo>
}
impl Device for StaticFramebuffer {
fn name(&self) -> &'static str {
"Generic framebuffer device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl Display for StaticFramebuffer {
fn set_mode(&self, mode: DisplayMode) -> Result<(), Errno> {
Err(Errno::InvalidOperation)
}
fn framebuffer(&self) -> Result<&FramebufferInfo, Errno> {
if let Some(fb) = self.framebuffer.as_ref_option() {
Ok(fb)
} else {
Err(Errno::InvalidOperation)
}
}
}
impl StaticFramebuffer {
pub const fn uninit() -> Self {
Self { framebuffer: InitOnce::new() }
}
pub fn set_framebuffer(&self, framebuffer: FramebufferInfo) {
self.framebuffer.init(framebuffer);
}
}
+7 -2
View File
@@ -5,6 +5,7 @@ use libsys::error::Errno;
/// Token to indicate the local core is running in IRQ context
pub struct IrqContext<'irq_context> {
token: usize,
_0: PhantomData<&'irq_context ()>,
}
@@ -45,7 +46,11 @@ impl<'q> IrqContext<'q> {
///
/// Only allowed to be constructed in top-level IRQ handlers
#[inline(always)]
pub unsafe fn new() -> Self {
Self { _0: PhantomData }
pub unsafe fn new(token: usize) -> Self {
Self { token, _0: PhantomData }
}
pub const fn token(&self) -> usize {
self.token
}
}
+7 -5
View File
@@ -3,14 +3,16 @@
use libsys::error::Errno;
// Device classes
pub mod fdt;
pub mod gpio;
// pub mod fdt;
// pub mod gpio;
pub mod irq;
pub mod pci;
pub mod rtc;
pub mod sd;
pub mod display;
// pub mod pci;
// pub mod rtc;
// pub mod sd;
pub mod serial;
pub mod timer;
pub mod pseudo;
pub mod tty;
/// Generic device trait
+99
View File
@@ -0,0 +1,99 @@
use crate::dev::{
serial::SerialDevice,
Device,
};
use crate::mem::virt::DeviceMemoryIo;
use crate::sync::IrqSafeSpinLock;
use crate::util::InitOnce;
use libsys::{error::Errno, ioctl::IoctlCmd};
use core::sync::atomic::{AtomicU32, Ordering};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use vfs::CharDevice;
pub struct Random {
state: AtomicU32
}
pub struct Zero;
impl Device for Random {
fn name(&self) -> &'static str {
"Pseudo-random device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl CharDevice for Random {
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
for byte in data.iter_mut() {
*byte = self.read_single() as u8;
}
Ok(data.len())
}
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
Ok(0)
}
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
Ok(true)
}
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
Err(Errno::InvalidArgument)
}
}
impl Device for Zero {
fn name(&self) -> &'static str {
"Zero device"
}
unsafe fn enable(&self) -> Result<(), Errno> {
Ok(())
}
}
impl CharDevice for Zero {
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
data.fill(0);
Ok(data.len())
}
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
Ok(0)
}
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
Ok(true)
}
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
Err(Errno::InvalidArgument)
}
}
impl Random {
pub fn set_state(&self, state: u32) {
self.state.store(state, Ordering::Release);
}
pub fn read_single(&self) -> u32 {
let mut x = self.state.load(Ordering::Acquire);
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state.store(x, Ordering::Release);
x
}
}
pub static RANDOM: Random = Random { state: AtomicU32::new(0) };
pub static ZERO: Zero = Zero;
+3 -2
View File
@@ -60,7 +60,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
},
IoctlCmd::TtySetPgrp => {
let src = arg::struct_ref::<u32>(ptr)?;
self.ring().inner.lock().fg_pgid = Some(unsafe { Pid::from_raw(*src) });
self.ring().inner.lock().fg_pgid = Some(Pid::try_from(*src)?);
Ok(0)
},
_ => Err(Errno::InvalidArgument)
@@ -126,7 +126,8 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
// TODO send to pgid
let proc = Process::get(pgid);
if let Some(proc) = proc {
proc.set_signal(Signal::Interrupt);
// TODO
// proc.set_signal(Signal::Interrupt);
}
}
return;
+49
View File
@@ -0,0 +1,49 @@
use crate::util::InitOnce;
use libsys::mem::read_le32;
use crate::dev::display::FramebufferInfo;
static FONT_DATA: &[u8] = include_bytes!("../../etc/default8x16.psfu");
static FONT: InitOnce<Font> = InitOnce::new();
pub struct Font {
char_width: usize,
char_height: usize,
bytes_per_glyph: usize,
data: &'static [u8],
}
impl Font {
pub fn draw(&self, fb: &FramebufferInfo, bx: usize, by: usize, ch: char, fg: u32, bg: u32) {
if ch >= ' ' && ch < '\x7B' {
let char_data = &self.data[ch as usize * self.bytes_per_glyph..];
for iy in 0..self.char_height {
for ix in 0..self.char_width {
let cx = self.char_width - ix - 1;
let ptr = fb.virt_base + (ix + bx + (iy + by) * fb.width) * 4;
let value = if char_data[iy + (cx) / 8] & (1 << (cx & 0x7)) != 0 {
fg
} else {
bg
};
unsafe { core::ptr::write_volatile(ptr as *mut u32, value) }
}
}
}
}
}
pub fn init() {
assert_eq!(read_le32(&FONT_DATA[..]), 0x864ab572);
FONT.init(Font {
char_width: read_le32(&FONT_DATA[28..]) as usize,
char_height: read_le32(&FONT_DATA[24..]) as usize,
bytes_per_glyph: read_le32(&FONT_DATA[20..]) as usize,
data: &FONT_DATA[32..]
});
}
pub fn get() -> &'static Font {
FONT.get()
}
+2 -2
View File
@@ -26,7 +26,7 @@ pub fn root() -> &'static VnodeRef {
DEVFS_ROOT.get()
}
fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
infoln!("Add char device: {}", name);
let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
@@ -56,5 +56,5 @@ pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Re
let name = core::str::from_utf8(&buf[..=prefix.len()]).map_err(|_| Errno::InvalidArgument)?;
_add_char_device(dev, name)
add_named_char_device(dev, name)
}
+5 -4
View File
@@ -8,6 +8,7 @@ use vfs::VnodeRef;
use memfs::BlockAllocator;
pub mod devfs;
pub mod sysfs;
/// Allocator implementation for memfs
#[derive(Clone, Copy)]
@@ -32,9 +33,9 @@ unsafe impl BlockAllocator for MemfsBlockAlloc {
pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Errno> {
let fs_name = options.fs.unwrap();
if fs_name == "devfs" {
Ok(devfs::root().clone())
} else {
todo!();
match fs_name {
"devfs" => Ok(devfs::root().clone()),
"sysfs" => Ok(sysfs::root().clone()),
_ => todo!()
}
}
+169
View File
@@ -0,0 +1,169 @@
use crate::util::InitOnce;
use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, Ordering};
use fs_macros::auto_inode;
use libsys::{
error::Errno,
stat::{FileMode, OpenFlags, Stat},
};
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef};
use core::fmt::{self, Write};
use core::str::FromStr;
use crate::debug::{self, Level};
struct NodeData<
R: Fn(&mut [u8]) -> Result<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> {
read_func: R,
write_func: W,
}
struct BufferWriter<'a> {
dst: &'a mut [u8],
pos: usize
}
impl<'a> fmt::Write for BufferWriter<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.bytes() {
if self.pos == self.dst.len() {
todo!();
}
self.dst[self.pos] = byte;
self.pos += 1;
}
Ok(())
}
}
impl<'a> BufferWriter<'a> {
pub const fn new(dst: &'a mut [u8]) -> Self {
Self { dst, pos: 0 }
}
pub const fn count(&self) -> usize {
self.pos
}
}
#[auto_inode]
impl<
R: Fn(&mut [u8]) -> Result<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> VnodeImpl for NodeData<R, W>
{
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
Ok(0)
}
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Ok(())
}
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
if pos != 0 {
// TODO handle this
Ok(0)
} else {
(self.read_func)(data)
}
}
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
if pos != 0 {
todo!();
}
(self.write_func)(data)
}
}
impl<
R: Fn(&mut [u8]) -> Result<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> NodeData<R, W>
{
pub const fn new(read_func: R, write_func: W) -> Self {
Self {
read_func,
write_func,
}
}
}
static SYSFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
// TODO subdirs
fn add_generic_node<R, W>(parent: Option<VnodeRef>, name: &str, mode: FileMode, read: R, write: W)
where
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
{
let node = Vnode::new(name, VnodeKind::Regular, Vnode::CACHE_STAT);
node.props_mut().mode = mode | FileMode::S_IFREG;
node.set_data(Box::new(NodeData::new(read, write)));
if let Some(parent) = parent {
parent.attach(node);
} else {
SYSFS_ROOT.get().attach(node);
}
}
pub fn add_read_write_node<R, W>(parent: Option<VnodeRef>, name: &str, read: R, write: W)
where
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
{
add_generic_node(parent, name, FileMode::from_bits(0o600).unwrap(), read, write)
}
pub fn add_read_node<R>(parent: Option<VnodeRef>, name: &str, read: R) where R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static {
add_generic_node(parent, name, FileMode::from_bits(0o400).unwrap(), read, |_| Err(Errno::ReadOnly))
}
pub fn add_directory(parent: Option<VnodeRef>, name: &str) -> Result<VnodeRef, Errno> {
let node = Vnode::new(name, VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
node.props_mut().mode = FileMode::from_bits(0o500).unwrap() | FileMode::S_IFDIR;
if let Some(parent) = parent {
parent.attach(node.clone());
} else {
SYSFS_ROOT.get().attach(node.clone());
}
Ok(node)
}
pub fn root() -> &'static VnodeRef {
SYSFS_ROOT.get()
}
pub fn init() {
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
node.props_mut().mode = FileMode::default_dir();
SYSFS_ROOT.init(node);
let debug_dir = add_directory(None, "debug").unwrap();
add_read_write_node(Some(debug_dir.clone()), "level", |buf| {
let mut writer = BufferWriter::new(buf);
write!(&mut writer, "{}\n", debug::LEVEL as u32).map_err(|_| Errno::InvalidArgument)?;
Ok(writer.count())
}, |buf| {
let s = core::str::from_utf8(buf).map_err(|_| Errno::InvalidArgument)?;
let value = u32::from_str(s).map_err(|_| Errno::InvalidArgument).and_then(Level::try_from)?;
todo!()
});
add_read_node(None, "uptime", |buf| {
use crate::arch::machine;
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)?;
Ok(writer.count())
});
}
+8 -5
View File
@@ -12,7 +12,9 @@
panic_info_message,
alloc_error_handler,
linked_list_cursors,
const_btree_new
const_btree_new,
core_intrinsics,
const_generics_defaults,
)]
#![no_std]
#![no_main]
@@ -28,11 +30,12 @@ extern crate alloc;
#[macro_use]
pub mod debug;
//
pub mod arch;
pub mod config;
pub mod dev;
pub mod fs;
pub mod font;
pub mod init;
pub mod mem;
pub mod proc;
@@ -42,9 +45,9 @@ pub mod util;
#[panic_handler]
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
unsafe {
asm!("msr daifset, #2");
}
// unsafe {
// asm!("msr daifset, #2");
// }
errorln!("Panic: {:?}", pi);
// TODO
+1 -1
View File
@@ -59,7 +59,7 @@ static HEAP: InitOnce<IrqSafeSpinLock<Heap>> = InitOnce::new();
pub unsafe fn init(base: usize, size: usize) {
let heap = Heap { base, size, ptr: 0 };
infoln!("Kernel heap: {:#x}..{:#x}", base, base + size);
// infoln!("Kernel heap: {:#x}..{:#x}", base, base + size);
HEAP.init(IrqSafeSpinLock::new(heap));
}
+5 -2
View File
@@ -9,7 +9,7 @@ mod manager;
mod reserved;
use manager::{Manager, SimpleManager, MANAGER};
pub use reserved::ReservedRegion;
pub use reserved::{ReservedRegion, reserve};
type ManagerImpl = SimpleManager;
@@ -213,7 +213,10 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
// Step 1. Count available memory
let mut total_pages = 0usize;
for reg in iter.clone() {
total_pages += (reg.end - reg.start) / PAGE_SIZE;
let upper = (reg.end - mem_base) / PAGE_SIZE;
if upper > total_pages {
total_pages = upper;
}
}
// TODO maybe instead of size_of::<...> use Layout?
let need_pages = ((total_pages * size_of::<PageInfo>()) + 0xFFF) / 0x1000;
+33 -30
View File
@@ -2,18 +2,18 @@
use core::marker::PhantomData;
use core::ops::Deref;
use cortex_a::asm::barrier::{self, dsb, isb};
use cortex_a::registers::TTBR0_EL1;
// use cortex_a::asm::barrier::{self, dsb, isb};
// use cortex_a::registers::TTBR0_EL1;
use libsys::error::Errno;
use tock_registers::interfaces::Writeable;
// use tock_registers::interfaces::Writeable;
//
pub mod table;
pub use table::{Entry, MapAttributes, Space, Table};
pub mod fixed;
pub use fixed::FixedTableGroup;
pub use table::{AddressSpace, Space};
#[no_mangle]
static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty();
// pub use table::{Entry, MapAttributes, Space, Table};
// pub mod fixed;
// pub use fixed::FixedTableGroup;
use crate::arch::platform::table as plat_table;
/// Structure representing a region of memory used for MMIO/device access
// TODO: this shouldn't be trivially-cloneable and should instead incorporate
@@ -45,15 +45,16 @@ impl DeviceMemory {
///
/// See [FixedTableGroup::map_region]
pub fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
let base = unsafe { KERNEL_TTBR1.map_region(phys, count) }?;
debugln!(
"Mapping {:#x}..{:#x} -> {:#x} for {:?}",
base,
base + count * 0x1000,
phys,
name
);
Ok(Self { name, base, count })
todo!();
// let base = unsafe { KERNEL_TTBR1.map_region(phys, count) }?;
// debugln!(
// "Mapping {:#x}..{:#x} -> {:#x} for {:?}",
// base,
// base + count * 0x1000,
// phys,
// name
// );
// Ok(Self { name, base, count })
}
}
@@ -90,16 +91,18 @@ impl<T> Deref for DeviceMemoryIo<T> {
/// Sets up device mapping tables and disable lower-half
/// identity-mapped translation
pub fn enable() -> Result<(), Errno> {
unsafe {
KERNEL_TTBR1.init_device_map();
dsb(barrier::ISH);
isb(barrier::SY);
}
// Disable lower-half translation
TTBR0_EL1.set(0);
//TCR_EL1.modify(TCR_EL1::EPD0::SET);
Ok(())
plat_table::enable()
}
// unsafe {
// KERNEL_TTBR1.init_device_map();
//
// dsb(barrier::ISH);
// isb(barrier::SY);
// }
//
// // Disable lower-half translation
// TTBR0_EL1.set(0);
// //TCR_EL1.modify(TCR_EL1::EPD0::SET);
//
// Ok(())
// }
+36 -375
View File
@@ -7,269 +7,62 @@ use crate::mem::{
use core::ops::{Index, IndexMut};
use libsys::{error::Errno, mem::memset};
/// Transparent wrapper structure representing a single
/// translation table entry
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Entry(u64);
use crate::arch::platform::table;
/// 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);
pub use table::{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;
const USER_READ = 1 << 1;
const USER_WRITE = 1 << 2;
const USER_EXEC = 1 << 3;
const KERNEL_WRITE = 1 << 4;
const KERNEL_EXEC = 1 << 5;
/// Pages marked with this bit are Copy-on-Write
const EX_COW = 1 << 55;
const SHARE_OUTER = 1 << 6;
const SHARE_INNER = 2 << 6;
/// 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;
const NOT_GLOBAL = 1 << 8;
}
}
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],
}
}
pub trait Entry: Clone + Copy {
fn from_parts(phys: usize, attrs: MapAttributes) -> Self;
fn target(self) -> usize;
fn is_present(self) -> bool;
fn is_table(self) -> bool;
}
impl Index<usize> for Table {
type Output = Entry;
pub trait AddressSpace {
type Entry: Entry;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
fn alloc_empty() -> Result<&'static mut Self, Errno>;
fn fork(&mut self) -> Result<&'static mut Self, Errno>;
fn release(space: &mut Self);
fn address_phys(&mut self) -> usize;
fn read_last_level_entry(&mut self, virt: usize) -> Result<Self::Entry, Errno>;
fn write_last_level_entry(
&mut self,
virt: usize,
entry: Self::Entry,
map_intermediate: bool,
) -> Result<(), Errno>;
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)
#[inline(always)]
fn map(&mut self, virt: usize, phys: usize, attrs: MapAttributes) -> Result<(), Errno> {
let entry = Entry::from_parts(phys, attrs);
self.write_last_level_entry(virt, entry, true).map(|_| ())
}
/// 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(())
#[inline(always)]
fn translate(&mut self, virt: usize) -> Result<usize, Errno> {
self.read_last_level_entry(virt).map(Entry::target)
}
/// Allocates a contiguous region from the address space and maps
/// physical pages to it
pub fn allocate(
fn allocate(
&mut self,
start: usize,
end: usize,
@@ -292,136 +85,4 @@ impl Space {
}
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
}
}
+6 -6
View File
@@ -2,7 +2,7 @@
use crate::mem::{
self,
phys::{self, PageUsage},
virt::{MapAttributes, Space},
virt::{Space, table::{AddressSpace, MapAttributes}},
};
use core::mem::{size_of, MaybeUninit};
use libsys::{
@@ -66,10 +66,10 @@ struct Phdr<E: Elf> {
}
fn map_flags(elf_flags: usize) -> MapAttributes {
let mut dst_flags = MapAttributes::NOT_GLOBAL | MapAttributes::SH_OUTER;
let mut dst_flags = MapAttributes::SHARE_OUTER | MapAttributes::NOT_GLOBAL;
if elf_flags & (1 << 0) /* PF_X */ == 0 {
dst_flags |= MapAttributes::UXN | MapAttributes::PXN;
if elf_flags & (1 << 0) /* PF_X */ != 0 {
dst_flags |= MapAttributes::USER_EXEC | MapAttributes::KERNEL_EXEC;
}
match (elf_flags & (3 << 1)) >> 1 {
@@ -78,9 +78,9 @@ fn map_flags(elf_flags: usize) -> MapAttributes {
// Write-only: not sure if such mapping should exist at all
1 => todo!(),
// Read-only
2 => dst_flags |= MapAttributes::AP_BOTH_READONLY,
2 => dst_flags |= MapAttributes::USER_READ,
// Read+Write
3 => dst_flags |= MapAttributes::AP_BOTH_READWRITE,
3 => dst_flags |= MapAttributes::USER_READ | MapAttributes::USER_WRITE | MapAttributes::KERNEL_WRITE,
_ => unreachable!(),
};
+7 -2
View File
@@ -3,7 +3,7 @@
use crate::init;
use crate::sync::IrqSafeSpinLock;
use alloc::collections::BTreeMap;
use libsys::proc::Pid;
use libsys::proc::{Tid, Pid};
pub mod elf;
pub mod thread;
@@ -27,10 +27,15 @@ pub fn switch() {
SCHED.switch(false);
}
#[no_mangle]
extern "C" fn sched_yield() {
SCHED.switch(false);
}
pub(self) static PROCESSES: IrqSafeSpinLock<BTreeMap<Pid, ProcessRef>> =
IrqSafeSpinLock::new(BTreeMap::new());
pub(self) static THREADS: IrqSafeSpinLock<BTreeMap<u32, ThreadRef>> =
pub(self) static THREADS: IrqSafeSpinLock<BTreeMap<Tid, ThreadRef>> =
IrqSafeSpinLock::new(BTreeMap::new());
/// Sets up initial process and enters it.
+194 -141
View File
@@ -1,14 +1,14 @@
//! Process data and control
use crate::arch::aarch64::exception::ExceptionFrame;
use crate::arch::platform::ForkFrame;
use crate::mem::{
self,
phys::{self, PageUsage},
virt::{MapAttributes, Space},
virt::{table::{MapAttributes, AddressSpace}, Space},
};
use crate::proc::{
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED,
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED, Tid,
};
use crate::sync::IrqSafeSpinLock;
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
use alloc::{rc::Rc, vec::Vec};
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{
@@ -18,6 +18,7 @@ use libsys::{
signal::Signal,
ProgramArgs,
};
use core::arch::asm;
/// Wrapper type for a process struct reference
pub type ProcessRef = Rc<Process>;
@@ -39,7 +40,7 @@ struct ProcessInner {
ppid: Option<Pid>,
sid: Pid,
exit: Option<ExitCode>,
threads: Vec<u32>,
threads: Vec<Tid>,
}
/// Structure describing an operating system process
@@ -54,7 +55,7 @@ pub struct Process {
impl Process {
const USTACK_VIRT_TOP: usize = 0x100000000;
const USTACK_PAGES: usize = 4;
const USTACK_PAGES: usize = 8;
/// Returns the process ID
#[inline]
@@ -146,168 +147,209 @@ impl Process {
PROCESSES.lock().get(&pid).cloned()
}
/// Sets a pending signal for a process
pub fn set_signal(&self, signal: Signal) {
let mut lock = self.inner.lock();
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
let main_thread = Thread::get(lock.threads[0]).unwrap();
drop(lock);
// TODO check that `signal` is not a fault signal
// it is illegal to call this function with
// fault signals
match main_thread.state() {
ThreadState::Running => {
main_thread.enter_signal(signal, ttbr0);
}
ThreadState::Waiting => {
main_thread.clone().setup_signal(signal, ttbr0);
main_thread.interrupt_wait(true);
}
ThreadState::Ready => {
main_thread.clone().setup_signal(signal, ttbr0);
main_thread.interrupt_wait(false);
}
ThreadState::Finished => {
// TODO report error back
todo!()
fn find1(a: u32) -> Option<usize> {
for i in 0..32 {
if a & (1 << i) != 0 {
return Some(i);
}
}
None
}
/// Immediately delivers a signal to requested thread
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
let mut lock = self.inner.lock();
let ttbr0 = lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
thread.enter_signal(signal, ttbr0);
fn space_phys(lock: &mut IrqSafeSpinLockGuard<ProcessInner>) -> usize {
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48)
}
/// Crates a new thread in the process
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<u32, Errno> {
let mut lock = self.inner.lock();
// /// Handles all pending signals (when returning from aborted syscall)
// pub fn handle_pending_signals(&self) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let main_thread = Thread::get(lock.threads[0]).unwrap();
// drop(lock);
let space_phys = lock.space.as_mut().unwrap().address_phys();
let ttbr0 = space_phys | ((lock.id.asid() as usize) << 48);
// loop {
// let state = self.signal_state.load(Ordering::Acquire);
// if let Some(signal) = Self::find1(state).map(|e| Signal::try_from(e as u32).unwrap()) {
// self.signal_state.fetch_and(!(1 << (signal as u32)), Ordering::Release);
// main_thread.clone().enter_signal(signal, table);
// } else {
// break;
// }
// }
// }
let thread = Thread::new_user(lock.id, entry, stack, arg, ttbr0)?;
let tid = thread.id();
lock.threads.push(tid);
SCHED.enqueue(tid);
// /// Sets a pending signal for a process
// pub fn set_signal(&self, signal: Signal) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let main_thread = Thread::get(lock.threads[0]).unwrap();
// drop(lock);
Ok(tid)
}
// // TODO check that `signal` is not a fault signal
// // it is illegal to call this function with
// // fault signals
// match main_thread.state() {
// ThreadState::Running => {
// main_thread.enter_signal(signal, table);
// }
// ThreadState::Waiting => {
// self.signal_state.fetch_or(1 << (signal as u32), Ordering::Release);
// main_thread.interrupt_wait(true);
// }
// ThreadState::Ready => {
// main_thread.clone().setup_signal(signal, table);
// main_thread.interrupt_wait(false);
// }
// ThreadState::Finished => {
// // TODO report error back
// todo!()
// }
// }
// }
// /// Immediately delivers a signal to requested thread
// pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// drop(lock);
// thread.enter_signal(signal, table);
// }
// /// Crates a new thread in the process
// pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
// let mut lock = self.inner.lock();
// let table = Self::space_phys(&lock);
// let thread = Thread::new_user(lock.id, entry, stack, arg, table)?;
// let tid = thread.id();
// lock.threads.push(tid);
// SCHED.enqueue(tid);
// Ok(tid)
// }
/// Creates a "fork" of the process, cloning its address space and
/// resources
pub fn fork(&self, frame: &mut ExceptionFrame) -> Result<Pid, Errno> {
pub fn fork(&self, frame: &mut ForkFrame) -> Result<Pid, Errno> {
todo!();
let src_io = self.io.lock();
let mut src_inner = self.inner.lock();
let dst_id = new_user_pid();
let dst_space = src_inner.space.as_mut().unwrap().fork()?;
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
let mut threads = Vec::new();
let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
threads.push(tid);
todo!()
let dst = Rc::new(Self {
exit_wait: Wait::new("process_exit"),
io: IrqSafeSpinLock::new(src_io.fork()?),
signal_state: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(ProcessInner {
threads,
exit: None,
space: Some(dst_space),
state: ProcessState::Active,
id: dst_id,
pgid: src_inner.pgid,
ppid: Some(src_inner.id),
sid: src_inner.sid,
}),
});
// let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
// let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
// let mut threads = Vec::new();
// let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
// threads.push(tid);
SCHED.enqueue(tid);
// let dst = Rc::new(Self {
// exit_wait: Wait::new("process_exit"),
// io: IrqSafeSpinLock::new(src_io.fork()?),
// signal_state: AtomicU32::new(0),
// inner: IrqSafeSpinLock::new(ProcessInner {
// threads,
// exit: None,
// space: Some(dst_space),
// state: ProcessState::Active,
// id: dst_id,
// pgid: src_inner.pgid,
// ppid: Some(src_inner.id),
// sid: src_inner.sid,
// }),
// });
Ok(dst_id)
// debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
// assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
// SCHED.enqueue(tid);
// Ok(dst_id)
}
/// Terminates a process.
pub fn exit(self: ProcessRef, status: ExitCode) {
let thread = Thread::current();
let mut lock = self.inner.lock();
let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
todo!()
// let thread = Thread::current();
// let mut lock = self.inner.lock();
// let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
assert!(lock.exit.is_none());
lock.exit = Some(status);
lock.state = ProcessState::Finished;
// infoln!("Process {:?} is exiting: {:?}", lock.id, status);
// assert!(lock.exit.is_none());
// lock.exit = Some(status);
// lock.state = ProcessState::Finished;
for &tid in lock.threads.iter() {
Thread::get(tid).unwrap().terminate(status);
SCHED.dequeue(tid);
}
// for &tid in lock.threads.iter() {
// let thread = Thread::get(tid).unwrap();
// if thread.state() == ThreadState::Waiting {
// todo!()
// }
// thread.terminate(status);
// SCHED.dequeue(tid);
// }
if let Some(space) = lock.space.take() {
unsafe {
Space::release(space);
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
}
}
// if let Some(space) = lock.space.take() {
// unsafe {
// Space::release(space);
// Process::invalidate_asid((lock.id.asid() as usize) << 48);
// }
// }
// TODO when exiting from signal handler interrupting an IO operation
// deadlock is achieved
self.io.lock().handle_exit();
// // TODO when exiting from signal handler interrupting an IO operation
// // deadlock is achieved
// self.io.lock().handle_exit();
drop(lock);
// drop(lock);
self.exit_wait.wakeup_all();
// self.exit_wait.wakeup_all();
if is_running {
SCHED.switch(true);
panic!("This code should never run");
}
// if is_running {
// SCHED.switch(true);
// panic!("This code should never run");
// }
}
/// Terminates a thread of the process. If the thread is the only
/// one remaining, process itself is exited (see [Process::exit])
pub fn exit_thread(thread: ThreadRef, status: ExitCode) {
let switch = {
let switch = thread.state() == ThreadState::Running;
let process = thread.owner().unwrap();
let mut lock = process.inner.lock();
let tid = thread.id();
todo!()
if lock.threads.len() == 1 {
// TODO call Process::exit instead?
drop(lock);
process.exit(status);
return;
}
// let switch = {
// let switch = thread.state() == ThreadState::Running;
// let process = thread.owner().unwrap();
// let mut lock = process.inner.lock();
// let tid = thread.id();
lock.threads.retain(|&e| e != tid);
// if lock.threads.len() == 1 {
// // TODO call Process::exit instead?
// drop(lock);
// process.exit(status);
// return;
// }
thread.terminate(status);
SCHED.dequeue(tid);
debugln!("Thread {} terminated", tid);
// lock.threads.retain(|&e| e != tid);
switch
};
// thread.terminate(status);
// SCHED.dequeue(tid);
// debugln!("Thread {:?} terminated", tid);
if switch {
// TODO retain thread ID in process "finished" list and
// drop it when process finishes
SCHED.switch(true);
panic!("This code should not run");
} else {
// Can drop this thread: it's not running
todo!();
}
// switch
// };
// if switch {
// // TODO retain thread ID in process "finished" list and
// // drop it when process finishes
// SCHED.switch(true);
// panic!("This code should not run");
// } else {
// // Can drop this thread: it's not running
// todo!();
// }
}
fn collect(&self) -> Option<ExitCode> {
@@ -349,11 +391,9 @@ impl Process {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SH_OUTER
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READONLY;
| MapAttributes::USER_READ;
space.map(page_virt, page, flags)?;
page
};
@@ -373,11 +413,9 @@ impl Process {
phys
} else {
let page = phys::alloc_page(PageUsage::UserPrivate)?;
let flags = MapAttributes::SH_OUTER
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READONLY;
| MapAttributes::USER_READ;
space.map(page_virt, page, flags)?;
page
};
@@ -410,7 +448,7 @@ impl Process {
let mut data_offset = 0usize;
for arg in argv.iter() {
// XXX this is really unsafe and I am not really sure ABI will stay like this XXX
Self::write_paged(space, base + offset + 0, base + data_offset)?;
Self::write_paged(space, base + offset, base + data_offset)?;
Self::write_paged(space, base + offset + 8, arg.len())?;
offset += 16;
data_offset += arg.len();
@@ -428,6 +466,21 @@ impl Process {
Ok(base + offset)
}
pub fn asid(&self) -> usize {
(self.id().asid() as usize) << 48
}
// pub fn invalidate_tlb(&self) {
// Process::invalidate_asid(self.asid());
// }
// #[inline]
// pub fn invalidate_asid(asid: usize) {
// unsafe {
// asm!("tlbi aside1, {}", in(reg) asid);
// }
// }
/// Loads a new program into current process address space
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
loader: F,
@@ -435,7 +488,8 @@ impl Process {
) -> Result<(), Errno> {
unsafe {
// Run with interrupts disabled
asm!("msr daifset, #2");
// asm!("msr daifset, #2");
asm!("cli");
}
let proc = Process::current();
@@ -472,11 +526,11 @@ impl Process {
let ustack_virt_bottom = Self::USTACK_VIRT_TOP - Self::USTACK_PAGES * mem::PAGE_SIZE;
for i in 0..Self::USTACK_PAGES {
let page = phys::alloc_page(PageUsage::UserPrivate).unwrap();
let flags = MapAttributes::SH_OUTER
let flags = MapAttributes::SHARE_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READWRITE;
| MapAttributes::USER_WRITE
| MapAttributes::USER_READ
| MapAttributes::KERNEL_WRITE;
new_space
.map(ustack_virt_bottom + i * mem::PAGE_SIZE, page, flags)
.unwrap();
@@ -485,7 +539,6 @@ impl Process {
let entry = loader(new_space)?;
let arg = Self::store_arguments(new_space, argv)?;
debugln!("Will now enter at {:#x}", entry);
// TODO drop old address space
process_lock.space = Some(new_space);
@@ -493,7 +546,7 @@ impl Process {
// TODO drop old context
let ctx = thread.ctx.get();
let asid = (process_lock.id.asid() as usize) << 48;
asm!("tlbi aside1, {}", in(reg) asid);
// Process::invalidate_asid(asid);
ctx.write(Context::user(
entry,
+17 -11
View File
@@ -2,12 +2,14 @@
use crate::proc::{Thread, ThreadRef, THREADS};
use crate::sync::IrqSafeSpinLock;
use crate::util::InitOnce;
use libsys::proc::Tid;
use alloc::{collections::VecDeque, rc::Rc};
use core::arch::asm;
struct SchedulerInner {
queue: VecDeque<u32>,
idle: Option<u32>,
current: Option<u32>,
queue: VecDeque<Tid>,
idle: Option<Tid>,
current: Option<Tid>,
}
/// Process scheduler state and queues
@@ -23,7 +25,9 @@ impl SchedulerInner {
current: None,
};
this.idle = Some(Thread::new_kernel(None, idle_fn, 0).unwrap().id());
let idle = Thread::new_kernel(None, idle_fn, 0).unwrap().id();
assert_eq!(idle, Tid::IDLE);
this.idle = Some(idle);
this
}
@@ -39,12 +43,12 @@ impl Scheduler {
}
/// Schedules a thread for execution
pub fn enqueue(&self, tid: u32) {
pub fn enqueue(&self, tid: Tid) {
self.inner.get().lock().queue.push_back(tid);
}
/// Removes given `tid` from execution queue
pub fn dequeue(&self, tid: u32) {
pub fn dequeue(&self, tid: Tid) {
self.inner.get().lock().queue.retain(|&p| p != tid)
}
@@ -66,7 +70,8 @@ impl Scheduler {
THREADS.lock().get(&id).unwrap().clone()
};
asm!("msr daifset, #2");
asm!("cli");
// asm!("msr daifset, #2");
Thread::enter(thread)
}
@@ -76,7 +81,7 @@ impl Scheduler {
/// # Safety
///
/// Unsafe: only allowed to be called from Process::execve()
pub unsafe fn hack_current_tid(&self, old: u32, new: u32) {
pub unsafe fn hack_current_tid(&self, old: Tid, new: Tid) {
let mut lock = self.inner.get().lock();
match lock.current {
Some(t) if t == old => {
@@ -93,7 +98,7 @@ impl Scheduler {
let mut inner = self.inner.get().lock();
let current = inner.current.unwrap();
if !discard && current != 0 {
if !discard && current != Tid::IDLE {
// Put the process into the back of the queue
inner.queue.push_back(current);
}
@@ -118,7 +123,8 @@ impl Scheduler {
if !Rc::ptr_eq(&from, &to) {
unsafe {
asm!("msr daifset, #2");
asm!("cli");
// asm!("msr daifset, #2");
Thread::switch(from, to, discard);
}
}
@@ -147,7 +153,7 @@ pub fn is_ready() -> bool {
#[inline(never)]
extern "C" fn idle_fn(_a: usize) -> ! {
loop {
cortex_a::asm::wfi();
// cortex_a::asm::wfi();
}
}
+43 -38
View File
@@ -1,6 +1,6 @@
//! Facilities for controlling threads - smallest units of
//! execution in the operating system
use crate::arch::aarch64::exception::ExceptionFrame;
// use crate::arch::aarch64::exception::ExceptionFrame;
use crate::proc::{
wait::{Wait, WaitStatus},
Process, ProcessRef, SCHED, THREADS,
@@ -12,7 +12,7 @@ use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicU32, Ordering};
use libsys::{
error::Errno,
proc::{ExitCode, Pid},
proc::{ExitCode, Pid, Tid},
signal::Signal,
};
@@ -35,7 +35,7 @@ pub enum State {
}
struct ThreadInner {
id: u32,
id: Tid,
state: State,
owner: Option<Pid>,
pending_wait: Option<&'static Wait>,
@@ -61,9 +61,14 @@ impl Thread {
SCHED.current_thread()
}
/// Returns `true` if the thread is currently executing a signal handler context
pub fn is_handling_signal(&self) -> bool {
self.signal_pending.load(Ordering::Acquire) != 0
}
/// Returns a reference to thread `tid`, if it exists
#[inline]
pub fn get(tid: u32) -> Option<ThreadRef> {
pub fn get(tid: Tid) -> Option<ThreadRef> {
THREADS.lock().get(&tid).cloned()
}
@@ -138,38 +143,38 @@ impl Thread {
Ok(res)
}
/// Creates a fork thread cloning `frame` context
pub fn fork(
owner: Option<Pid>,
frame: &ExceptionFrame,
ttbr0: usize,
) -> Result<ThreadRef, Errno> {
let id = new_tid();
// /// Creates a fork thread cloning `frame` context
// pub fn fork(
// owner: Option<Pid>,
// frame: &ExceptionFrame,
// ttbr0: usize,
// ) -> Result<ThreadRef, Errno> {
// let id = new_tid();
let res = Rc::new(Self {
ctx: UnsafeCell::new(Context::fork(frame, ttbr0)),
signal_ctx: UnsafeCell::new(Context::empty()),
signal_pending: AtomicU32::new(0),
exit_wait: Wait::new("thread_exit"),
exit_status: InitOnce::new(),
inner: IrqSafeSpinLock::new(ThreadInner {
signal_entry: 0,
signal_stack: 0,
id,
owner,
pending_wait: None,
wait_status: WaitStatus::Done,
state: State::Ready,
}),
});
debugln!("Forked new user thread: {:?}", id);
assert!(THREADS.lock().insert(id, res.clone()).is_none());
Ok(res)
}
// let res = Rc::new(Self {
// ctx: UnsafeCell::new(Context::fork(frame, ttbr0)),
// signal_ctx: UnsafeCell::new(Context::empty()),
// signal_pending: AtomicU32::new(0),
// exit_wait: Wait::new("thread_exit"),
// exit_status: InitOnce::new(),
// inner: IrqSafeSpinLock::new(ThreadInner {
// signal_entry: 0,
// signal_stack: 0,
// id,
// owner,
// pending_wait: None,
// wait_status: WaitStatus::Done,
// state: State::Ready,
// }),
// });
// debugln!("Forked new user thread: {:?}", id);
// assert!(THREADS.lock().insert(id, res.clone()).is_none());
// Ok(res)
// }
/// Returns the thread ID
#[inline]
pub fn id(&self) -> u32 {
pub fn id(&self) -> Tid {
self.inner.lock().id
}
@@ -237,6 +242,7 @@ impl Thread {
/// Changes process wait condition status
pub fn setup_wait(&self, wait: *const Wait) {
#![allow(clippy::not_unsafe_ptr_arg_deref)]
let mut lock = self.inner.lock();
// FIXME this is not cool
lock.pending_wait = Some(unsafe { &*wait });
@@ -244,7 +250,7 @@ impl Thread {
}
/// Suspends current thread until thread `tid` terminates
pub fn waittid(tid: u32) -> Result<(), Errno> {
pub fn waittid(tid: Tid) -> Result<(), Errno> {
loop {
let thread = THREADS
.lock()
@@ -334,7 +340,7 @@ impl Thread {
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
debugln!(
"Signal entry: tid={}, pc={:#x}, sp={:#x}, ttbr0={:#x}",
"Signal entry: tid={:?}, pc={:#x}, sp={:#x}, ttbr0={:#x}",
lock.id,
lock.signal_entry,
lock.signal_stack,
@@ -398,9 +404,8 @@ impl Drop for Thread {
}
/// Allocates a new thread ID
pub fn new_tid() -> u32 {
static LAST: AtomicU32 = AtomicU32::new(1);
pub fn new_tid() -> Tid {
static LAST: AtomicU32 = AtomicU32::new(0);
let id = LAST.fetch_add(1, Ordering::Relaxed);
assert!(id < 256, "Out of user TIDs");
id
Tid::from(id)
}
+166 -152
View File
@@ -6,12 +6,12 @@ use crate::proc::{sched::SCHED, Thread, ThreadRef};
use crate::sync::IrqSafeSpinLock;
use alloc::collections::LinkedList;
use core::time::Duration;
use libsys::{error::Errno, stat::FdSet};
use libsys::{error::Errno, proc::Tid, stat::FdSet};
/// Wait channel structure. Contains a queue of processes
/// waiting for some event to happen.
pub struct Wait {
queue: IrqSafeSpinLock<LinkedList<u32>>,
queue: IrqSafeSpinLock<LinkedList<Tid>>,
#[allow(dead_code)]
name: &'static str
}
@@ -28,7 +28,7 @@ pub enum WaitStatus {
}
struct Timeout {
tid: u32,
tid: Tid,
deadline: Duration,
}
@@ -39,35 +39,37 @@ pub static WAIT_SELECT: Wait = Wait::new("select");
/// Checks for any timed out wait channels and interrupts them
pub fn tick() {
let time = machine::local_timer().timestamp().unwrap();
let mut list = TICK_LIST.lock();
let mut cursor = list.cursor_front_mut();
todo!();
// let time = machine::local_timer().timestamp().unwrap();
// let mut list = TICK_LIST.lock();
// let mut cursor = list.cursor_front_mut();
while let Some(item) = cursor.current() {
if time > item.deadline {
let tid = item.tid;
cursor.remove_current();
SCHED.enqueue(tid);
} else {
cursor.move_next();
}
}
// while let Some(item) = cursor.current() {
// if time > item.deadline {
// let tid = item.tid;
// cursor.remove_current();
// SCHED.enqueue(tid);
// } else {
// cursor.move_next();
// }
// }
}
/// Suspends current process for given duration
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
// Dummy wait descriptor which will never receive notifications
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
let deadline = machine::local_timer().timestamp()? + timeout;
match SLEEP_NOTIFY.wait(Some(deadline)) {
Err(Errno::Interrupt) => {
*remaining = deadline - machine::local_timer().timestamp()?;
Err(Errno::Interrupt)
}
Err(Errno::TimedOut) => Ok(()),
Ok(_) => panic!("Impossible result"),
res => res,
}
todo!()
// // Dummy wait descriptor which will never receive notifications
// static SLEEP_NOTIFY: Wait = Wait::new("sleep");
// let deadline = machine::local_timer().timestamp()? + timeout;
// match SLEEP_NOTIFY.wait(Some(deadline)) {
// Err(Errno::Interrupt) => {
// *remaining = deadline - machine::local_timer().timestamp()?;
// Err(Errno::Interrupt)
// }
// Err(Errno::TimedOut) => Ok(()),
// Ok(_) => panic!("Impossible result"),
// res => res,
// }
}
/// Suspends current process until some file descriptor
@@ -78,45 +80,51 @@ pub fn select(
mut wfds: Option<&mut FdSet>,
timeout: Option<Duration>,
) -> Result<usize, Errno> {
if wfds.is_none() && rfds.is_none() {
todo!();
}
let read = rfds.as_deref().map(FdSet::clone);
let write = wfds.as_deref().map(FdSet::clone);
rfds.as_deref_mut().map(FdSet::reset);
wfds.as_deref_mut().map(FdSet::reset);
todo!();
let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
let proc = thread.owner().unwrap();
let mut io = proc.io.lock();
// if wfds.is_none() && rfds.is_none() {
// todo!();
// }
// let read = rfds.as_deref().map(FdSet::clone);
// let write = wfds.as_deref().map(FdSet::clone);
// if let Some(rfds) = &mut rfds {
// rfds.reset();
// }
// if let Some(wfds) = &mut wfds {
// wfds.reset();
// }
loop {
if let Some(read) = &read {
for fd in read.iter() {
let file = io.file(fd)?;
if file.borrow().is_ready(false)? {
rfds.as_mut().unwrap().set(fd);
return Ok(1);
}
}
}
if let Some(write) = &write {
for fd in write.iter() {
let file = io.file(fd)?;
if file.borrow().is_ready(true)? {
wfds.as_mut().unwrap().set(fd);
return Ok(1);
}
}
}
// let deadline = timeout.map(|v| v + machine::local_timer().timestamp().unwrap());
// let proc = thread.owner().unwrap();
// let mut io = proc.io.lock();
// Suspend
match WAIT_SELECT.wait(deadline) {
Err(Errno::TimedOut) => return Ok(0),
Err(e) => return Err(e),
Ok(_) => {}
}
}
// loop {
// if let Some(read) = &read {
// for fd in read.iter() {
// let file = io.file(fd)?;
// if file.borrow().is_ready(false)? {
// rfds.as_mut().unwrap().set(fd);
// return Ok(1);
// }
// }
// }
// if let Some(write) = &write {
// for fd in write.iter() {
// let file = io.file(fd)?;
// if file.borrow().is_ready(true)? {
// wfds.as_mut().unwrap().set(fd);
// return Ok(1);
// }
// }
// }
// // Suspend
// match WAIT_SELECT.wait(deadline) {
// Err(Errno::TimedOut) => return Ok(0),
// Err(e) => return Err(e),
// Ok(_) => {}
// }
// }
}
impl Wait {
@@ -129,62 +137,66 @@ impl Wait {
}
/// Interrupt wait pending on the channel
pub fn abort(&self, tid: u32, enqueue: bool) {
let mut queue = self.queue.lock();
let mut tick_lock = TICK_LIST.lock();
let mut cursor = tick_lock.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == item.tid {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
pub fn abort(&self, tid: Tid, enqueue: bool) {
todo!();
let mut cursor = queue.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == *item {
cursor.remove_current();
let thread = Thread::get(tid).unwrap();
thread.set_wait_status(WaitStatus::Interrupted);
if enqueue {
SCHED.enqueue(tid);
}
break;
} else {
cursor.move_next();
}
}
// let mut queue = self.queue.lock();
// let mut tick_lock = TICK_LIST.lock();
// let mut cursor = tick_lock.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == item.tid {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// let mut cursor = queue.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == *item {
// cursor.remove_current();
// let thread = Thread::get(tid).unwrap();
// thread.set_wait_status(WaitStatus::Interrupted);
// if enqueue {
// SCHED.enqueue(tid);
// }
// break;
// } else {
// cursor.move_next();
// }
// }
}
fn wakeup_some(&self, mut limit: usize) -> usize {
// No IRQs will arrive now == safe to manipulate tick list
let mut queue = self.queue.lock();
let mut count = 0;
while limit != 0 && !queue.is_empty() {
let tid = queue.pop_front();
if let Some(tid) = tid {
let mut tick_lock = TICK_LIST.lock();
let mut cursor = tick_lock.cursor_front_mut();
while let Some(item) = cursor.current() {
if tid == item.tid {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
drop(tick_lock);
todo!();
Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
SCHED.enqueue(tid);
}
// // No IRQs will arrive now == safe to manipulate tick list
// let mut queue = self.queue.lock();
// let mut count = 0;
// while limit != 0 && !queue.is_empty() {
// let tid = queue.pop_front();
// if let Some(tid) = tid {
// let mut tick_lock = TICK_LIST.lock();
// let mut cursor = tick_lock.cursor_front_mut();
// while let Some(item) = cursor.current() {
// if tid == item.tid {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// drop(tick_lock);
limit -= 1;
count += 1;
}
count
// Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
// SCHED.enqueue(tid);
// }
// limit -= 1;
// count += 1;
// }
// count
}
/// Notifies all processes waiting for this event
@@ -200,51 +212,53 @@ impl Wait {
/// Suspends current process until event is signalled or
/// (optional) deadline is reached
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
let thread = Thread::current();
//let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
let mut queue_lock = self.queue.lock();
todo!();
queue_lock.push_back(thread.id());
thread.setup_wait(self);
// let thread = Thread::current();
// //let deadline = timeout.map(|t| machine::local_timer().timestamp().unwrap() + t);
// let mut queue_lock = self.queue.lock();
if let Some(deadline) = deadline {
TICK_LIST.lock().push_back(Timeout {
tid: thread.id(),
deadline,
});
}
// queue_lock.push_back(thread.id());
// thread.setup_wait(self);
loop {
match thread.wait_status() {
WaitStatus::Pending => {}
WaitStatus::Done => {
return Ok(());
}
WaitStatus::Interrupted => {
return Err(Errno::Interrupt);
}
};
// if let Some(deadline) = deadline {
// TICK_LIST.lock().push_back(Timeout {
// tid: thread.id(),
// deadline,
// });
// }
drop(queue_lock);
thread.enter_wait();
queue_lock = self.queue.lock();
// loop {
// match thread.wait_status() {
// WaitStatus::Pending => {}
// WaitStatus::Done => {
// return Ok(());
// }
// WaitStatus::Interrupted => {
// return Err(Errno::Interrupt);
// }
// };
if let Some(deadline) = deadline {
if machine::local_timer().timestamp()? > deadline {
let mut cursor = queue_lock.cursor_front_mut();
// drop(queue_lock);
// thread.enter_wait();
// queue_lock = self.queue.lock();
while let Some(&mut item) = cursor.current() {
if thread.id() == item {
cursor.remove_current();
break;
} else {
cursor.move_next();
}
}
// if let Some(deadline) = deadline {
// if machine::local_timer().timestamp()? > deadline {
// let mut cursor = queue_lock.cursor_front_mut();
return Err(Errno::TimedOut);
}
}
}
// while let Some(&mut item) = cursor.current() {
// if thread.id() == item {
// cursor.remove_current();
// break;
// } else {
// cursor.move_next();
// }
// }
// return Err(Errno::TimedOut);
// }
// }
// }
}
}
+2 -2
View File
@@ -38,7 +38,7 @@ impl<T> IrqSafeSpinLock<T> {
#[inline(always)]
unsafe fn force_release(&self) {
self.state.store(false, Ordering::Release);
cortex_a::asm::sev();
// cortex_a::asm::sev();
}
/// Returns [IrqSafeSpinLockGuard] for this lock
@@ -47,7 +47,7 @@ impl<T> IrqSafeSpinLock<T> {
let irq_state = unsafe { irq_mask_save() };
while self.try_lock().is_err() {
cortex_a::asm::wfe();
// cortex_a::asm::wfe();
}
IrqSafeSpinLockGuard {
+30 -15
View File
@@ -12,28 +12,39 @@ macro_rules! invalid_memory {
warnln!($($args)+);
#[cfg(feature = "aggressive_syscall")]
{
use libsys::signal::Signal;
use crate::proc::Thread;
todo!()
// 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);
}
}
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
let mut res: usize;
unsafe {
if write {
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
} else {
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
let mut res: usize;
unsafe {
if write {
asm!("at s1e0w, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
} else {
asm!("at s1e0r, {}; mrs {}, par_el1", in(reg) virt, out(reg) res);
}
}
res & 1 == 0
}
} else {
#[inline(always)]
fn is_el0_accessible(virt: usize, write: bool) -> bool {
// TODO implement this
true
}
}
res & 1 == 0
}
/// Checks given argument and interprets it as a `T` reference
@@ -122,14 +133,18 @@ 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) {
// It's possible a CoW page hasn't yet been cloned when trying
// a write access
let res = if write {
todo!();
process.manipulate_space(|space| {
space.try_cow_copy(i * mem::PAGE_SIZE)
// space.try_cow_copy(i * mem::PAGE_SIZE)?;
// Process::invalidate_asid(asid);
Ok(())
})
} else {
Err(Errno::DoesNotExist)
+339 -323
View File
@@ -1,20 +1,24 @@
//! System call implementation
use crate::arch::{machine, platform::exception::ExceptionFrame};
use crate::mem::{virt::MapAttributes, phys::PageUsage};
// use crate::arch::{machine, platform::exception::ExceptionFrame};
use crate::arch::platform::ForkFrame;
use crate::debug::Level;
use crate::dev::timer::TimestampSource;
// use crate::dev::timer::TimestampSource;
use crate::fs::create_filesystem;
use crate::mem::{
phys::PageUsage,
virt::table::{AddressSpace, MapAttributes},
};
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
use core::mem::size_of;
// use core::mem::size_of;
use core::ops::DerefMut;
use core::time::Duration;
// use core::time::Duration;
use libsys::{
abi::SystemCall,
debug::TraceLevel,
error::Errno,
ioctl::IoctlCmd,
proc::{ExitCode, Pid, MemoryAccess},
proc::{ExitCode, MemoryAccess, Pid, Tid},
signal::{Signal, SignalDestination},
stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
@@ -33,7 +37,7 @@ pub mod arg;
///
/// Unsafe: accepts and clones process states. Only legal to call
/// from exception handlers.
pub unsafe fn sys_fork(regs: &mut ExceptionFrame) -> Result<Pid, Errno> {
pub unsafe fn sys_fork(regs: &mut ForkFrame) -> Result<Pid, Errno> {
Process::current().fork(regs)
}
@@ -56,8 +60,7 @@ fn find_at_node<T: DerefMut<Target = ProcessIo>>(
}
}
/// Main system call dispatcher function
pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
fn _syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
match num {
// I/O
SystemCall::Read => {
@@ -76,132 +79,132 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
io.file(fd)?.borrow_mut().write(buf)
}
SystemCall::Open => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let path = arg::str_ref(args[1], args[2])?;
let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
let proc = Process::current();
let mut io = proc.io.lock();
let at = if let Some(fd) = at_fd {
io.file(fd)?.borrow().node()
} else {
None
};
let file = io.ioctx().open(at, path, mode, opts)?;
Ok(u32::from(io.place_file(file)?) as usize)
}
SystemCall::Close => {
let proc = Process::current();
let mut io = proc.io.lock();
let fd = FileDescriptor::from(args[0] as u32);
io.close_file(fd)?;
Ok(0)
}
SystemCall::FileStatus => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let filename = arg::str_ref(args[1], args[2])?;
let buf = arg::struct_mut::<Stat>(args[3])?;
let flags = args[4] as u32;
let proc = Process::current();
let mut io = proc.io.lock();
let stat =
find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?;
*buf = stat;
Ok(0)
}
SystemCall::Ioctl => {
let fd = FileDescriptor::from(args[0] as u32);
let cmd = IoctlCmd::try_from(args[1] as u32)?;
let proc = Process::current();
let mut io = proc.io.lock();
let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?;
node.ioctl(cmd, args[2], args[3])
}
SystemCall::Select => {
let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
let timeout = if args[2] == 0 {
None
} else {
Some(Duration::from_nanos(args[2] as u64))
};
wait::select(Thread::current(), rfds, wfds, timeout)
}
SystemCall::Access => {
let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
let path = arg::str_ref(args[1], args[2])?;
let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
let flags = args[4] as u32;
let proc = Process::current();
let mut io = proc.io.lock();
find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
.check_access(io.ioctx(), mode)?;
Ok(0)
}
SystemCall::ReadDirectory => {
let proc = Process::current();
let fd = FileDescriptor::from(args[0] as u32);
let mut io = proc.io.lock();
let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
io.file(fd)?.borrow_mut().readdir(buf)
}
SystemCall::GetUserId => {
let proc = Process::current();
let uid = proc.io.lock().uid();
Ok(u32::from(uid) as usize)
}
SystemCall::GetGroupId => {
let proc = Process::current();
let gid = proc.io.lock().gid();
Ok(u32::from(gid) as usize)
}
SystemCall::DuplicateFd => {
let src = FileDescriptor::from(args[0] as u32);
let dst = FileDescriptor::from_i32(args[1] as i32)?;
let proc = Process::current();
let mut io = proc.io.lock();
let res = io.duplicate_file(src, dst)?;
Ok(u32::from(res) as usize)
}
SystemCall::SetUserId => {
let uid = UserId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_uid(uid)?;
Ok(0)
}
SystemCall::SetGroupId => {
let gid = GroupId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_gid(gid)?;
Ok(0)
}
SystemCall::SetCurrentDirectory => {
let path = arg::str_ref(args[0], args[1])?;
let proc = Process::current();
proc.io.lock().ioctx().chdir(path)?;
Ok(0)
}
SystemCall::GetCurrentDirectory => {
todo!()
}
SystemCall::Seek => {
todo!()
}
// SystemCall::Open => {
// let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
// let path = arg::str_ref(args[1], args[2])?;
// let mode = FileMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
// let opts = OpenFlags::from_bits(args[4] as u32).ok_or(Errno::InvalidArgument)?;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// let at = if let Some(fd) = at_fd {
// io.file(fd)?.borrow().node()
// } else {
// None
// };
//
// let file = io.ioctx().open(at, path, mode, opts)?;
// Ok(u32::from(io.place_file(file)?) as usize)
// }
// SystemCall::Close => {
// let proc = Process::current();
// let mut io = proc.io.lock();
// let fd = FileDescriptor::from(args[0] as u32);
//
// io.close_file(fd)?;
// Ok(0)
// }
// SystemCall::FileStatus => {
// let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
// let filename = arg::str_ref(args[1], args[2])?;
// let buf = arg::struct_mut::<Stat>(args[3])?;
// let flags = args[4] as u32;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
// let stat =
// find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?;
// *buf = stat;
// Ok(0)
// }
// SystemCall::Ioctl => {
// let fd = FileDescriptor::from(args[0] as u32);
// let cmd = IoctlCmd::try_from(args[1] as u32)?;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// let node = io.file(fd)?.borrow().node().ok_or(Errno::InvalidFile)?;
// node.ioctl(cmd, args[2], args[3])
// }
// SystemCall::Select => {
// let rfds = arg::option_struct_mut::<FdSet>(args[0])?;
// let wfds = arg::option_struct_mut::<FdSet>(args[1])?;
// let timeout = if args[2] == 0 {
// None
// } else {
// Some(Duration::from_nanos(args[2] as u64))
// };
//
// wait::select(Thread::current(), rfds, wfds, timeout)
// }
// SystemCall::Access => {
// let at_fd = FileDescriptor::from_i32(args[0] as i32)?;
// let path = arg::str_ref(args[1], args[2])?;
// let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
// let flags = args[4] as u32;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?
// .check_access(io.ioctx(), mode)?;
// Ok(0)
// }
// SystemCall::ReadDirectory => {
// let proc = Process::current();
// let fd = FileDescriptor::from(args[0] as u32);
// let mut io = proc.io.lock();
// let buf = arg::struct_buf_mut::<DirectoryEntry>(args[1], args[2])?;
//
// io.file(fd)?.borrow_mut().readdir(buf)
// }
// SystemCall::GetUserId => {
// let proc = Process::current();
// let uid = proc.io.lock().uid();
// Ok(u32::from(uid) as usize)
// }
// SystemCall::GetGroupId => {
// let proc = Process::current();
// let gid = proc.io.lock().gid();
// Ok(u32::from(gid) as usize)
// }
// SystemCall::DuplicateFd => {
// let src = FileDescriptor::from(args[0] as u32);
// let dst = FileDescriptor::from_i32(args[1] as i32)?;
//
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// let res = io.duplicate_file(src, dst)?;
//
// Ok(u32::from(res) as usize)
// }
// SystemCall::SetUserId => {
// let uid = UserId::from(args[0] as u32);
// let proc = Process::current();
// proc.io.lock().set_uid(uid)?;
// Ok(0)
// }
// SystemCall::SetGroupId => {
// let gid = GroupId::from(args[0] as u32);
// let proc = Process::current();
// proc.io.lock().set_gid(gid)?;
// Ok(0)
// }
// SystemCall::SetCurrentDirectory => {
// let path = arg::str_ref(args[0], args[1])?;
// let proc = Process::current();
// proc.io.lock().ioctx().chdir(path)?;
// Ok(0)
// }
// SystemCall::GetCurrentDirectory => {
// todo!()
// }
// SystemCall::Seek => {
// todo!()
// }
SystemCall::MapMemory => {
let len = args[1];
if len == 0 || (len & 0xFFF) != 0 {
@@ -210,7 +213,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let acc = MemoryAccess::from_bits(args[2] as u32).ok_or(Errno::InvalidArgument)?;
let _flags = MemoryAccess::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?;
let mut attrs = MapAttributes::NOT_GLOBAL | MapAttributes::SH_OUTER | MapAttributes::PXN;
let mut attrs =
MapAttributes::NOT_GLOBAL | MapAttributes::SHARE_OUTER | MapAttributes::USER_READ;
if !acc.contains(MemoryAccess::READ) {
return Err(Errno::NotImplemented);
}
@@ -218,12 +222,12 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
if acc.contains(MemoryAccess::EXEC) {
return Err(Errno::PermissionDenied);
}
attrs |= MapAttributes::AP_BOTH_READWRITE;
attrs |= MapAttributes::USER_WRITE | MapAttributes::KERNEL_WRITE;
} else {
attrs |= MapAttributes::AP_BOTH_READONLY;
attrs |= MapAttributes::USER_READ;
}
if !acc.contains(MemoryAccess::EXEC) {
attrs |= MapAttributes::UXN;
attrs |= MapAttributes::USER_EXEC;
}
// TODO don't ignore flags
@@ -235,186 +239,186 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
space.allocate(0x100000000, 0xF00000000, len / 4096, attrs, usage)
})
}
SystemCall::UnmapMemory => {
let addr = args[0];
let len = args[1];
if addr == 0 || len == 0 || addr & 0xFFF != 0 || len & 0xFFF != 0 {
return Err(Errno::InvalidArgument);
}
let proc = Process::current();
proc.manipulate_space(move |space| {
space.free(addr, len / 4096)
})?;
Ok(0)
}
// Process
SystemCall::Clone => {
let entry = args[0];
let stack = args[1];
let arg = args[2];
Process::current()
.new_user_thread(entry, stack, arg)
.map(|e| e as usize)
}
SystemCall::Exec => {
let filename = arg::str_ref(args[0], args[1])?;
let argv = arg::struct_buf_ref::<&str>(args[2], args[3])?;
// Validate each argument as well
for item in argv.iter() {
arg::validate_ptr(item.as_ptr() as usize, item.len(), false)?;
}
let node = {
let proc = Process::current();
let mut io = proc.io.lock();
// TODO argv, envp array passing ABI?
let node = io.ioctx().find(None, filename, true)?;
drop(io);
node
};
let file = node.open(OpenFlags::O_RDONLY)?;
Process::execve(move |space| elf::load_elf(space, file), argv).unwrap();
panic!();
}
SystemCall::Exit => {
let status = ExitCode::from(args[0] as i32);
let flags = args[1];
if flags & (1 << 0) != 0 {
Process::exit_thread(Thread::current(), status);
} else {
Process::current().exit(status);
}
unreachable!();
}
SystemCall::WaitPid => {
// TODO special "pid" values
let pid = unsafe { Pid::from_raw(args[0] as u32) };
let status = arg::struct_mut::<i32>(args[1])?;
match Process::waitpid(pid) {
Ok(exit) => {
*status = i32::from(exit);
Ok(0)
}
e => e.map(|e| i32::from(e) as usize),
}
}
SystemCall::WaitTid => {
let tid = args[0] as u32;
match Thread::waittid(tid) {
Ok(_) => Ok(0),
_ => todo!(),
}
}
SystemCall::GetPid => Ok(Process::current().id().value() as usize),
SystemCall::GetTid => Ok(Thread::current().id() as usize),
SystemCall::Sleep => {
let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
let mut rem = Duration::new(0, 0);
let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
if res == Err(Errno::Interrupt) {
warnln!("Sleep interrupted, {:?} remaining", rem);
if rem_buf.is_some() {
todo!()
}
}
res.map(|_| 0)
}
SystemCall::SetSignalEntry => {
Thread::current().set_signal_entry(args[0], args[1]);
Ok(0)
}
SystemCall::SignalReturn => {
Thread::current().return_from_signal();
unreachable!();
}
SystemCall::SendSignal => {
let target = SignalDestination::from(args[0] as isize);
let signal = Signal::try_from(args[1] as u32)?;
match target {
SignalDestination::This => Process::current().set_signal(signal),
SignalDestination::Process(pid) => Process::get(pid)
.ok_or(Errno::DoesNotExist)?
.set_signal(signal),
_ => todo!(),
};
Ok(0)
}
SystemCall::Yield => {
proc::switch();
Ok(0)
}
SystemCall::GetSid => {
// TODO handle kernel processes here?
let pid = args[0] as u32;
let current = Process::current();
let proc = if pid == 0 {
current
} else {
let pid = unsafe { Pid::from_raw(pid) };
let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
if proc.sid() != current.sid() {
return Err(Errno::PermissionDenied);
}
proc
};
Ok(proc.sid().value() as usize)
}
SystemCall::GetPgid => {
// TODO handle kernel processes here?
let pid = args[0] as u32;
let current = Process::current();
let proc = if pid == 0 {
current
} else {
let pid = unsafe { Pid::from_raw(pid) };
Process::get(pid).ok_or(Errno::DoesNotExist)?
};
Ok(proc.pgid().value() as usize)
}
SystemCall::GetPpid => Ok(Process::current().ppid().unwrap().value() as usize),
SystemCall::SetSid => {
let proc = Process::current();
let mut io = proc.io.lock();
if let Some(_ctty) = io.ctty() {
todo!();
}
let id = proc.id();
proc.set_sid(id);
Ok(id.value() as usize)
}
SystemCall::SetPgid => {
let pid = args[0] as u32;
let pgid = args[1] as u32;
let current = Process::current();
let proc = if pid == 0 { current } else { todo!() };
if pgid == 0 {
proc.set_pgid(proc.id());
} else {
todo!();
}
Ok(proc.pgid().value() as usize)
}
// System
SystemCall::GetCpuTime => {
let time = machine::local_timer().timestamp()?;
Ok(time.as_nanos() as usize)
}
// SystemCall::UnmapMemory => {
// let addr = args[0];
// let len = args[1];
//
// if addr == 0 || len == 0 || addr & 0xFFF != 0 || len & 0xFFF != 0 {
// return Err(Errno::InvalidArgument);
// }
//
// let proc = Process::current();
// proc.manipulate_space(move |space| space.free(addr, len / 4096))?;
// Ok(0)
// }
//
// // Process
// SystemCall::Clone => {
// let entry = args[0];
// let stack = args[1];
// let arg = args[2];
//
// Process::current()
// .new_user_thread(entry, stack, arg)
// .map(|e| u32::from(e) as usize)
// }
// SystemCall::Exec => {
// let filename = arg::str_ref(args[0], args[1])?;
// let argv = arg::struct_buf_ref::<&str>(args[2], args[3])?;
// // Validate each argument as well
// for item in argv.iter() {
// arg::validate_ptr(item.as_ptr() as usize, item.len(), false)?;
// }
// let node = {
// let proc = Process::current();
// let mut io = proc.io.lock();
// // TODO argv, envp array passing ABI?
// let node = io.ioctx().find(None, filename, true)?;
// drop(io);
// node
// };
// let file = node.open(OpenFlags::O_RDONLY)?;
// Process::execve(move |space| elf::load_elf(space, file), argv).unwrap();
// panic!();
// }
// SystemCall::Exit => {
// let status = ExitCode::from(args[0] as i32);
// let flags = args[1];
//
// if flags & (1 << 0) != 0 {
// Process::exit_thread(Thread::current(), status);
// } else {
// Process::current().exit(status);
// }
//
// unreachable!();
// }
// SystemCall::WaitPid => {
// // TODO special "pid" values
// let pid = Pid::try_from(args[0] as u32)?;
// let status = arg::struct_mut::<i32>(args[1])?;
//
// match Process::waitpid(pid) {
// Ok(exit) => {
// *status = i32::from(exit);
// Ok(0)
// }
// e => e.map(|e| i32::from(e) as usize),
// }
// }
// SystemCall::WaitTid => {
// let tid = Tid::from(args[0] as u32);
//
// match Thread::waittid(tid) {
// Ok(_) => Ok(0),
// _ => todo!(),
// }
// }
// SystemCall::GetPid => Ok(u32::from(Process::current().id()) as usize),
// SystemCall::GetTid => Ok(u32::from(Thread::current().id()) as usize),
// SystemCall::Sleep => {
// let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
// let mut rem = Duration::new(0, 0);
// let res = wait::sleep(Duration::from_nanos(args[0] as u64), &mut rem);
// if res == Err(Errno::Interrupt) {
// warnln!("Sleep interrupted, {:?} remaining", rem);
// if rem_buf.is_some() {
// todo!()
// }
// }
// res.map(|_| 0)
// }
// SystemCall::SetSignalEntry => {
// Thread::current().set_signal_entry(args[0], args[1]);
// Ok(0)
// }
// SystemCall::SignalReturn => {
// Thread::current().return_from_signal();
// unreachable!();
// }
// SystemCall::SendSignal => {
// let target = SignalDestination::from(args[0] as isize);
// let signal = Signal::try_from(args[1] as u32)?;
//
// match target {
// SignalDestination::This => Process::current().set_signal(signal),
// SignalDestination::Process(pid) => Process::get(pid)
// .ok_or(Errno::DoesNotExist)?
// .set_signal(signal),
// _ => todo!(),
// };
// Ok(0)
// }
// SystemCall::Yield => {
// proc::switch();
// Ok(0)
// }
// SystemCall::GetSid => {
// // TODO handle kernel processes here?
// let pid = Pid::to_option(args[0] as u32);
// let current = Process::current();
// let proc = if let Some(pid) = pid {
// let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
// if proc.sid() != current.sid() {
// return Err(Errno::PermissionDenied);
// }
// proc
// } else {
// current
// };
//
// Ok(u32::from(proc.sid()) as usize)
// }
// SystemCall::GetPgid => {
// // TODO handle kernel processes here?
// let pid = Pid::to_option(args[0] as u32);
// let current = Process::current();
// let proc = if let Some(pid) = pid {
// Process::get(pid).ok_or(Errno::DoesNotExist)?
// } else {
// current
// };
//
// Ok(u32::from(proc.pgid()) as usize)
// }
// SystemCall::GetPpid => Ok(u32::from(Process::current().ppid().unwrap()) as usize),
// SystemCall::SetSid => {
// let proc = Process::current();
// let mut io = proc.io.lock();
//
// if let Some(_ctty) = io.ctty() {
// todo!();
// }
//
// let id = proc.id();
// proc.set_sid(id);
// Ok(u32::from(id) as usize)
// }
// SystemCall::SetPgid => {
// let pid = Pid::to_option(args[0] as u32);
// let pgid = Pid::to_option(args[1] as u32);
//
// let current = Process::current();
// let proc = if let Some(_pid) = pid {
// todo!()
// } else {
// current
// };
//
// if let Some(_pgid) = pgid {
// todo!();
// } else {
// proc.set_pgid(proc.id());
// }
//
// Ok(u32::from(proc.pgid()) as usize)
// }
//
// // System
// SystemCall::GetCpuTime => {
// let time = machine::local_timer().timestamp()?;
// Ok(time.as_nanos() as usize)
// }
SystemCall::Mount => {
let target = arg::str_ref(args[0], args[1])?;
let options = arg::struct_ref::<MountOptions>(args[2])?;
@@ -430,7 +434,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
target_node.mount(root)?;
Ok(0)
}
},
// Debugging
SystemCall::DebugTrace => {
@@ -440,11 +444,23 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let buf = arg::str_ref(args[1], args[2])?;
let thread = Thread::current();
let proc = thread.owner().unwrap();
println!(level, "[trace {:?}:{}] {}", proc.id(), thread.id(), buf);
println!(level, "[trace {:?}:{:?}] {}", proc.id(), thread.id(), buf);
Ok(args[1])
}
// Handled elsewhere
SystemCall::Fork => unreachable!(),
// // Handled elsewhere
// SystemCall::Fork => unreachable!(),
_ => panic!("Unimplemented: {:?}", num),
}
}
/// Main system call dispatcher function
pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let thread = Thread::current();
let process = thread.owner().unwrap();
let result = _syscall(num, args);
if !thread.is_handling_signal() {
// process.handle_pending_signals();
}
result
}
+9 -1
View File
@@ -20,6 +20,14 @@ impl<T> InitOnce<T> {
}
}
pub fn as_ref_option(&self) -> Option<&T> {
if self.is_initialized() {
Some(self.get())
} else {
None
}
}
/// Returns `true` if this [InitOnce<T>] can be used
#[inline(always)]
pub fn is_initialized(&self) -> bool {
@@ -29,7 +37,7 @@ impl<T> InitOnce<T> {
/// Returns the initialized value. Will panic if the value has not
/// yet been initialized.
#[allow(clippy::mut_from_ref)]
pub fn get(&self) -> &mut T {
pub fn get(&self) -> &T {
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
unsafe { (*self.inner.get()).assume_init_mut() }
}
+62 -71
View File
@@ -3,7 +3,7 @@ use crate::{
debug::TraceLevel,
error::Errno,
ioctl::IoctlCmd,
proc::{ExitCode, MemoryAccess, MemoryMap, Pid},
proc::{ExitCode, MemoryAccess, MemoryMap, Pid, Tid},
signal::{Signal, SignalDestination},
stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
@@ -11,49 +11,62 @@ use crate::{
},
};
use core::time::Duration;
use core::arch::asm;
// TODO document the syscall ABI
// rax
//
// rdi
// rsi
// rdx
// r10
// r8
// r9
// TODO move this to libusr
#[cfg(target_arch = "x86_64")]
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
asm!("svc #0", out("x0") res, in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res,
options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res,
in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res, in("rdi") $a0,
options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1,
in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, in("r10") $a3, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x4") $a4, in("x8") $num.repr(), options(nostack));
let mut res: usize = $num.repr();
asm!("syscall",
inout("rax") res, in("rdi") $a0, in("rsi") $a1,
in("rdx") $a2, in("r10") $a3, in("r8") $a4, options(nostack));
res
}};
}
@@ -75,9 +88,6 @@ macro_rules! argp {
// ($a:expr) => ($a as *const core::ffi::c_void as usize)
// }
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_exit(code: ExitCode) -> ! {
unsafe {
@@ -86,17 +96,11 @@ pub fn sys_exit(code: ExitCode) -> ! {
unreachable!();
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_close(fd: FileDescriptor) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::Close, argn!(u32::from(fd))) })
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ex_nanosleep(ns: u64, rem: &mut [u64; 2]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
@@ -104,9 +108,6 @@ pub fn sys_ex_nanosleep(ns: u64, rem: &mut [u64; 2]) -> Result<(), Errno> {
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ex_debug_trace(level: TraceLevel, msg: &[u8]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
@@ -119,9 +120,6 @@ pub fn sys_ex_debug_trace(level: TraceLevel, msg: &[u8]) -> Result<(), Errno> {
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_openat(
at: Option<FileDescriptor>,
@@ -142,9 +140,6 @@ pub fn sys_openat(
.map(|e| FileDescriptor::from(e as u32))
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_read(fd: FileDescriptor, data: &mut [u8]) -> Result<usize, Errno> {
Errno::from_syscall(unsafe {
@@ -169,9 +164,6 @@ pub fn sys_write(fd: FileDescriptor, data: &[u8]) -> Result<usize, Errno> {
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_fstatat(
at: Option<FileDescriptor>,
@@ -196,18 +188,15 @@ pub fn sys_fstatat(
/// System call
#[inline(always)]
pub unsafe fn sys_fork() -> Result<Option<Pid>, Errno> {
Errno::from_syscall(syscall!(SystemCall::Fork)).map(|res| {
Errno::from_syscall(syscall!(SystemCall::Fork)).and_then(|res| {
if res != 0 {
Some(unsafe { Pid::from_raw(res as u32) })
Pid::try_from(res as u32).map(Some)
} else {
None
Ok(None)
}
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_execve(pathname: &str, argv: &[&str]) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
@@ -221,23 +210,17 @@ pub fn sys_execve(pathname: &str, argv: &[&str]) -> Result<(), Errno> {
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_waitpid(pid: Pid, status: &mut i32) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::WaitPid,
argn!(pid.value()),
argn!(u32::from(pid)),
argp!(status as *mut i32)
)
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub fn sys_ioctl(
fd: FileDescriptor,
@@ -289,10 +272,10 @@ pub fn sys_ex_kill(pid: SignalDestination, signum: Signal) -> Result<(), Errno>
}
#[inline(always)]
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<usize, Errno> {
pub fn sys_ex_clone(entry: usize, stack: usize, arg: usize) -> Result<Tid, Errno> {
Errno::from_syscall(unsafe {
syscall!(SystemCall::Clone, argn!(entry), argn!(stack), argn!(arg))
})
}).map(|e| Tid::from(e as u32))
}
#[inline(always)]
@@ -304,8 +287,8 @@ pub fn sys_ex_thread_exit(status: ExitCode) -> ! {
}
#[inline(always)]
pub fn sys_ex_thread_wait(tid: u32) -> Result<ExitCode, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::WaitTid, argn!(tid)) })
pub fn sys_ex_thread_wait(tid: Tid) -> Result<ExitCode, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::WaitTid, argn!(u32::from(tid))) })
.map(|_| ExitCode::from(0))
}
@@ -356,27 +339,31 @@ pub fn sys_faccessat(
}
#[inline(always)]
pub fn sys_ex_gettid() -> u32 {
unsafe { syscall!(SystemCall::GetTid) as u32 }
pub fn sys_ex_gettid() -> Tid {
Tid::from(unsafe { syscall!(SystemCall::GetTid) as u32 })
}
#[inline(always)]
pub fn sys_getpid() -> Pid {
unsafe { Pid::from_raw(syscall!(SystemCall::GetPid) as u32) }
Pid::try_from(unsafe { syscall!(SystemCall::GetPid) as u32 }).unwrap()
}
#[inline(always)]
pub fn sys_getpgid(pid: Pid) -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::GetPgid, argn!(pid.value())) })
.map(|e| unsafe { Pid::from_raw(e as u32) })
pub fn sys_getpgid(pid: Option<Pid>) -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::GetPgid, argn!(Pid::from_option(pid))) })
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
pub fn sys_setpgid(pid: Pid, pgid: Pid) -> Result<Pid, Errno> {
pub fn sys_setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> Result<Pid, Errno> {
Errno::from_syscall(unsafe {
syscall!(SystemCall::SetPgid, argn!(pid.value()), argn!(pgid.value()))
syscall!(
SystemCall::SetPgid,
argn!(Pid::from_option(pid)),
argn!(Pid::from_option(pgid))
)
})
.map(|e| unsafe { Pid::from_raw(e as u32) })
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
@@ -404,7 +391,7 @@ pub fn sys_getgid() -> GroupId {
#[inline(always)]
pub fn sys_setsid() -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::SetSid) })
.map(|e| unsafe { Pid::from_raw(e as u32) })
.and_then(|e| Pid::try_from(e as u32))
}
#[inline(always)]
@@ -470,6 +457,10 @@ pub fn sys_mmap(
})
}
/// # Safety
///
/// System call
#[inline(always)]
pub unsafe fn sys_munmap(addr: usize, len: usize) -> Result<(), Errno> {
Errno::from_syscall_unit(syscall!(SystemCall::UnmapMemory, argn!(addr), argn!(len)))
+49
View File
@@ -24,6 +24,55 @@ pub struct ProgramArgs {
pub size: usize
}
// TODO utils
use core::fmt;
#[derive(Clone, Copy)]
pub struct FixedStr<const N: usize> {
len: usize,
data: [u8; N],
}
impl<const N: usize> FixedStr<N> {
pub const fn empty() -> Self {
Self {
len: 0,
data: [0; N]
}
}
pub fn copy_from_str(&mut self, src: &str) {
if src.len() > self.data.len() {
panic!("copy_from_str: src len > data len");
}
self.len = src.len();
self.data[..self.len].copy_from_slice(src.as_bytes());
}
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.data[..self.len])
}
}
}
impl<const N: usize> fmt::Debug for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"")?;
fmt::Display::fmt(self, f)?;
write!(f, "\"")
}
}
impl<const N: usize> fmt::Display for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &byte in &self.data[..self.len] {
write!(f, "{}", byte as char)?;
}
Ok(())
}
}
#[cfg(feature = "user")]
pub mod calls;
#[cfg(feature = "user")]
+66 -16
View File
@@ -12,6 +12,11 @@ pub struct ExitCode(i32);
#[repr(transparent)]
pub struct Pid(u32);
/// Wrapper type for thread ID
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
#[repr(transparent)]
pub struct Tid(u32);
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct Pgid(u32);
@@ -26,8 +31,8 @@ bitflags! {
bitflags! {
pub struct MemoryMap: u32 {
const BACKEND = 0x3 << 0;
const ANONYMOUS = 1 << 0;
const BACKEND = 0x3;
const ANONYMOUS = 1;
const SHARING = 0x3 << 2;
const PRIVATE = 1 << 2;
@@ -53,14 +58,15 @@ impl From<ExitCode> for i32 {
}
impl Pid {
/// Kernel idle process always has PID of zero
pub const IDLE: Self = Self(Self::KERNEL_BIT);
const KERNEL_BIT: u32 = 1 << 31;
const USER_MAX: u32 = 256;
/// Constructs an instance of user-space PID
pub const fn user(id: u32) -> Self {
assert!(id < 256, "PID is too high");
assert!(id < Self::USER_MAX, "PID is too high");
if id == 0 {
panic!("User PID cannot be zero");
}
Self(id)
}
@@ -83,18 +89,20 @@ impl Pid {
self.0 as u8
}
/// Returns bit value of this pid
pub const fn value(self) -> u32 {
self.0
pub fn from_option(m: Option<Self>) -> u32 {
if let Some(pid) = m {
u32::from(pid)
} else {
0
}
}
/// Constructs [Pid] from raw [u32] value
///
/// # Safety
///
/// Unsafe: does not check `num`
pub const unsafe fn from_raw(num: u32) -> Self {
Self(num)
pub fn to_option(m: u32) -> Option<Self> {
if m != 0 {
Some(Self::try_from(m).unwrap())
} else {
None
}
}
}
@@ -109,6 +117,26 @@ impl fmt::Debug for Pid {
}
}
impl TryFrom<u32> for Pid {
type Error = Errno;
fn try_from(raw: u32) -> Result<Pid, Errno> {
if raw & Self::KERNEL_BIT != 0 {
Ok(Pid::kernel(raw & !Self::KERNEL_BIT))
} else if raw != 0 && raw < Self::USER_MAX {
Ok(Pid::user(raw))
} else {
Err(Errno::InvalidArgument)
}
}
}
impl From<Pid> for u32 {
fn from(pid: Pid) -> u32 {
pid.0
}
}
impl TryFrom<Pid> for Pgid {
type Error = Errno;
@@ -132,3 +160,25 @@ impl From<Pgid> for u32 {
p.0
}
}
impl Tid {
pub const IDLE: Tid = Tid(0);
}
impl fmt::Debug for Tid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tid(#{})", self.0)
}
}
impl From<u32> for Tid {
fn from(p: u32) -> Tid {
Self(p)
}
}
impl From<Tid> for u32 {
fn from(p: Tid) -> u32 {
p.0
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ impl From<isize> for SignalDestination {
impl From<SignalDestination> for isize {
fn from(p: SignalDestination) -> isize {
match p {
SignalDestination::Process(pid) => pid.value() as isize,
SignalDestination::Process(pid) => u32::from(pid) as isize,
SignalDestination::Group(pgid) => -(u32::from(pgid) as isize),
SignalDestination::This => 0,
SignalDestination::All => -1
+12 -7
View File
@@ -1,5 +1,6 @@
// TODO split up this file
use crate::error::Errno;
use core::str::FromStr;
use core::fmt;
const AT_FDCWD: i32 = -2;
@@ -143,19 +144,23 @@ impl DirectoryEntry {
Self { name: [0; 64] }
}
pub fn from_str(i: &str) -> DirectoryEntry {
let mut res = DirectoryEntry { name: [0; 64] };
let bytes = i.as_bytes();
res.name[..bytes.len()].copy_from_slice(bytes);
res
}
pub fn as_str(&self) -> &str {
let zero = self.name.iter().position(|&c| c == 0).unwrap();
core::str::from_utf8(&self.name[..zero]).unwrap()
}
}
impl FromStr for DirectoryEntry {
type Err = Errno;
fn from_str(i: &str) -> Result<Self, Errno> {
let mut res = DirectoryEntry { name: [0; 64] };
let bytes = i.as_bytes();
res.name[..bytes.len()].copy_from_slice(bytes);
Ok(res)
}
}
impl fmt::Debug for DirectoryEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DirectoryEntry")
+3
View File
@@ -9,3 +9,6 @@ edition = "2021"
libsys = { path = "../libsys", features = ["user"] }
lazy_static = { version = "^1.4.0", features = ["spin_no_std"] }
memoffset = "^0.6.4"
[features]
verbose = []
+11 -7
View File
@@ -82,6 +82,7 @@ impl Zone {
MemoryAccess::READ | MemoryAccess::WRITE,
MemoryMap::ANONYMOUS | MemoryMap::PRIVATE,
)?;
#[cfg(feature = "verbose")]
trace_debug!("Zone::alloc({}) => {:#x}", size, pages);
let zone_ptr = pages as *mut Zone;
@@ -101,8 +102,9 @@ impl Zone {
}
unsafe fn free(zone: *mut Self) {
#[cfg(feature = "verbose")]
trace_debug!("Zone::free({:p})", zone);
sys_munmap(zone as usize, (&*zone).size + size_of::<Zone>())
sys_munmap(zone as usize, (*zone).size + size_of::<Zone>())
.expect("Failed to unmap heap pages");
}
@@ -165,7 +167,7 @@ unsafe fn alloc_from(list: &mut ZoneList, zone_size: usize, size: usize) -> *mut
if !ptr.is_null() {
return ptr;
}
zone = (&mut *zone).next;
zone = (*zone).next;
}
let zone = match Zone::alloc(zone_size) {
@@ -175,7 +177,7 @@ unsafe fn alloc_from(list: &mut ZoneList, zone_size: usize, size: usize) -> *mut
return null_mut();
}
};
list.add(&mut (&mut *zone).list);
list.add(&mut (*zone).list);
}
}
@@ -183,6 +185,7 @@ unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
assert!(layout.align() < 16);
let size = (layout.size() + 15) & !15;
#[cfg(feature = "verbose")]
trace_debug!("alloc({:?})", layout);
if size <= SMALL_ZONE_ELEM {
alloc_from(SMALL_ZONE_LIST.assume_init_mut(), SMALL_ZONE_SIZE, size)
@@ -196,6 +199,7 @@ unsafe impl GlobalAlloc for Allocator {
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
#[cfg(feature = "verbose")]
trace_debug!("free({:p}, {:?})", ptr, layout);
assert!(!ptr.is_null());
let mut block = ptr.sub(size_of::<Block>()) as *mut Block;
@@ -232,7 +236,7 @@ unsafe impl GlobalAlloc for Allocator {
if !next.is_null() && next_ref.flags & BLOCK_ALLOC == 0 {
next_ref.flags = 0;
if !next_ref.next.is_null() {
(&mut *(next_ref.next)).prev = block;
(*next_ref.next).prev = block;
}
block_ref.next = next_ref.next;
block_ref.size += (next_ref.size as usize + size_of::<Block>()) as u32;
@@ -241,15 +245,15 @@ unsafe impl GlobalAlloc for Allocator {
if block_ref.prev.is_null() && block_ref.next.is_null() {
let zone = (block as usize - size_of::<Zone>()) as *mut Zone;
assert_eq!((zone as usize) & 0xFFF, 0);
(&mut *zone).list.del();
(*zone).list.del();
Zone::free(zone);
}
}
}
#[alloc_error_handler]
fn alloc_error_handler(_layout: Layout) -> ! {
loop {}
fn alloc_error_handler(layout: Layout) -> ! {
panic!("Allocation failed: {:?}", layout);
}
#[global_allocator]
+12 -3
View File
@@ -1,6 +1,14 @@
use libsys::{debug::TraceLevel, ProgramArgs};
use alloc::vec::Vec;
use crate::trace;
use alloc::vec::Vec;
use libsys::{
debug::TraceLevel,
ProgramArgs,
};
mod passwd;
pub use passwd::UserInfo;
mod shadow;
pub use shadow::UserShadow;
static mut PROGRAM_ARGS: Vec<&'static str> = Vec::new();
@@ -10,12 +18,13 @@ pub fn args() -> &'static [&'static str] {
pub(crate) unsafe fn setup_env(arg: &ProgramArgs) {
for i in 0..arg.argc {
let base = core::ptr::read((arg.argv + i * 16 + 0) as *const *const u8);
let base = core::ptr::read((arg.argv + i * 16) as *const *const u8);
let len = core::ptr::read((arg.argv + i * 16 + 8) as *const usize);
let string = core::str::from_utf8(core::slice::from_raw_parts(base, len)).unwrap();
PROGRAM_ARGS.push(string);
}
#[cfg(feature = "verbose")]
trace!(TraceLevel::Debug, "args = {:?}", PROGRAM_ARGS);
}
+99
View File
@@ -0,0 +1,99 @@
use crate::io::{Read, read_line};
use core::str::FromStr;
use core::fmt;
use crate::trace_debug;
use crate::file::File;
use libsys::{FixedStr, stat::{UserId, GroupId}};
#[derive(Debug, Clone, Copy)]
pub struct UserInfo {
name: FixedStr<32>,
uid: UserId,
gid: GroupId,
home: FixedStr<64>,
shell: FixedStr<64>,
}
impl UserInfo {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn home(&self) -> &str {
self.home.as_str()
}
pub fn shell(&self) -> &str {
self.shell.as_str()
}
pub fn uid(&self) -> UserId {
self.uid
}
pub fn gid(&self) -> GroupId {
self.gid
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/passwd").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserInfo::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserInfo {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(":");
let name = iter.next().ok_or(())?;
let uid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(UserId::from)?;
let gid = iter
.next()
.ok_or(())
.and_then(|e| u32::from_str(e).map_err(|_| ()))
.map(GroupId::from)?;
let comment = iter.next().ok_or(())?;
let home = iter.next().ok_or(())?;
let shell = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
uid,
gid,
name: FixedStr::empty(),
home: FixedStr::empty(),
shell: FixedStr::empty(),
};
res.name.copy_from_str(&name);
res.home.copy_from_str(&home);
res.shell.copy_from_str(&shell);
Ok(res)
}
}
+67
View File
@@ -0,0 +1,67 @@
use crate::file::File;
use crate::io::{Read, read_line};
use core::str::FromStr;
use libsys::FixedStr;
#[derive(Debug, Clone, Copy)]
pub struct UserShadow {
name: FixedStr<32>,
password: FixedStr<64>,
}
impl UserShadow {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn password(&self) -> &str {
self.password.as_str()
}
pub fn find<F: Fn(&Self) -> bool>(pred: F) -> Result<Self, ()> {
let mut file = File::open("/etc/shadow").map_err(|_| ())?;
let mut buf = [0; 128];
loop {
let line = read_line(&mut file, &mut buf).map_err(|_| ())?;
if let Some(line) = line {
let ent = UserShadow::from_str(line)?;
if pred(&ent) {
return Ok(ent);
}
} else {
break;
}
}
Err(())
}
pub fn by_name(name: &str) -> Result<Self, ()> {
Self::find(|ent| ent.name() == name)
}
}
impl FromStr for UserShadow {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
let mut iter = s.split(':');
let name = iter.next().ok_or(())?;
let password = iter.next().ok_or(())?;
if iter.next().is_some() {
return Err(());
}
let mut res = Self {
name: FixedStr::empty(),
password: FixedStr::empty(),
};
res.name.copy_from_str(name);
res.password.copy_from_str(password);
Ok(res)
}
}
+24
View File
@@ -42,3 +42,27 @@ pub fn stat(pathname: &str) -> Result<Stat, Error> {
sys_fstatat(None, pathname, &mut buf, 0).unwrap();
Ok(buf)
}
// TODO use BufRead instead once it's implemented
pub(crate) fn read_line<'a, F: Read>(f: &mut F, buf: &'a mut [u8]) -> Result<Option<&'a str>, ()> {
let mut pos = 0;
loop {
if pos == buf.len() {
return Err(());
}
let count = f.read(&mut buf[pos..=pos]).map_err(|_| ())?;
if count == 0 {
if pos == 0 {
return Ok(None);
}
break;
}
if buf[pos] == b'\n' {
break;
}
pos += 1;
}
core::str::from_utf8(&buf[..pos]).map_err(|_| ()).map(Some)
}
+4 -2
View File
@@ -8,7 +8,8 @@ macro_rules! print {
#[macro_export]
macro_rules! println {
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)))
($($args:tt)+) => (print!("{}\n", format_args!($($args)+)));
() => (print!("\n"));
}
#[macro_export]
@@ -18,7 +19,8 @@ macro_rules! eprint {
#[macro_export]
macro_rules! eprintln {
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)))
($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+)));
() => (eprint!("\n"));
}
pub fn _print<T: Write>(out: fn() -> T, args: fmt::Arguments) {
+4 -3
View File
@@ -40,7 +40,8 @@ extern "C" fn _start(arg: &'static ProgramArgs) -> ! {
fn panic_handler(pi: &PanicInfo) -> ! {
// TODO unwind to send panic argument back to parent thread
// TODO print to stdout/stderr (if available)
let thread = thread::current();
trace!(TraceLevel::Error, "{:?} panicked: {:?}", thread, pi);
sys::sys_exit(ExitCode::from(-1));
// let thread = thread::current();
// trace!(TraceLevel::Error, "{:?} panicked: {:?}", thread, pi);
loop {}
// sys::sys_exit(ExitCode::from(-1));
}
+18 -18
View File
@@ -6,7 +6,7 @@ use core::fmt;
use core::mem::MaybeUninit;
use libsys::{
calls::{sys_ex_clone, sys_ex_gettid, sys_ex_signal, sys_ex_thread_exit, sys_ex_thread_wait},
proc::ExitCode,
proc::{ExitCode, Tid},
};
struct NativeData<F, T>
@@ -22,19 +22,19 @@ where
#[derive(Clone)]
pub struct Thread {
id: u32,
id: Tid,
}
pub type ThreadResult<T> = Result<T, Box<dyn Any + Send + Sync>>;
pub type ThreadPacket<T> = Arc<UnsafeCell<MaybeUninit<ThreadResult<T>>>>;
pub struct JoinHandle<T> {
native: u32,
native: Tid,
result: ThreadPacket<T>,
}
impl Thread {
pub const fn id(&self) -> u32 {
pub const fn id(&self) -> Tid {
self.id
}
}
@@ -60,16 +60,16 @@ impl<T> JoinHandle<T> {
}
unsafe fn init_common(signal_stack_pointer: *mut u8) {
let tid = sys_ex_gettid() as u64;
asm!("msr tpidr_el0, {:x}", in(reg) tid);
// let tid = u32::from(sys_ex_gettid()) as u64;
// asm!("msr tpidr_el0, {:x}", in(reg) tid);
// thread::current() should be valid at this point
sys_ex_signal(
signal::signal_handler as usize,
signal_stack_pointer as usize,
)
.unwrap();
// sys_ex_signal(
// signal::signal_handler as usize,
// signal_stack_pointer as usize,
// )
// .unwrap();
}
pub(crate) unsafe fn init_main() {
@@ -82,12 +82,12 @@ pub(crate) unsafe fn init_main() {
}
pub fn current() -> Thread {
let mut id: u64;
unsafe {
asm!("mrs {:x}, tpidr_el0", out(reg) id);
}
Thread { id: id as u32 }
todo!()
// let mut id: u64;
// unsafe {
// asm!("mrs {:x}, tpidr_el0", out(reg) id);
// }
// Thread { id: Tid::from(id as u32) }
}
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
@@ -136,7 +136,7 @@ where
result: result.clone(),
}));
sys_ex_clone(thread_entry::<F, T> as usize, stack, data as usize).unwrap() as u32
sys_ex_clone(thread_entry::<F, T> as usize, stack, data as usize).unwrap()
};
JoinHandle { native, result }
+6 -3
View File
@@ -13,9 +13,9 @@ path = "src/init/main.rs"
name = "shell"
path = "src/bin/shell.rs"
[[bin]]
name = "fuzzy"
path = "src/bin/fuzzy.rs"
# [[bin]]
# name = "fuzzy"
# path = "src/bin/fuzzy.rs"
[[bin]]
name = "ls"
@@ -37,3 +37,6 @@ path = "src/sbin/login.rs"
libusr = { path = "../libusr" }
libsys = { path = "../libsys" }
lazy_static = { version = "*", features = ["spin_no_std"] }
[features]
verbose = ["libusr/verbose"]
+1 -1
View File
@@ -30,7 +30,7 @@ fn main() -> i32 {
if args.len() == 1 {
if let Err(e) = do_cat(io::stdin()) {
eprintln!("{}: {:?}", ".", e);
eprintln!(".: {:?}", e);
res = -1;
}
} else {
+134 -132
View File
@@ -1,139 +1,141 @@
#![feature(asm)]
// #![feature(asm)]
#![no_std]
#![no_main]
#![allow(unused_macros)]
#![allow(dead_code)]
//
// #![allow(unused_macros)]
// #![allow(dead_code)]
//
#[macro_use]
extern crate libusr;
use libusr::sys::{abi::SystemCall, stat::Stat};
static mut STATE: u64 = 0;
macro_rules! syscall {
($num:expr) => {{
let mut res: usize;
asm!("svc #0", out("x0") res, in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x8") $num, options(nostack));
res
}};
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
let mut res: usize = $a0;
asm!("svc #0",
inout("x0") res, in("x1") $a1, in("x2") $a2,
in("x3") $a3, in("x4") $a4, in("x8") $num, options(nostack));
res
}};
}
/// Integer/size argument
macro_rules! argn {
($a:expr) => {
$a as usize
};
}
/// Pointer/base argument
macro_rules! argp {
($a:expr) => {
$a as usize
};
}
fn random_set_seed(seed: u64) {
unsafe { STATE = seed; }
}
fn random_u64() -> u64 {
let mut x = unsafe { STATE };
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
unsafe {
STATE = x;
}
x
}
fn random_ascii_char() -> u8 {
((random_u64() % (0x7F - 0x20)) as u8) + 0x20
}
fn random_str_range(buf: &mut [u8], min: usize, max: usize) -> &str {
let max = core::cmp::min(buf.len(), max);
assert!(max > min);
let len = ((random_u64() as usize) % (max - min)) + min;
for c in buf[..len].iter_mut() {
*c = random_ascii_char();
}
core::str::from_utf8(&buf[..len]).unwrap()
}
fn random_str(buf: &mut [u8]) -> &str {
random_str_range(buf, 0, buf.len())
}
fn random_bytes(buf: &mut [u8]) {
for byte in buf.iter_mut() {
*byte = (random_u64() & 0xFF) as u8;
}
}
//
// use libusr::sys::{abi::SystemCall, stat::Stat};
//
// static mut STATE: u64 = 0;
//
// macro_rules! syscall {
// ($num:expr) => {{
// let mut res: usize;
// asm!("svc #0", out("x0") res, in("x8") $num, options(nostack));
// res
// }};
// ($num:expr, $a0:expr) => {{
// let mut res: usize = $a0;
// asm!("svc #0",
// inout("x0") res,
// in("x8") $num, options(nostack));
// res
// }};
// ($num:expr, $a0:expr, $a1:expr) => {{
// let mut res: usize = $a0;
// asm!("svc #0",
// inout("x0") res, in("x1") $a1,
// in("x8") $num, options(nostack));
// res
// }};
// ($num:expr, $a0:expr, $a1:expr, $a2:expr) => {{
// let mut res: usize = $a0;
// asm!("svc #0",
// inout("x0") res, in("x1") $a1, in("x2") $a2,
// in("x8") $num, options(nostack));
// res
// }};
// ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {{
// let mut res: usize = $a0;
// asm!("svc #0",
// inout("x0") res, in("x1") $a1, in("x2") $a2,
// in("x3") $a3, in("x8") $num, options(nostack));
// res
// }};
// ($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
// let mut res: usize = $a0;
// asm!("svc #0",
// inout("x0") res, in("x1") $a1, in("x2") $a2,
// in("x3") $a3, in("x4") $a4, in("x8") $num, options(nostack));
// res
// }};
// }
//
// /// Integer/size argument
// macro_rules! argn {
// ($a:expr) => {
// $a as usize
// };
// }
// /// Pointer/base argument
// macro_rules! argp {
// ($a:expr) => {
// $a as usize
// };
// }
//
// fn random_set_seed(seed: u64) {
// unsafe { STATE = seed; }
// }
//
// fn random_u64() -> u64 {
// let mut x = unsafe { STATE };
// x ^= x << 13;
// x ^= x >> 7;
// x ^= x << 17;
// unsafe {
// STATE = x;
// }
// x
// }
//
// fn random_ascii_char() -> u8 {
// ((random_u64() % (0x7F - 0x20)) as u8) + 0x20
// }
//
// fn random_str_range(buf: &mut [u8], min: usize, max: usize) -> &str {
// let max = core::cmp::min(buf.len(), max);
// assert!(max > min);
// let len = ((random_u64() as usize) % (max - min)) + min;
// for c in buf[..len].iter_mut() {
// *c = random_ascii_char();
// }
// core::str::from_utf8(&buf[..len]).unwrap()
// }
//
// fn random_str(buf: &mut [u8]) -> &str {
// random_str_range(buf, 0, buf.len())
// }
//
// fn random_bytes(buf: &mut [u8]) {
// for byte in buf.iter_mut() {
// *byte = (random_u64() & 0xFF) as u8;
// }
// }
//
#[no_mangle]
fn main() -> i32 {
let seed = libusr::sys::sys_ex_getcputime().unwrap().as_nanos() as u64 / 13;
println!("Using seed: {:#x}", seed);
random_set_seed(seed);
let mut buf = [0; 256];
// Test sys_ex_getcputime()
let mut prev_time = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
for _ in 0..1000 {
let t = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
assert!(t >= prev_time);
prev_time = t;
}
// Test non-utf8 input fed into syscalls expecting strings
// let old_signal = signal::set_handler(Signal::InvalidSystemCall, SignalHandler::Ignore);
for _ in 0..10000 {
random_bytes(&mut buf);
let mut stat = Stat::default();
unsafe {
syscall!(SystemCall::FileStatus.repr(), (-2i32) as usize, buf.as_mut_ptr() as usize, buf.len(), (&mut stat) as *mut _ as usize);
}
}
// signal::set_handler(Signal::InvalidSystemCall, old_signal);
0
loop {}
}
// let seed = libusr::sys::sys_ex_getcputime().unwrap().as_nanos() as u64 / 13;
// println!("Using seed: {:#x}", seed);
// random_set_seed(seed);
//
// let mut buf = [0; 256];
//
// // Test sys_ex_getcputime()
// let mut prev_time = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
// for _ in 0..1000 {
// let t = libusr::sys::sys_ex_getcputime().unwrap().as_nanos();
// assert!(t >= prev_time);
// prev_time = t;
// }
//
// // Test non-utf8 input fed into syscalls expecting strings
// // let old_signal = signal::set_handler(Signal::InvalidSystemCall, SignalHandler::Ignore);
// for _ in 0..10000 {
// random_bytes(&mut buf);
// let mut stat = Stat::default();
//
// unsafe {
// syscall!(SystemCall::FileStatus.repr(), (-2i32) as usize, buf.as_mut_ptr() as usize, buf.len(), (&mut stat) as *mut _ as usize);
// }
// }
// // signal::set_handler(Signal::InvalidSystemCall, old_signal);
//
// 0
// }
+2 -2
View File
@@ -27,7 +27,7 @@ fn line_print(off: usize, line: &[u8]) {
print!(".");
}
}
println!("");
println!();
}
fn do_hexd<F: Read>(mut fd: F) -> Result<(), io::Error> {
@@ -53,7 +53,7 @@ fn main() -> i32 {
if args.len() == 1 {
if let Err(e) = do_hexd(io::stdin()) {
eprintln!("{}: {:?}", ".", e);
eprintln!(".: {:?}", e);
res = -1;
}
} else {
+1 -1
View File
@@ -57,7 +57,7 @@ fn main() -> i32 {
if args.len() == 1 {
if let Err(e) = list_directory(".") {
eprintln!("{}: {:?}", ".", e);
eprintln!(".: {:?}", e);
res = -1;
}
} else {
+8 -10
View File
@@ -9,7 +9,7 @@ use alloc::{borrow::ToOwned, vec::Vec};
use libusr::io::{self, Read};
use libusr::signal::{self, SignalHandler};
use libusr::sys::{
proc::Pid, sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid,
sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid,
sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal,
};
@@ -22,13 +22,11 @@ fn cmd_cd(args: &[&str]) -> ExitCode {
if args.len() != 2 {
eprintln!("Usage: cd DIR");
ExitCode::from(-1)
} else if let Err(err) = sys_chdir(args[1]) {
eprintln!("{}: {:?}", args[1], err);
ExitCode::from(-1)
} else {
if let Err(err) = sys_chdir(args[1]) {
eprintln!("{}: {:?}", args[1], err);
ExitCode::from(-1)
} else {
ExitCode::from(0)
}
ExitCode::from(0)
}
}
@@ -67,11 +65,11 @@ fn execute(line: &str) -> Result<ExitCode, Errno> {
if let Some(pid) = unsafe { sys_fork()? } {
let mut status = 0;
sys_waitpid(pid, &mut status)?;
let pgid = sys_getpgid(unsafe { Pid::from_raw(0) }).unwrap();
let pgid = sys_getpgid(None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(ExitCode::from(status))
} else {
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(&filename, &args).unwrap();
sys_exit(ExitCode::from(-1));
@@ -84,7 +82,7 @@ fn main() -> i32 {
let mut stdin = io::stdin();
signal::set_handler(Signal::Interrupt, SignalHandler::Ignore);
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
loop {
+29 -13
View File
@@ -9,6 +9,9 @@ use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitp
#[no_mangle]
fn main() -> i32 {
println!("Test");
trace_debug!("test!");
sys_mount(
"/dev",
&MountOptions {
@@ -17,19 +20,32 @@ fn main() -> i32 {
},
)
.expect("Failed to mount devfs");
sys_mount(
"/sys",
&MountOptions {
device: None,
fs: Some("sysfs"),
},
)
.expect("Failed to mount sysfs");
if let Some(pid) = unsafe { sys_fork().unwrap() } {
let mut status = 0;
sys_waitpid(pid, &mut status).unwrap();
println!("Process {:?} exited with status {}", pid, status);
let pid = unsafe { sys_fork().unwrap() };
loop {
unsafe {
asm!("nop");
}
}
} else {
sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap();
loop {}
}
trace_debug!("fork returned {:?}", pid);
loop {}
// loop {}
// if let Some(pid) = unsafe { sys_fork().unwrap() } {
// let mut status = 0;
// sys_waitpid(pid, &mut status).unwrap();
// println!("Process {:?} exited with status {}", pid, status);
// loop {
// }
// } else {
// sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap();
// unreachable!();
// }
}
+29 -13
View File
@@ -11,11 +11,11 @@ use libsys::{
},
error::Errno,
ioctl::IoctlCmd,
proc::Pid,
stat::{FileDescriptor, FileMode, GroupId, OpenFlags, UserId},
termios::{Termios, TermiosLflag},
};
use libusr::{env, io};
use libusr::{env::{self, UserInfo, UserShadow}, io};
use core::str::FromStr;
struct HiddenInput {
fd: FileDescriptor,
@@ -74,23 +74,28 @@ fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, Errno> {
}
}
fn login_as(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
fn login(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
if let Some(pid) = unsafe { sys_fork() }? {
let mut status = 0;
sys_waitpid(pid, &mut status).ok();
let pgid = sys_getpgid(unsafe { Pid::from_raw(0) }).unwrap();
let pgid = sys_getpgid(None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(())
} else {
sys_setuid(uid).expect("setuid failed");
sys_setgid(gid).expect("setgid failed");
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
let pgid = sys_setpgid(None, None).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(shell, &[shell]).expect("execve() failed");
panic!();
}
}
fn login_as(name: &str) -> Result<(), Errno> {
let ent = UserInfo::by_name(name).map_err(|_| Errno::DoesNotExist)?;
login(ent.uid(), ent.gid(), ent.shell())
}
// TODO baud rate and misc port settings
#[no_mangle]
fn main() -> i32 {
@@ -133,15 +138,26 @@ fn main() -> i32 {
loop {
print!("login: ");
let username = readline(FileDescriptor::STDIN, &mut user_buf).expect("Login read failed");
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if username == "root" && password == "toor" {
login_as(UserId::from(0), GroupId::from(0), "/bin/shell").unwrap();
let shadow = match UserShadow::by_name(username) {
Ok(e) => e,
Err(_) => continue
};
if !shadow.password().is_empty() {
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if password != shadow.password() {
eprintln!("Incorrect password");
continue;
}
}
login_as(username);
}
}