refactor: fix kernel warnings

This commit is contained in:
Mark Poliakov 2024-11-19 17:28:41 +02:00
parent 74be46b29b
commit 49942563ef
17 changed files with 143 additions and 129 deletions

View File

@ -11,7 +11,7 @@ use async_trait::async_trait;
use libk::{
block,
error::Error,
task::runtime::{run_with_timeout, FutureTimeout},
task::runtime::with_timeout,
vfs::{ConnectionSocket, FileReadiness, ListenerSocket, Socket},
};
use libk_device::monotonic_timestamp;
@ -64,7 +64,7 @@ impl TcpSocket {
) -> Result<(SocketAddr, Arc<TcpSocket>), Error> {
let future = Self::connect_async(remote);
match timeout {
Some(timeout) => run_with_timeout(timeout, future).await.into(),
Some(timeout) => with_timeout(future, timeout).await?.into(),
None => future.await,
}
}
@ -290,10 +290,7 @@ impl TcpSocket {
connection.poll_established(cx)
});
match run_with_timeout(timeout, fut).await {
FutureTimeout::Ok(value) => value,
FutureTimeout::Timeout => Err(Error::TimedOut),
}
with_timeout(fut, timeout).await?
}
}

View File

@ -4,7 +4,7 @@ use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec, vec::Vec};
use atomic_enum::atomic_enum;
use device_api::{interrupt::InterruptHandler, Device};
use futures_util::task::AtomicWaker;
use libk::task::runtime::{self, FutureTimeout};
use libk::task::runtime;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox,
@ -278,8 +278,7 @@ impl Xhci {
// Wait for port reset
// TODO handle disconnect during reset?
let result = runtime::run_with_timeout(
Duration::from_secs(1),
let result = runtime::with_timeout(
poll_fn(|cx| {
let state = &self.port_states[port];
@ -290,12 +289,13 @@ impl Xhci {
Poll::Pending
}
}),
Duration::from_secs(1),
)
.await;
match result {
FutureTimeout::Ok(()) => Ok(()),
FutureTimeout::Timeout => Err(UsbError::PortResetFailed),
Ok(()) => Ok(()),
Err(_) => Err(UsbError::PortResetFailed),
}
}

View File

@ -9,8 +9,6 @@ use core::{
use bytemuck::{Pod, Zeroable};
use kernel_arch_interface::mem::KernelTableManager;
use crate::table::EntryLevel;
/// Wrapper type to represent a physical memory address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Pod, Zeroable)]
#[repr(transparent)]

View File

@ -1,4 +1,4 @@
use core::ops::Range;
use core::ops::{Deref, Range};
use alloc::sync::Arc;
use kernel_arch::ProcessAddressSpaceImpl;
@ -7,7 +7,7 @@ use libk_mm_interface::{
process::ProcessAddressSpaceManager,
table::{MapAttributes, TableAllocator},
};
use libk_util::sync::IrqSafeSpinlock;
use libk_util::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard};
use vmalloc::{RangeData, VirtualMemoryAllocator};
use yggdrasil_abi::error::Error;
@ -26,6 +26,13 @@ pub enum VirtualRangeBacking {
File(FileBacking),
}
/// Wrapper type for ensuring the translation table cannot be modified while performing accesses
/// to the inner [PhysicalAddress].
pub struct TranslateGuard<'a, TA: TableAllocator> {
address: PhysicalAddress,
_guard: IrqSafeSpinlockGuard<'a, Inner<TA>>,
}
/// Describes a file-backed memory range provider
#[derive(Clone)]
pub struct FileBacking {
@ -415,6 +422,17 @@ impl<TA: TableAllocator> ProcessAddressSpace<TA> {
self.inner.lock().table.translate(address).map(|e| e.0)
}
/// Same as [ProcessAddressSpace::translate], except the lock on the address space is held
/// until the resulting [TranslateGuard] is dropped.
pub fn translate_lock(&self, address: usize) -> Result<TranslateGuard<TA>, Error> {
let guard = self.inner.lock();
let address = guard.table.translate(address).map(|e| e.0)?;
Ok(TranslateGuard {
address,
_guard: guard,
})
}
/// Removes a single PAGE_SIZE mapping from the address space.
///
/// See [ProcessAddressSpaceManager::unmap].
@ -449,3 +467,11 @@ impl<TA: TableAllocator> Drop for ProcessAddressSpace<TA> {
self.clear().ok();
}
}
impl<TA: TableAllocator> Deref for TranslateGuard<'_, TA> {
type Target = PhysicalAddress;
fn deref(&self) -> &Self::Target {
&self.address
}
}

