feat: basic IPC
This commit is contained in:
Generated
+15
-1
@@ -2,6 +2,13 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dusk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dusk"
|
||||
version = "0.1.0"
|
||||
@@ -10,7 +17,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "init"
|
||||
name = "dusk-sys"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
@@ -18,3 +25,10 @@ name = "limine"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29363c0f37e66e18575fadf7141c56ee7ea04ae5fecbeb25eff303f77af203a9"
|
||||
|
||||
[[package]]
|
||||
name = "omega3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dusk-sys",
|
||||
]
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
members = [".", "userspace/init"]
|
||||
members = [".", "userspace/*"]
|
||||
|
||||
[dependencies]
|
||||
limine = "0.6.5"
|
||||
|
||||
@@ -67,10 +67,12 @@ prepare-bin-files:
|
||||
mkdir -p ${INITRAMFS_PATH}
|
||||
|
||||
compile-user:
|
||||
RUSTFLAGS="-C relocation-model=static" cargo build --package init ${USERSPACE_CARGO_OPTS}
|
||||
RUSTFLAGS="-C relocation-model=static" cargo build --package omega3 ${USERSPACE_CARGO_OPTS}
|
||||
RUSTFLAGS="-C relocation-model=static" cargo build --package client ${USERSPACE_CARGO_OPTS}
|
||||
|
||||
copy-initramfs-files: compile-user
|
||||
cp -v target/${ARCH}-unknown-none/${MODE}/init ${INITRAMFS_PATH}/init.elf
|
||||
cp -v target/${ARCH}-unknown-none/${MODE}/omega3 ${INITRAMFS_PATH}/omega3.elf
|
||||
cp -v target/${ARCH}-unknown-none/${MODE}/client ${INITRAMFS_PATH}/client.elf
|
||||
|
||||
compile-initramfs: copy-initramfs-files
|
||||
(cd ${INITRAMFS_PATH} && find . -mindepth 1 | cpio -o -H newc) > ${ARTIFACTS_PATH}/initramfs.img
|
||||
|
||||
+13
-29
@@ -12,12 +12,9 @@ mod platform;
|
||||
mod syscall;
|
||||
mod task;
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
use crate::{
|
||||
debug::serial,
|
||||
memory::{AddressSpace, KernelStackPool, MemoryRegionKind, UserStack},
|
||||
task::tcb::Tcb,
|
||||
memory::{AddressSpace, KernelStackPool, MemoryRegionKind},
|
||||
};
|
||||
|
||||
pub struct KernelHandoff {
|
||||
@@ -119,9 +116,6 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
MemoryRegionKind::BootloaderReclaimable,
|
||||
);
|
||||
|
||||
let init_code = format::cpio::find_file(boot_info.initramfs.data(), "init.elf")
|
||||
.expect("Failed to load init program from initramfs");
|
||||
|
||||
println!("Initializing local ACPI...",);
|
||||
|
||||
let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
|
||||
@@ -139,35 +133,25 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! {
|
||||
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
|
||||
.expect("failed to initialize interrupt controller");
|
||||
|
||||
let user_kernel_stack = kernel_stack_pool
|
||||
.allocate(&mut address_space, &mut allocator)
|
||||
.expect("failed to allocate task kernel stack");
|
||||
|
||||
let mut user_address_space = address_space
|
||||
.new_user(&mut allocator)
|
||||
.expect("failed to create user address space");
|
||||
|
||||
let image = task::loader::load_elf(
|
||||
init_code,
|
||||
&mut user_address_space,
|
||||
task::bootstrap::spawn(
|
||||
"omega3.elf",
|
||||
boot_info.initramfs.data(),
|
||||
&mut address_space,
|
||||
&mut allocator,
|
||||
direct_map,
|
||||
&mut kernel_stack_pool,
|
||||
);
|
||||
|
||||
let user_stack = UserStack::allocate(&mut user_address_space, &mut allocator)
|
||||
.expect("failed to allocate user stack");
|
||||
let task = Tcb::new_user(
|
||||
0, // overwritten by add_task for now
|
||||
user_address_space,
|
||||
user_kernel_stack,
|
||||
image.expect("Failed to load elf"),
|
||||
user_stack.top(),
|
||||
task::bootstrap::spawn(
|
||||
"client.elf",
|
||||
boot_info.initramfs.data(),
|
||||
&mut address_space,
|
||||
&mut allocator,
|
||||
direct_map,
|
||||
&mut kernel_stack_pool,
|
||||
);
|
||||
|
||||
task::scheduler::add_task(task).expect("scheduler is full");
|
||||
task::scheduler::start();
|
||||
|
||||
hcf();
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
|
||||
@@ -176,6 +176,7 @@ impl KernelStack {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserStack {
|
||||
mapping: StackMapping,
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ pub enum SyscallNumber {
|
||||
Yield = 1,
|
||||
Exit = 2,
|
||||
Write = 3,
|
||||
Send = 4,
|
||||
Recv = 5,
|
||||
}
|
||||
|
||||
impl TryFrom<u64> for SyscallNumber {
|
||||
@@ -28,6 +30,8 @@ impl TryFrom<u64> for SyscallNumber {
|
||||
1 => Ok(Self::Yield),
|
||||
2 => Ok(Self::Exit),
|
||||
3 => Ok(Self::Write),
|
||||
4 => Ok(Self::Send),
|
||||
5 => Ok(Self::Recv),
|
||||
_ => Err(Status::InvalidArgument),
|
||||
}
|
||||
}
|
||||
@@ -42,6 +46,10 @@ pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64,
|
||||
SyscallNumber::Write => {
|
||||
sys_write(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
||||
}
|
||||
SyscallNumber::Send => sys_send(arg0 as usize, arg1 as usize, arg2 as usize),
|
||||
SyscallNumber::Recv => {
|
||||
sys_recv(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
+76
-1
@@ -1,4 +1,7 @@
|
||||
use crate::memory::{VirtualAddr, copy_from_user, copy_val_to_user};
|
||||
use crate::{
|
||||
memory::{VirtualAddr, copy_from_user, copy_to_user, copy_val_to_user},
|
||||
task::tcb::{BlockReason, MAX_MSG_SIZE, Message},
|
||||
};
|
||||
|
||||
use super::Status;
|
||||
|
||||
@@ -31,3 +34,75 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
|
||||
if len > MAX_MSG_SIZE {
|
||||
return Err(Status::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut msg_buf = [0u8; MAX_MSG_SIZE];
|
||||
copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])?;
|
||||
|
||||
let dest_task = crate::task::scheduler::get_task_mut(dest_task_id).ok_or(Status::NoSuchTask)?;
|
||||
let sender = crate::task::scheduler::current();
|
||||
|
||||
let msg = Message {
|
||||
sender,
|
||||
length: len,
|
||||
data: msg_buf,
|
||||
};
|
||||
|
||||
if !dest_task.mailbox.push(msg) {
|
||||
return Err(Status::OutOfMemory);
|
||||
}
|
||||
|
||||
if matches!(
|
||||
dest_task.state,
|
||||
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv { .. })
|
||||
) {
|
||||
crate::task::scheduler::unblock(dest_task_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sys_recv(
|
||||
out_ptr: usize,
|
||||
max_len: usize,
|
||||
out_actual_len: usize,
|
||||
out_sender: usize,
|
||||
) -> Result<(), Status> {
|
||||
if out_ptr == 0 {
|
||||
return Err(Status::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current())
|
||||
.ok_or(Status::NoSuchTask)?;
|
||||
|
||||
if current_task.mailbox.len == 0 {
|
||||
crate::arch::restore_interrupts(interrupt_state);
|
||||
crate::task::scheduler::block_current(BlockReason::Recv { ep: 0 });
|
||||
interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||
}
|
||||
|
||||
// if we blocked, we will wake up when the mailbox is non-empty
|
||||
|
||||
let msg = current_task.mailbox.pop().ok_or(Status::NoSuchTask)?;
|
||||
|
||||
copy_to_user(
|
||||
VirtualAddr::new(out_ptr),
|
||||
&msg.data[..msg.length.min(max_len)],
|
||||
)?;
|
||||
if out_actual_len != 0 {
|
||||
copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?;
|
||||
}
|
||||
if out_sender != 0 {
|
||||
copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?;
|
||||
}
|
||||
|
||||
crate::arch::restore_interrupts(interrupt_state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
use crate::{
|
||||
format,
|
||||
memory::{
|
||||
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, PagePermissions, UserStack,
|
||||
VirtualAddr,
|
||||
},
|
||||
println,
|
||||
task::tcb::Tcb,
|
||||
};
|
||||
|
||||
pub fn spawn(
|
||||
name: &str,
|
||||
initramfs: &[u8],
|
||||
kernel_as: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
direct_map: DirectMap,
|
||||
stacks: &mut crate::memory::KernelStackPool,
|
||||
) -> usize {
|
||||
let bytes = format::cpio::find_file(initramfs, name)
|
||||
.unwrap_or_else(|| panic!("{name} missing from initramfs"));
|
||||
let kernel_stack = stacks
|
||||
.allocate(kernel_as, allocator)
|
||||
.expect("kernel stack allocation failed");
|
||||
|
||||
let mut address_space = kernel_as
|
||||
.new_user(allocator)
|
||||
.expect("address space allocation failed");
|
||||
let user_stack =
|
||||
UserStack::allocate(&mut address_space, allocator).expect("user stack allocation failed");
|
||||
|
||||
let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF");
|
||||
|
||||
let task = Tcb::new_user(0, address_space, kernel_stack, entry, user_stack.top());
|
||||
crate::task::scheduler::add_task(task).expect("scheduler is full")
|
||||
}
|
||||
|
||||
pub fn root(
|
||||
initramfs: &[u8],
|
||||
kernel_as: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
direct_map: DirectMap,
|
||||
stacks: &mut crate::memory::KernelStackPool,
|
||||
) {
|
||||
spawn(
|
||||
"omega3.elf",
|
||||
initramfs,
|
||||
kernel_as,
|
||||
allocator,
|
||||
direct_map,
|
||||
stacks,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ElfLoadError {
|
||||
AddressTranslationFailed,
|
||||
FailedToMapSegment,
|
||||
AddressOverflow,
|
||||
InvalidStack,
|
||||
OutOfMemory,
|
||||
InvalidElf,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||
// on x86_64, we only support ELFs that are either 32 bit x86 or 64 bit x86
|
||||
matches!(
|
||||
elf.machine(),
|
||||
format::elf::ElfIsa::X86 | format::elf::ElfIsa::Amd64
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn load_elf(
|
||||
bytes: &[u8],
|
||||
user_address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
direct_map: DirectMap,
|
||||
) -> Result<VirtualAddr, ElfLoadError> {
|
||||
let program = format::elf::Elf::parse(bytes).map_err(|_| ElfLoadError::InvalidElf)?;
|
||||
if !is_loadable(&program) {
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
for header in program
|
||||
.program_headers()
|
||||
.map_err(|_| ElfLoadError::InvalidElf)?
|
||||
{
|
||||
println!("Processing program header: {:?}", header);
|
||||
let header = header.map_err(|_| ElfLoadError::InvalidElf)?;
|
||||
|
||||
if header.file_size > header.memory_size {
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
match header.segment_type {
|
||||
format::elf::ProgramHeaderType::Load => {
|
||||
// TODO: give a fuck about alignment
|
||||
// TODO: handle program segments that overlap
|
||||
let segment_start = header.virtual_address as usize;
|
||||
if (program.entry() >= segment_start
|
||||
&& program.entry() < segment_start + header.memory_size as usize)
|
||||
&& header.flags & 0x01 == 0
|
||||
{
|
||||
// entry is within NX segment
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
let page_start = segment_start & !(FRAME_SIZE - 1);
|
||||
let page_offset = segment_start - page_start;
|
||||
|
||||
let mapped_length = page_offset
|
||||
.checked_add(header.memory_size as usize)
|
||||
.ok_or(ElfLoadError::AddressOverflow)?
|
||||
.div_ceil(FRAME_SIZE)
|
||||
* FRAME_SIZE;
|
||||
|
||||
let frame_count = mapped_length / FRAME_SIZE;
|
||||
|
||||
let executable = header.flags & 0x01 != 0;
|
||||
let writable = header.flags & 0x02 != 0;
|
||||
// TODO: support only-executable segments
|
||||
// let readable = header.flags & 0x04 != 0;
|
||||
|
||||
for i in 0..frame_count {
|
||||
let frame = allocator.alloc().ok_or(ElfLoadError::OutOfMemory)?;
|
||||
|
||||
println!(
|
||||
"Mapping code frame: {:X?} to {:X?}",
|
||||
frame,
|
||||
page_start + i * FRAME_SIZE
|
||||
);
|
||||
|
||||
if user_address_space
|
||||
.map(
|
||||
frame.frame_address().start_address(),
|
||||
VirtualAddr::new(page_start + i * FRAME_SIZE),
|
||||
PagePermissions::new(writable, executable, true),
|
||||
allocator,
|
||||
memory::CachePolicy::WriteBack,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
unsafe { allocator.dealloc(frame) };
|
||||
return Err(ElfLoadError::FailedToMapSegment);
|
||||
}
|
||||
|
||||
let _ = frame.into_raw();
|
||||
}
|
||||
|
||||
let mut copied = 0;
|
||||
|
||||
while copied < header.file_size as usize {
|
||||
let destination = VirtualAddr::new(segment_start + copied);
|
||||
let physical = user_address_space
|
||||
.to_physical(destination)
|
||||
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||
let direct_mapped = direct_map
|
||||
.translate(physical)
|
||||
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||
|
||||
let page_remaining = FRAME_SIZE - destination.as_usize() % FRAME_SIZE;
|
||||
let copy_length = page_remaining.min(header.file_size as usize - copied);
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
program
|
||||
.bytes()
|
||||
.as_ptr()
|
||||
.add(header.file_offset as usize + copied),
|
||||
direct_mapped.as_mut_ptr(),
|
||||
copy_length,
|
||||
);
|
||||
}
|
||||
|
||||
copied += copy_length;
|
||||
}
|
||||
}
|
||||
format::elf::ProgramHeaderType::GnuStack => {
|
||||
// if the stack is NOT R/W NX, we refuse to map it
|
||||
if header.flags != 6 {
|
||||
return Err(ElfLoadError::InvalidStack);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(VirtualAddr::new(program.entry()))
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
use crate::{
|
||||
format,
|
||||
memory::{
|
||||
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, OwnedFrame, PagePermissions,
|
||||
VirtualAddr,
|
||||
},
|
||||
println,
|
||||
};
|
||||
|
||||
const MAX_LOAD_SEGMENTS: usize = 32;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LoadedRegion {
|
||||
start: VirtualAddr,
|
||||
mapped_pages: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoadedImage {
|
||||
pub entry: VirtualAddr,
|
||||
regions: [LoadedRegion; MAX_LOAD_SEGMENTS],
|
||||
region_count: usize,
|
||||
}
|
||||
|
||||
impl LoadedImage {
|
||||
/// # Safety
|
||||
/// The supplied address space must contain this image's original mappings.
|
||||
/// Its frames must be exclusively owned by this image and no longer in use.
|
||||
pub unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) {
|
||||
for region in self.regions[..self.region_count].iter().rev() {
|
||||
for page in (0..region.mapped_pages).rev() {
|
||||
let address = VirtualAddr::new(region.start.as_usize() + page * FRAME_SIZE);
|
||||
let frame = unsafe {
|
||||
address_space
|
||||
.unmap(address, allocator)
|
||||
.expect("loaded image mapping was unexpectedly missing")
|
||||
};
|
||||
unsafe { allocator.dealloc(OwnedFrame::from_raw(frame)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ElfLoadError {
|
||||
AddressTranslationFailed,
|
||||
FailedToMapSegment,
|
||||
AddressOverflow,
|
||||
InvalidStack,
|
||||
OutOfMemory,
|
||||
InvalidElf,
|
||||
TooManyLoadSegments,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||
// on x86_64, we only support ELFs that are either 32 bit x86 or 64 bit x86
|
||||
matches!(
|
||||
elf.machine(),
|
||||
format::elf::ElfIsa::X86 | format::elf::ElfIsa::Amd64
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
fn is_loadable(elf: &format::elf::Elf) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn load_elf(
|
||||
bytes: &[u8],
|
||||
user_address_space: &mut AddressSpace,
|
||||
allocator: &mut FrameAllocator,
|
||||
direct_map: DirectMap,
|
||||
) -> Result<LoadedImage, ElfLoadError> {
|
||||
let program = format::elf::Elf::parse(bytes).map_err(|_| ElfLoadError::InvalidElf)?;
|
||||
if !is_loadable(&program) {
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
let mut image = LoadedImage {
|
||||
entry: VirtualAddr::new(program.entry()),
|
||||
regions: core::array::from_fn(|_| LoadedRegion {
|
||||
start: VirtualAddr::new(0),
|
||||
mapped_pages: 0,
|
||||
}),
|
||||
region_count: 0,
|
||||
};
|
||||
|
||||
let result = (|| {
|
||||
for header in program
|
||||
.program_headers()
|
||||
.map_err(|_| ElfLoadError::InvalidElf)?
|
||||
{
|
||||
println!("Processing program header: {:?}", header);
|
||||
let header = header.map_err(|_| ElfLoadError::InvalidElf)?;
|
||||
|
||||
if header.file_size > header.memory_size {
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
match header.segment_type {
|
||||
format::elf::ProgramHeaderType::Load => {
|
||||
if image.region_count == MAX_LOAD_SEGMENTS {
|
||||
return Err(ElfLoadError::TooManyLoadSegments);
|
||||
}
|
||||
// TODO: give a fuck about alignment
|
||||
// TODO: handle program segments that overlap
|
||||
let segment_start = header.virtual_address as usize;
|
||||
if (program.entry() >= segment_start
|
||||
&& program.entry() < segment_start + header.memory_size as usize)
|
||||
&& header.flags & 0x01 == 0
|
||||
{
|
||||
// entry is within NX segment
|
||||
return Err(ElfLoadError::InvalidElf);
|
||||
}
|
||||
|
||||
let page_start = segment_start & !(FRAME_SIZE - 1);
|
||||
let page_offset = segment_start - page_start;
|
||||
|
||||
let mapped_length = page_offset
|
||||
.checked_add(header.memory_size as usize)
|
||||
.ok_or(ElfLoadError::AddressOverflow)?
|
||||
.div_ceil(FRAME_SIZE)
|
||||
* FRAME_SIZE;
|
||||
|
||||
let frame_count = mapped_length / FRAME_SIZE;
|
||||
|
||||
let executable = header.flags & 0x01 != 0;
|
||||
let writable = header.flags & 0x02 != 0;
|
||||
// TODO: support only-executable segments
|
||||
// let readable = header.flags & 0x04 != 0;
|
||||
|
||||
let region = &mut image.regions[image.region_count];
|
||||
region.start = VirtualAddr::new(page_start);
|
||||
image.region_count += 1;
|
||||
|
||||
for i in 0..frame_count {
|
||||
let frame = allocator.alloc().ok_or(ElfLoadError::OutOfMemory)?;
|
||||
|
||||
println!(
|
||||
"Mapping code frame: {:X?} to {:X?}",
|
||||
frame,
|
||||
page_start + i * FRAME_SIZE
|
||||
);
|
||||
|
||||
if user_address_space
|
||||
.map(
|
||||
frame.frame_address().start_address(),
|
||||
VirtualAddr::new(page_start + i * FRAME_SIZE),
|
||||
PagePermissions::new(writable, executable, true),
|
||||
allocator,
|
||||
memory::CachePolicy::WriteBack,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
unsafe { allocator.dealloc(frame) };
|
||||
return Err(ElfLoadError::FailedToMapSegment);
|
||||
}
|
||||
|
||||
let _ = frame.into_raw();
|
||||
region.mapped_pages += 1;
|
||||
}
|
||||
|
||||
let mut copied = 0;
|
||||
|
||||
while copied < header.file_size as usize {
|
||||
let destination = VirtualAddr::new(segment_start + copied);
|
||||
let physical = user_address_space
|
||||
.to_physical(destination)
|
||||
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||
let direct_mapped = direct_map
|
||||
.translate(physical)
|
||||
.ok_or(ElfLoadError::AddressTranslationFailed)?;
|
||||
|
||||
let page_remaining = FRAME_SIZE - destination.as_usize() % FRAME_SIZE;
|
||||
let copy_length = page_remaining.min(header.file_size as usize - copied);
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
program
|
||||
.bytes()
|
||||
.as_ptr()
|
||||
.add(header.file_offset as usize + copied),
|
||||
direct_mapped.as_mut_ptr(),
|
||||
copy_length,
|
||||
);
|
||||
}
|
||||
|
||||
copied += copy_length;
|
||||
}
|
||||
}
|
||||
format::elf::ProgramHeaderType::GnuStack => {
|
||||
// if the stack is NOT R/W NX, we refuse to map it
|
||||
if header.flags != 6 {
|
||||
return Err(ElfLoadError::InvalidStack);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
if let Err(error) = result {
|
||||
// Only pages created by this load are recorded; none have been handed to a task.
|
||||
unsafe { image.destroy(user_address_space, allocator) };
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
pub mod loader;
|
||||
pub mod bootstrap;
|
||||
pub mod scheduler;
|
||||
pub mod tcb;
|
||||
|
||||
+61
-2
@@ -4,7 +4,7 @@ use crate::{
|
||||
arch::ThreadContext,
|
||||
memory::{AddressSpace, VirtualAddr},
|
||||
println,
|
||||
task::tcb::{ExitReason, Tcb, ThreadState},
|
||||
task::tcb::{BlockReason, ExitReason, Tcb, ThreadState},
|
||||
};
|
||||
|
||||
const MAX_TASKS: usize = 32;
|
||||
@@ -42,7 +42,7 @@ impl Scheduler {
|
||||
previous_context: prev_ctx,
|
||||
next_context: next_ctx,
|
||||
next_address_space: next_addr_space,
|
||||
next_kernel_stack: next_kernel_stack,
|
||||
next_kernel_stack,
|
||||
activate_address_space: unsafe { *next_addr_space != *prev_addr_space },
|
||||
}
|
||||
}
|
||||
@@ -172,6 +172,65 @@ pub fn start() -> ! {
|
||||
panic!("scheduler returned to bootstrap context");
|
||||
}
|
||||
|
||||
pub fn get_task(id: TaskId) -> Option<&'static Tcb> {
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
scheduler.tasks.get(id).and_then(Option::as_ref)
|
||||
}
|
||||
|
||||
pub fn get_task_mut(id: TaskId) -> Option<&'static mut Tcb> {
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
scheduler.tasks.get_mut(id).and_then(Option::as_mut)
|
||||
}
|
||||
|
||||
pub fn current() -> TaskId {
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
scheduler.current.expect("no current task")
|
||||
}
|
||||
|
||||
pub fn block_current(reason: BlockReason) {
|
||||
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
let switch = {
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
|
||||
let Some(next_id) = scheduler.ready.pop_front() else {
|
||||
println!("Deadlock: all tasks blocked");
|
||||
crate::hcf();
|
||||
};
|
||||
|
||||
let current_id = scheduler.current.expect("no current task");
|
||||
|
||||
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Blocked(reason);
|
||||
// explicitly do NOT push back the current task, because it is not ready
|
||||
|
||||
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running;
|
||||
scheduler.current = Some(next_id);
|
||||
|
||||
scheduler.make_switch(current_id, next_id)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
switch.perform();
|
||||
}
|
||||
|
||||
// this runs when this task is selected to run again
|
||||
crate::arch::restore_interrupts(interrupt_state);
|
||||
}
|
||||
|
||||
pub fn unblock(id: TaskId) {
|
||||
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
|
||||
if let Some(task) = scheduler.tasks[id].as_mut() {
|
||||
if matches!(task.state, ThreadState::Blocked(_)) {
|
||||
task.state = ThreadState::Ready;
|
||||
assert!(scheduler.ready.push_back(id));
|
||||
}
|
||||
}
|
||||
|
||||
crate::arch::restore_interrupts(interrupt_state);
|
||||
}
|
||||
|
||||
pub fn yield_current() {
|
||||
let interrupt_state = crate::arch::disable_interrupts_and_save();
|
||||
|
||||
|
||||
+61
-6
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
arch::ThreadContext,
|
||||
memory::{AddressSpace, KernelStack, VirtualAddr},
|
||||
task::loader::LoadedImage,
|
||||
};
|
||||
|
||||
// Thread Control Block
|
||||
@@ -12,14 +11,70 @@ pub enum ExitReason {
|
||||
Fault,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BlockReason {
|
||||
Send { ep: usize },
|
||||
Recv { ep: usize },
|
||||
Reply { client: usize },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ThreadState {
|
||||
Ready,
|
||||
Running,
|
||||
Blocked,
|
||||
Blocked(BlockReason),
|
||||
Dead(ExitReason),
|
||||
}
|
||||
|
||||
pub const MAX_MSG_SIZE: usize = 128;
|
||||
pub const MAILBOX_CAPACITY: usize = 4;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Message {
|
||||
pub sender: usize,
|
||||
pub length: usize,
|
||||
pub data: [u8; MAX_MSG_SIZE],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mailbox {
|
||||
pub messages: [Option<Message>; MAILBOX_CAPACITY],
|
||||
pub head: usize,
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
messages: [None; MAILBOX_CAPACITY],
|
||||
head: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<Message> {
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let msg = self.messages[self.head];
|
||||
self.head = (self.head + 1) % MAILBOX_CAPACITY;
|
||||
self.len -= 1;
|
||||
msg
|
||||
}
|
||||
|
||||
pub fn push(&mut self, msg: Message) -> bool {
|
||||
if self.len == MAILBOX_CAPACITY {
|
||||
return false;
|
||||
}
|
||||
|
||||
let tail = (self.head + self.len) % MAILBOX_CAPACITY;
|
||||
self.messages[tail] = Some(msg);
|
||||
self.len += 1;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tcb {
|
||||
pub id: usize,
|
||||
@@ -27,7 +82,7 @@ pub struct Tcb {
|
||||
pub kernel_stack: KernelStack,
|
||||
pub context: ThreadContext,
|
||||
pub address_space: AddressSpace,
|
||||
pub image: LoadedImage,
|
||||
pub mailbox: Mailbox,
|
||||
}
|
||||
|
||||
impl Tcb {
|
||||
@@ -35,10 +90,10 @@ impl Tcb {
|
||||
id: usize,
|
||||
address_space: AddressSpace,
|
||||
kernel_stack: KernelStack,
|
||||
image: LoadedImage,
|
||||
entry: VirtualAddr,
|
||||
user_stack: VirtualAddr,
|
||||
) -> Self {
|
||||
let context = ThreadContext::new(image.entry, user_stack, kernel_stack.top());
|
||||
let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
|
||||
|
||||
Self {
|
||||
id,
|
||||
@@ -46,7 +101,7 @@ impl Tcb {
|
||||
kernel_stack,
|
||||
context,
|
||||
address_space,
|
||||
image,
|
||||
mailbox: Mailbox::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dusk-sys = { path = "../dusk-sys" }
|
||||
|
||||
[[bin]]
|
||||
name = "client"
|
||||
test = false
|
||||
bench = false
|
||||
@@ -0,0 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use dusk_sys::{println, sys_exit, sys_recv, sys_send};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
let msg = "Hello from client!";
|
||||
println!("[client] Sent: {}", msg);
|
||||
sys_send(0, msg.as_ptr() as usize, msg.len()).unwrap();
|
||||
let out = [0u8; 128];
|
||||
let out_ptr = out.as_ptr() as usize;
|
||||
let max_len = out.len();
|
||||
let (actual_len, sender) = sys_recv(out_ptr, max_len).unwrap();
|
||||
println!(
|
||||
"[client] Received: {}",
|
||||
core::str::from_utf8(&out[..actual_len]).unwrap()
|
||||
);
|
||||
sys_exit(0);
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
println!("{info}");
|
||||
sys_exit(1);
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
[package]
|
||||
name = "init"
|
||||
name = "dusk-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "init"
|
||||
[lib]
|
||||
test = false
|
||||
bench = false
|
||||
@@ -0,0 +1,151 @@
|
||||
#![no_std]
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Status {
|
||||
// Success = 0,
|
||||
InvalidArgument = 1,
|
||||
BadAddress = 2,
|
||||
BadFileDescriptor = 3,
|
||||
NoSuchTask = 4,
|
||||
OutOfMemory = 5,
|
||||
}
|
||||
|
||||
impl From<usize> for Status {
|
||||
fn from(value: usize) -> Self {
|
||||
match value {
|
||||
1 => Self::InvalidArgument,
|
||||
2 => Self::BadAddress,
|
||||
3 => Self::BadFileDescriptor,
|
||||
4 => Self::NoSuchTask,
|
||||
5 => Self::OutOfMemory,
|
||||
_ => Self::InvalidArgument,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sys_yield() {
|
||||
unsafe {
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rax") 1usize,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_write(buf: &str) -> Result<(), Status> {
|
||||
unsafe {
|
||||
let status: usize;
|
||||
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rdi") 1,
|
||||
in("rsi") buf.as_ptr(),
|
||||
in("rdx") buf.len(),
|
||||
in("r10") 0,
|
||||
inlateout("rax") 3usize => status,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _
|
||||
);
|
||||
|
||||
if status != 0 {
|
||||
Err(status.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugWriter;
|
||||
|
||||
impl core::fmt::Write for DebugWriter {
|
||||
fn write_str(&mut self, value: &str) -> core::fmt::Result {
|
||||
debug_write(value).map_err(|_| core::fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn __print(arguments: core::fmt::Arguments<'_>) {
|
||||
use core::fmt::Write;
|
||||
|
||||
let _ = DebugWriter.write_fmt(arguments);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::__print(core::format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {{
|
||||
$crate::print!("\n");
|
||||
}};
|
||||
($($arg:tt)*) => {{
|
||||
$crate::print!("{}\n", core::format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn sys_exit(exit_code: usize) -> ! {
|
||||
unsafe {
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rdi") exit_code,
|
||||
in("rax") 2usize,
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
|
||||
unsafe {
|
||||
let status: usize;
|
||||
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rdi") dest_task_id,
|
||||
in("rsi") msg_ptr,
|
||||
in("rdx") len,
|
||||
inlateout("rax") 4usize => status,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _,
|
||||
);
|
||||
|
||||
if status != 0 {
|
||||
Err(status.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status> {
|
||||
let mut actual_len: usize = 0;
|
||||
let mut sender: usize = 0;
|
||||
|
||||
unsafe {
|
||||
let status: usize;
|
||||
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rdi") buf_ptr,
|
||||
in("rsi") max_len,
|
||||
in("rdx") &raw mut actual_len as usize,
|
||||
in("r10") &raw mut sender as usize,
|
||||
inlateout("rax") 5usize => status,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _,
|
||||
);
|
||||
|
||||
if status != 0 {
|
||||
Err(status.into())
|
||||
} else {
|
||||
Ok((actual_len, sender))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::arch::asm;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt::Write;
|
||||
|
||||
fn sys_yield() {
|
||||
unsafe {
|
||||
asm!("mov rax, 1", "syscall");
|
||||
}
|
||||
}
|
||||
|
||||
fn sys_write(fd: usize, buf: &str) -> Result<(), usize> {
|
||||
unsafe {
|
||||
let status;
|
||||
|
||||
asm!(
|
||||
"mov rax, 3",
|
||||
"syscall",
|
||||
in("rdi") fd,
|
||||
in("rsi") buf.as_ptr(),
|
||||
in("rdx") buf.len(),
|
||||
in("r10") 0,
|
||||
lateout("rax") status,
|
||||
);
|
||||
|
||||
if status != 0 { Err(status) } else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
struct Writer;
|
||||
|
||||
impl core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
sys_write(1, s).map_err(|_| core::fmt::Error)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => (let _ = $crate::Writer.write_fmt(format_args!($($arg)*)););
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
pub fn sys_exit(exit_code: usize) -> ! {
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov rax, 2",
|
||||
"syscall",
|
||||
in("rdi") exit_code,
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct Heap {
|
||||
pub data: UnsafeCell<[u8; 1024]>,
|
||||
}
|
||||
|
||||
impl Heap {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
data: UnsafeCell::new([0; 1024]),
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
unsafe { (*self.data.get()).len() }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for Heap {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { (*self.data.get()).as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Heap {}
|
||||
|
||||
static HEAP: Heap = Heap::new();
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
(0..100).for_each(|i| {
|
||||
println!("Hello {}", i);
|
||||
sys_yield();
|
||||
});
|
||||
|
||||
let len = HEAP.len();
|
||||
for i in 0..len {
|
||||
unsafe { HEAP.data.get().as_mut().unwrap()[i] = i as u8 };
|
||||
}
|
||||
|
||||
(0..1024).for_each(|i| {
|
||||
println!("{}", unsafe { HEAP.data.get().as_ref().unwrap()[i] });
|
||||
sys_yield();
|
||||
});
|
||||
|
||||
sys_exit(0);
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
println!("{info}");
|
||||
sys_exit(1);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "omega3"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
dusk-sys = { path = "../dusk-sys" }
|
||||
|
||||
[[bin]]
|
||||
name = "omega3"
|
||||
test = false
|
||||
bench = false
|
||||
@@ -0,0 +1,36 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use dusk_sys::{println, sys_exit, sys_recv, sys_send};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
println!(r#"-----------------------------"#);
|
||||
println!(r#" .d88888888b. .d88888b. "#);
|
||||
println!(r#" d88P" "Y88b 88P" "Y88 "#);
|
||||
println!(r#" 888 888 .od88P "#);
|
||||
println!(r#" Y88b d88P "Y88b "#);
|
||||
println!(r#" "88bo od88" 88b d88 "#);
|
||||
println!(r#" d88888 88888b "Y88888P" "#);
|
||||
println!(r#"----- Omega3 Dusk Root Server"#);
|
||||
|
||||
let buf = [0u8; 128];
|
||||
let buf_ptr = buf.as_ptr() as usize;
|
||||
let max_len = buf.len();
|
||||
loop {
|
||||
let (actual_len, sender) = sys_recv(buf_ptr, max_len).unwrap();
|
||||
println!(
|
||||
"[server] Received: {}",
|
||||
core::str::from_utf8(&buf[..actual_len]).unwrap()
|
||||
);
|
||||
sys_send(sender, buf_ptr, actual_len).unwrap();
|
||||
}
|
||||
|
||||
sys_exit(0);
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
println!("{info}");
|
||||
sys_exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user