Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syscalls physical memory #201

Merged
merged 9 commits into from
Mar 9, 2019
1 change: 1 addition & 0 deletions ahci/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ capabilities!(CAPABILITIES = Capabilities {
kfs_libuser::syscalls::nr::UnmapSharedMemory,
kfs_libuser::syscalls::nr::ConnectToNamedPort,
kfs_libuser::syscalls::nr::CreateInterruptEvent,
kfs_libuser::syscalls::nr::QueryPhysicalAddress,
],
raw_caps: [
kfs_libuser::caps::ioport(pci::CONFIG_ADDRESS + 0), kfs_libuser::caps::ioport(pci::CONFIG_ADDRESS + 1), kfs_libuser::caps::ioport(pci::CONFIG_ADDRESS + 2), kfs_libuser::caps::ioport(pci::CONFIG_ADDRESS + 3),
Expand Down
34 changes: 23 additions & 11 deletions kernel/src/frame_allocator/physical_mem_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

use super::{FrameAllocator, FrameAllocatorTraitPrivate};
use crate::paging::PAGE_SIZE;
use crate::mem::PhysicalAddress;
use crate::utils::{align_down, div_ceil, check_aligned, Splittable};
use crate::mem::{PhysicalAddress, VirtualAddress};
use crate::utils::{div_ceil, check_aligned, check_nonzero_length, Splittable};
use core::ops::Range;
use core::iter::StepBy;
use core::fmt::{Formatter, Error, Debug};
use core::marker::PhantomData;
use crate::error::KernelError;
use alloc::vec::Vec;
use failure::Backtrace;

/// A span of adjacent physical frames. A frame is [PAGE_SIZE].
///
Expand Down Expand Up @@ -45,16 +46,27 @@ impl PhysicalMemRegion {
/// On drop the region won't be given back to the [FrameAllocator],
/// and thous stay marked as reserved.
///
/// # Panic
/// # Error
///
/// * Panics if any of the frames in this span wasn't marked as reserved in
/// the [FrameAllocator], as it could had mistakenly given it as regular ram.
pub unsafe fn on_fixed_mmio(start_addr: PhysicalAddress, len: usize) -> Self {
assert!(FrameAllocator::check_is_reserved(start_addr, len));
PhysicalMemRegion {
start_addr: align_down(start_addr.addr(), PAGE_SIZE),
frames: div_ceil(len, PAGE_SIZE),
should_free_on_drop: false
/// * InvalidAddress:
/// * `address` is not PAGE_SIZE aligned.
/// * One or more of the frames in this span wasn't marked as reserved in
/// the [FrameAllocator], as it could had mistakenly given it as regular RAM.
/// * InvalidLength:
/// * `length` is not PAGE_SIZE aligned.
/// * `length` is zero.
pub unsafe fn on_fixed_mmio(address: PhysicalAddress, length: usize) -> Result<Self, KernelError> {
check_nonzero_length(length)?;
check_aligned(length, PAGE_SIZE)?;
check_aligned(address.addr(), PAGE_SIZE)?;
if !FrameAllocator::check_is_reserved(address, length) {
Err(KernelError::InvalidAddress { address: VirtualAddress(address.addr()), length, backtrace: Backtrace::new() })
} else {
Ok(PhysicalMemRegion {
start_addr: address.addr(),
frames: div_ceil(length, PAGE_SIZE),
should_free_on_drop: false
})
}
}

Expand Down
80 changes: 78 additions & 2 deletions kernel/src/interrupts/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::ipc;
use super::check_thread_killed;
use crate::error::UserspaceError;
use crate::error::{UserspaceError, KernelError};
use failure::Backtrace;
use kfs_libkern::{nr, SYSCALL_NAMES, MemoryInfo, MemoryAttributes, MemoryPermissions};
use bit_field::BitArray;

Expand Down Expand Up @@ -48,7 +49,7 @@ fn map_framebuffer() -> Result<(usize, usize, usize, usize), UserspaceError> {
* tag.framebuffer_dimensions().0 as usize
* tag.framebuffer_dimensions().1 as usize / 8;
let frame_buffer_phys_region = unsafe {
PhysicalMemRegion::on_fixed_mmio(PhysicalAddress(tag.framebuffer_addr()), framebuffer_size)
PhysicalMemRegion::on_fixed_mmio(PhysicalAddress(tag.framebuffer_addr()), framebuffer_size)?
};

let process = get_current_process();
Expand Down Expand Up @@ -99,6 +100,56 @@ fn create_interrupt_event(irq_num: usize, _flag: u32) -> Result<usize, Userspace
Ok(hnd as _)
}

/// Gets the physical region a given virtual address maps.
///
/// This syscall is mostly used for DMAs, where the physical address of a buffer needs to be known
/// by userspace.
///
/// # Return
///
/// 0. The start address of the physical region.
/// 1. 0x00000000 (On Horizon it contains the KernelSpace virtual address of this mapping,
/// but I don't see any use for it).
/// 2. The length of the physical region.
// kfs extension
/// 3. The offset in the region of the given virtual address.
///
/// # Error
///
/// - InvalidAddress: This address does not map physical memory.
// TODO: Kernel mappings must be physically continuous.
// BODY: Virtual memory is a great thing, it can make a fragmented mapping appear contiguous from the
// BODY: userspace. But unfortunately Horizon does not take advantage of this feature, and
// BODY: allocates its mapping as a single Physical Memory Region.
// BODY:
// BODY: Its syscalls are based around that fact, and to do a `virt_to_phys(addr)`, you simply
// BODY: need to `query_memory(addr).offset` to get its offset in its mapping, and compute its
// BODY: physical address as `query_physical_address(addr).base + offset`.
// BODY:
// BODY: This will not work when the mapping is composed of several physical regions, and
// BODY: Horizon drivers will not be expecting that. So for them to work on our kernel, we must
// BODY: renounce using fragmented mappings.
// BODY:
// BODY: For now `query_physical_address` is providing an additional "offset in physical region" return value,
// BODY: to help KFS drivers doing a virt_to_phys without needing to walk the list of physical regions.
fn query_physical_address(virtual_address: usize) -> Result<(usize, usize, usize, usize), UserspaceError> {
let virtual_address = VirtualAddress(virtual_address);
let proc = scheduler::get_current_process();
let mem = proc.pmemory.lock();
let mapping = mem.query_memory(virtual_address);
let frames = match mapping.mapping().mtype_ref() {
MappingType::Regular(regions) => regions,
MappingType::Shared(arc_regions) => arc_regions.as_ref(),
MappingType::Available | MappingType::Guarded | MappingType::SystemReserved =>
return Err(KernelError::InvalidAddress { address: virtual_address, length: 1, backtrace: Backtrace::new() }.into()),
};
let offset = virtual_address - mapping.mapping().address();
let mut i = 0;
let pos = frames.iter().position(|region| { i += region.size(); i > offset })
.expect("Mapping region count is corrupted");
Ok((frames[pos].address().addr(), 0x00000000, frames[pos].size(), offset - (i - frames[pos].size())))
}

/// Waits for one of the handles to signal an event.
///
/// When zero handles are passed, this will wait forever until either timeout or cancellation occurs.
Expand Down Expand Up @@ -483,6 +534,29 @@ fn create_session(_is_light: bool, _unk: usize) -> Result<(usize, usize), Usersp
Ok((serverhnd as _, clienthnd as _))
}

/// Maps a physical region in the address space of the process.
///
/// # Returns
///
/// The virtual address where it was mapped.
///
/// # Errors
///
/// * InvalidAddress:
/// * `virtual_address` is already occupied.
/// * `virtual_address` is not PAGE_SIZE aligned.
/// * `physical_address` points to a physical region in DRAM (it's not MMIO).
/// * InvalidLength:
/// * `length` is not PAGE_SIZE aligned.
/// * `length` is zero.
pub fn map_mmio_region(physical_address: usize, size: usize, virtual_address: usize, writable: bool) -> Result<(), UserspaceError> {
let region = unsafe { PhysicalMemRegion::on_fixed_mmio(PhysicalAddress(physical_address), size)? };
let curproc = scheduler::get_current_process();
let mut mem = curproc.pmemory.lock();
mem.map_phys_region_to(region, VirtualAddress(virtual_address), if writable { MappingAccessRights::u_rw() } else { MappingAccessRights::u_r() })?;
Ok(())
}

impl Registers {
/// Update the Registers with the passed result.
fn apply0(&mut self, ret: Result<(), UserspaceError>) {
Expand Down Expand Up @@ -608,12 +682,14 @@ pub extern fn syscall_handler_inner(registers: &mut Registers) {
(true, nr::ReplyAndReceiveWithUserBuffer) => registers.apply1(reply_and_receive_with_user_buffer(UserSpacePtrMut::from_raw_parts_mut(x0 as _, x1), UserSpacePtr::from_raw_parts(x2 as _, x3), x4 as _, x5)),
(true, nr::CreateSharedMemory) => registers.apply1(create_shared_memory(x0 as _, x1 as _, x2 as _)),
(true, nr::CreateInterruptEvent) => registers.apply1(create_interrupt_event(x0, x1 as u32)),
(true, nr::QueryPhysicalAddress) => registers.apply4(query_physical_address(x0 as _)),
(true, nr::CreatePort) => registers.apply2(create_port(x0 as _, x1 != 0, UserSpacePtr(x2 as _))),
(true, nr::ManageNamedPort) => registers.apply1(manage_named_port(UserSpacePtr(x0 as _), x1 as _)),
(true, nr::ConnectToPort) => registers.apply1(connect_to_port(x0 as _)),

// KFS extensions
(true, nr::MapFramebuffer) => registers.apply4(map_framebuffer()),
(true, nr::MapMmioRegion) => registers.apply0(map_mmio_region(x0, x1, x2, x3 != 0)),

// Unknown/unauthorized syscall.
(false, _) => {
Expand Down
4 changes: 2 additions & 2 deletions kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ impl ProcessStruct {
impl Drop for ProcessStruct {
fn drop(&mut self) {
// todo this should be a debug !
info!("Dropped a process : {:?}", self)
info!("☠️ Dropped a process : {}", self.name)
}
}

Expand Down Expand Up @@ -661,7 +661,7 @@ impl ThreadStruct {
impl Drop for ThreadStruct {
fn drop(&mut self) {
// todo this should be a debug !
info!("Dropped a thread : {:?}", self)
info!("💀 Dropped a thread : {}", self.process.name)
}
}

15 changes: 8 additions & 7 deletions kernel/src/process/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,14 @@ impl ProcessCapabilities {
duplicate_svc.set_bit(index as _, true);
let index = index as usize * 24;
// This cannot overflow: The first 8 bit are guaranteed to be 0.
let highest_svc_in_mask = 24 - (mask.leading_zeros() as usize - 8);
if index + highest_svc_in_mask > MAX_SVC {
return Err(KernelError::ExceedingMaximum {
maximum: MAX_SVC as u64,
value: (index + highest_svc_in_mask) as u64,
backtrace: Backtrace::new()
});
if let Some(highest_svc_in_mask) = 23usize.checked_sub(mask.leading_zeros() as usize - 8) {
if index + highest_svc_in_mask > MAX_SVC {
return Err(KernelError::ExceedingMaximum {
maximum: MAX_SVC as u64,
value: (index + highest_svc_in_mask) as u64,
backtrace: Backtrace::new()
});
}
}
capabilities.syscall_mask.set_bits(index..index + 24, mask);
}
Expand Down
17 changes: 11 additions & 6 deletions libkern/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,9 @@ macro_rules! syscalls {
(
static $byname:ident;
mod $byid:ident;
maxid = $maxid:expr;
$($name:ident = $id:expr),* $(,)*
$($name:ident = $id:expr,)*
---
$max_svc_ident:ident = $max_svc_id:expr
) => {
pub mod $byid {
//! Syscall numbers
Expand All @@ -161,12 +162,15 @@ macro_rules! syscalls {
#[allow(non_upper_case_globals)]
pub const $name: usize = $id;
)*

#[allow(non_upper_case_globals)]
pub const $max_svc_ident: usize = $max_svc_id;
}
lazy_static! {
/// A table associating a syscall name string for every syscall
/// number.
pub static ref $byname: [&'static str; $maxid] = {
let mut arr = ["Unknown"; $maxid];
pub static ref $byname: [&'static str; $max_svc_id + 1] = {
let mut arr = ["Unknown"; $max_svc_id + 1];
$(arr[$id] = stringify!($name);)*
arr
};
Expand All @@ -177,7 +181,6 @@ macro_rules! syscalls {
syscalls! {
static SYSCALL_NAMES;
mod nr;
maxid = 0x82;
SetHeapSize = 0x01,
SetMemoryPermission = 0x02,
SetMemoryAttribute = 0x03,
Expand Down Expand Up @@ -298,7 +301,9 @@ syscalls! {
// KFS Extensions
MapFramebuffer = 0x80,
StartProcessEntrypoint = 0x81,
MapMmioRegion = 0x82,

---
// Add SVCs before this line.
MaxSvc = 0x81
MaxSvc = 0x82
}
33 changes: 2 additions & 31 deletions libuser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ extern crate bitfield;

#[macro_use]
extern crate kfs_libutils;
use kfs_libkern;
#[macro_use]
extern crate failure;

Expand All @@ -41,6 +40,7 @@ extern crate lazy_static;

pub mod caps;
pub mod syscalls;
pub mod mem;
pub mod types;
pub mod ipc;
pub mod sm;
Expand All @@ -54,8 +54,7 @@ mod log_impl;
pub use kfs_libutils::io;

use kfs_libutils as utils;
use crate::error::{Error, LibuserError};


// TODO: report #[cfg(not(test))] and #[global_allocator]
// BODY: `#[cfg(not(test))]` still compiles this item with cargo test,
// BODY: but `#[cfg(target_os = "none")] does not. I think this is a bug,
Expand All @@ -66,34 +65,6 @@ use crate::error::{Error, LibuserError};
#[global_allocator]
static ALLOCATOR: allocator::Allocator = allocator::Allocator::new();

/// Finds a free memory zone of the given size and alignment in the current
/// process's virtual address space. Note that the address space is not reserved,
/// a call to map_memory to that address space might fail if another thread
/// maps to it first. It is recommended to use this function and the map syscall
/// in a loop.
///
/// # Panics
///
/// Panics on underflow when size = 0.
///
/// Panics on underflow when align = 0.
pub fn find_free_address(size: usize, align: usize) -> Result<usize, Error> {
let mut addr = 0;
// Go over the address space.
loop {
let (meminfo, _) = syscalls::query_memory(addr)?;
if meminfo.memtype == kfs_libkern::MemoryType::Unmapped {
let alignedbaseaddr = kfs_libutils::align_up_checked(meminfo.baseaddr, align).ok_or(LibuserError::AddressSpaceExhausted)?;

let alignment = alignedbaseaddr - meminfo.baseaddr;
if alignment.checked_add(size - 1).ok_or(LibuserError::AddressSpaceExhausted)? < meminfo.size {
return Ok(alignedbaseaddr)
}
}
addr = meminfo.baseaddr.checked_add(meminfo.size).ok_or(LibuserError::AddressSpaceExhausted)?;
}
}

// Runtime functions
//
// Functions beyond this lines are required by the rust compiler when building
Expand Down
Loading