View File

@ -20,7 +20,8 @@
trait_upcasting,
arbitrary_self_types,
slice_split_once,
arbitrary_self_types_pointers
arbitrary_self_types_pointers,
result_flattening
)]
extern crate alloc;

View File

@ -5,7 +5,6 @@ use alloc::sync::Arc;
use cfg_if::cfg_if;
use elf::{
endian::AnyEndian,
io_traits::InputStream,
relocation::{Rel, Rela},
segment::ProgramHeader,
ElfStream, ParseError,
@ -14,14 +13,14 @@ use libk_mm::{
pointer::PhysicalRefMut,
process::{ProcessAddressSpace, VirtualRangeBacking},
table::MapAttributes,
PageBox, L3_PAGE_SIZE,
L3_PAGE_SIZE,
};
use libk_util::io::{Read, Seek};
use yggdrasil_abi::{error::Error, io::SeekFrom, path::PathBuf};
use crate::{
random,
task::{mem::ForeignPointer, process::ProcessImage, types::TlsImage},
task::{process::ProcessImage, types::TlsImage},
};
cfg_if! {

View File

@ -1,5 +1,3 @@
use core::{alloc::Layout, ptr::NonNull};
use alloc::{
borrow::ToOwned,
string::String,
@ -18,7 +16,6 @@ use libk_util::io::{Read, Seek};
use yggdrasil_abi::{
error::Error,
io::SeekFrom,
pass::{Place, Placer},
path::Path,
process::{auxv, AuxValue, ProcessGroupId},
};

View File

@ -1,27 +1,22 @@
use core::{
future::poll_fn,
pin::Pin,
ptr,
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll},
task::Poll,
};
use alloc::sync::Arc;
use futures_util::Future;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRef,
process::ProcessAddressSpace,
};
use libk_mm::process::ProcessAddressSpace;
use libk_util::waker::QueueWaker;
use yggdrasil_abi::error::Error;
use crate::task::thread::Thread;
use crate::task::mem::ForeignAtomic;
/// User-space mutex (like BSD/Linux's futex) data structure
pub struct UserspaceMutex {
queue: QueueWaker,
space: Arc<ProcessAddressSpace>,
address: usize,
atomic: *const AtomicU32,
}
impl UserspaceMutex {
@ -30,35 +25,42 @@ impl UserspaceMutex {
Ok(Self {
queue: QueueWaker::new(),
space: space.clone(),
address,
atomic: ptr::with_exposed_provenance(address),
})
}
fn load(&self) -> u32 {
// TODO: this is slow, but this prevents the kernel from reading from unmapped memory
let phys = self.space.translate(self.address).unwrap();
unsafe { PhysicalRef::<AtomicU32>::map(phys).load(Ordering::Acquire) }
fn load(&self) -> Result<u32, Error> {
self.atomic
.atomic_load_foreign(&self.space, Ordering::Acquire)
}
async fn wait_predicate<P: Fn(u32) -> bool>(&self, predicate: P) {
async fn wait_predicate<P: Fn(u32) -> bool>(&self, predicate: P) -> Result<(), Error> {
poll_fn(|cx| {
if predicate(self.load()) {
self.queue.remove(cx.waker());
Poll::Ready(())
} else {
self.queue.register(cx.waker());
Poll::Pending
let result = self.load();
match result {
Err(err) => {
self.queue.remove(cx.waker());
Poll::Ready(Err(err))
}
Ok(val) if predicate(val) => {
self.queue.remove(cx.waker());
Poll::Ready(Ok(()))
}
Ok(_) => {
self.queue.register(cx.waker());
Poll::Pending
}
}
})
.await
}
pub async fn wait_until(self: Arc<Self>, compare_value: u32) {
pub async fn wait_until(self: Arc<Self>, compare_value: u32) -> Result<(), Error> {
self.wait_predicate(|value| value == compare_value).await
}
/// Blocks until the value at the mutex's address becomes different from `compare_value`
pub async fn wait(self: Arc<Self>, compare_value: u32) {
pub async fn wait(self: Arc<Self>, compare_value: u32) -> Result<(), Error> {
self.wait_predicate(|value| value != compare_value).await
}

View File

@ -1,4 +1,9 @@
use core::{alloc::Layout, mem::size_of};
use core::{
alloc::Layout,
mem::size_of,
ptr,
sync::atomic::{AtomicU32, Ordering},
};
use libk_mm::{address::Virtualize, process::ProcessAddressSpace};
use yggdrasil_abi::error::Error;
@ -96,6 +101,14 @@ pub trait ForeignPointer: Sized {
) -> Result<&'a mut [Self], Error>;
}
pub trait ForeignAtomic<T> {
fn atomic_load_foreign(
self: *const Self,
space: &ProcessAddressSpace,
ordering: Ordering,
) -> Result<T, Error>;
}
impl<T> ForeignPointer for T {
unsafe fn write_foreign_volatile(self: *mut Self, space: &ProcessAddressSpace, value: T) {
self.try_write_foreign_volatile(space, value)
@ -203,6 +216,23 @@ impl<T> ForeignPointer for T {
}
}
impl ForeignAtomic<u32> for AtomicU32 {
fn atomic_load_foreign(
self: *const Self,
space: &ProcessAddressSpace,
ordering: Ordering,
) -> Result<u32, Error> {
let virt = self.addr();
if virt % size_of::<u32>() != 0 {
// Misaligned atomic
return Err(Error::InvalidMemoryOperation);
}
let phys = space.translate_lock(virt)?;
let phys_virt: *const Self = ptr::with_exposed_provenance(phys.virtualize());
Ok(unsafe { &*phys_virt }.load(ordering))
}
}
fn validate_user_align_size(addr: usize, layout: &Layout) -> Result<(), Error> {
// Explicitly disallow NULL
if addr == 0 {

View File

@ -29,8 +29,7 @@ use yggdrasil_abi::{
use crate::{
task::{
binary, futex::UserspaceMutex, thread::Thread, types::AllocateProcessId, TaskContextImpl,
ThreadId,
futex::UserspaceMutex, thread::Thread, types::AllocateProcessId, TaskContextImpl, ThreadId,
},
vfs::{FileReadiness, FileSet, IoContext, NodeRef},
};

View File

@ -8,4 +8,4 @@ mod timer;
pub use executor::{run_to_completion, spawn, spawn_async_worker};
pub use task_queue::init_task_queue;
pub use timer::{run_with_timeout, sleep, tick, FutureTimeout, SleepFuture};
pub use timer::{maybe_timeout, sleep, tick, with_timeout, SleepFuture};

View File

@ -1,11 +1,12 @@
use core::{
future::poll_fn,
pin::Pin,
task::{Context, Poll, Waker},
time::Duration,
};
use alloc::vec::Vec;
use futures_util::{future::BoxFuture, Future, FutureExt};
use futures_util::{Future, FutureExt};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
@ -70,11 +71,6 @@ fn register_timeout(duration: Duration, waker: &Waker) {
}
}
pub enum FutureTimeout<T> {
Ok(T),
Timeout,
}
pub struct SleepFuture {
deadline: Duration,
}
@ -113,42 +109,33 @@ pub fn sleep(duration: Duration) -> SleepFuture {
SleepFuture { deadline }
}
pub fn run_with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
duration: Duration,
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
fut: F,
) -> impl Future<Output = FutureTimeout<T>> + 'a {
struct TimeoutFuture<'f, T> {
fut: BoxFuture<'f, T>,
sleep_fut: BoxFuture<'f, ()>,
}
timeout: Duration,
) -> impl Future<Output = Result<T, Error>> + Send + 'a {
let mut fut = fut.boxed();
let mut sleep_fut = sleep(timeout).boxed();
impl<'f, T> Future for TimeoutFuture<'f, T> {
type Output = FutureTimeout<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let (timeout, result) = (self.sleep_fut.as_mut().poll(cx), self.fut.as_mut().poll(cx));
if let Poll::Ready(result) = result {
Poll::Ready(FutureTimeout::Ok(result))
} else if timeout.is_ready() {
Poll::Ready(FutureTimeout::Timeout)
} else {
Poll::Pending
}
poll_fn(move |cx| {
let (timeout, output) = (sleep_fut.poll_unpin(cx), fut.poll_unpin(cx));
if let Poll::Ready(output) = output {
Poll::Ready(Ok(output))
} else if timeout.is_ready() {
Poll::Ready(Err(Error::TimedOut))
} else {
Poll::Pending
}
}
TimeoutFuture {
fut: fut.boxed(),
sleep_fut: sleep(duration).boxed(),
}
})
}
impl<T> From<FutureTimeout<Result<T, Error>>> for Result<T, Error> {
fn from(value: FutureTimeout<Result<T, Error>>) -> Self {
match value {
FutureTimeout::Ok(res) => res,
FutureTimeout::Timeout => Err(Error::TimedOut),
pub fn maybe_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
fut: F,
timeout: Option<Duration>,
) -> impl Future<Output = Result<T, Error>> + Send + 'a {
async move {
match timeout {
Some(timeout) => with_timeout(fut, timeout).await,
None => Ok(fut.await),
}
}
}

View File

@ -59,11 +59,6 @@ pub struct ThreadDebuggingInfo {
pub breakpoints: BTreeMap<usize, BreakpointType>,
}
struct SignalEntry {
entry: usize,
stack: usize,
}
/// Describes a single thread within the system
pub struct Thread {
/// Unique thread ID

View File

@ -1,11 +1,9 @@
use alloc::sync::Arc;
use core::{
fmt,
sync::atomic::{AtomicU32, AtomicU64, Ordering},
};
use atomic_enum::atomic_enum;
use libk_mm::PageBox;
use yggdrasil_abi::process::ProcessId;
pub trait AllocateProcessId {

View File

@ -1,6 +1,5 @@
use core::{
fmt,
future::Future,
task::{Context, Poll},
time::Duration,
};
@ -13,10 +12,7 @@ use yggdrasil_abi::{
net::{SocketAddr, SocketOption},
};
use crate::{
task::runtime::{run_with_timeout, FutureTimeout},
vfs::FileReadiness,
};
use crate::{task::runtime::maybe_timeout, vfs::FileReadiness};
enum SocketInner {
Connection(Arc<dyn ConnectionSocket + Send + 'static>),
@ -130,7 +126,7 @@ impl SocketWrapper {
let (remote, remote_socket) = match (options.non_blocking, options.recv_timeout) {
(false, timeout) => {
let fut = socket.accept();
block!(maybe_timeout(timeout, fut).await)??
block!(maybe_timeout(fut, timeout).await)???
}
(true, _) => socket.accept_nonblocking()?,
};
@ -157,11 +153,11 @@ impl SocketWrapper {
) -> Result<(usize, SocketAddr), Error> {
match &self.inner {
SocketInner::Packet(socket) => {
maybe_timeout(timeout, socket.receive_from(buffer)).await
maybe_timeout(socket.receive_from(buffer), timeout).await?
}
SocketInner::Connection(socket) => {
let remote = socket.remote_address().ok_or(Error::NotConnected)?;
let len = maybe_timeout(timeout, socket.receive(buffer)).await?;
let len = maybe_timeout(socket.receive(buffer), timeout).await??;
Ok((len, remote))
}
SocketInner::Listener(_) => Err(Error::InvalidOperation),
@ -184,9 +180,9 @@ impl SocketWrapper {
) -> Result<usize, Error> {
match &self.inner {
SocketInner::Packet(socket) => {
maybe_timeout(timeout, socket.send_to(remote, buffer)).await
maybe_timeout(socket.send_to(remote, buffer), timeout).await?
}
SocketInner::Connection(socket) => maybe_timeout(timeout, socket.send(buffer)).await,
SocketInner::Connection(socket) => maybe_timeout(socket.send(buffer), timeout).await?,
SocketInner::Listener(_) => Err(Error::InvalidOperation),
}
}
@ -325,20 +321,6 @@ impl Default for InnerOptions {
}
}
async fn maybe_timeout<R, F: Future<Output = Result<R, Error>> + Send>(
timeout: Option<Duration>,
fut: F,
) -> Result<R, Error> {
if let Some(timeout) = timeout {
match run_with_timeout(timeout, fut).await {
FutureTimeout::Ok(value) => value,
FutureTimeout::Timeout => Err(Error::TimedOut),
}
} else {
fut.await
}
}
// impl From<Arc<dyn ConnectionSocket + 'static>> for SocketWrapper {
// fn from(value: Arc<dyn ConnectionSocket + 'static>) -> Self {
// Self::Connection(value)

View File

@ -6,7 +6,7 @@ pub(crate) use abi::{
},
mem::{MappingFlags, MappingSource},
net::SocketType,
process::{Signal, SignalEntryData, SpawnOptions, ThreadOption},
process::{Signal, SignalEntryData, SpawnOptions},
system::SystemInfo,
};
use abi::{

View File

@ -48,14 +48,12 @@ pub(crate) fn map_memory(
}
};
let result = space.allocate(
None,
len,
backing,
MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NON_GLOBAL,
);
let mut attrs = MapAttributes::NON_GLOBAL | MapAttributes::USER_READ;
if flags.contains(MappingFlags::WRITE) {
attrs |= MapAttributes::USER_WRITE;
}
result
space.allocate(None, len, backing, attrs)
})
}
@ -218,6 +216,8 @@ pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
Ok(())
}
// TODO this would've been much simpler if physical pages behaved like Rc<...>'s and kernel could
// keep a copy of it, guaranteeing it cannot be freed and remapped into another process.
pub(crate) fn mutex(mutex: &AtomicU32, op: &MutexOperation) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process();
@ -225,8 +225,8 @@ pub(crate) fn mutex(mutex: &AtomicU32, op: &MutexOperation) -> Result<(), Error>
let mutex = process.get_or_insert_mutex((mutex as *const AtomicU32).addr())?;
match op {
&MutexOperation::Wait(value, _timeout) => block! { mutex.wait(value).await },
&MutexOperation::WaitUntil(value, _timeout) => block! { mutex.wait_until(value).await },
&MutexOperation::Wait(value, _timeout) => block! { mutex.wait(value).await }?,
&MutexOperation::WaitUntil(value, _timeout) => block! { mutex.wait_until(value).await }?,
MutexOperation::Wake => {
mutex.wake();
Ok(())
@ -291,7 +291,10 @@ pub(crate) fn wait_thread(id: u32) -> Result<(), Error> {
}
pub(crate) fn get_thread_option(option: &mut ThreadOption) -> Result<(), Error> {
todo!()
match option {
// There're better ways to do this, don't ask the kernel
ThreadOption::ThreadPointer(_) => Err(Error::InvalidOperation),
}
}
pub(crate) fn set_thread_option(option: &ThreadOption) -> Result<(), Error> {