Commit Diff


commit - e1940d574f26f4e261757461655d7290fe1ff4e9
commit + 28328e7600e718a0b0b419e39f5bcdfe01cd17f4
blob - /dev/null
blob + f3c082a5af513b2829ef2c0fa93ed0d26da17839 (mode 644)
--- /dev/null
+++ src/ipc.rs
@@ -0,0 +1,198 @@
+// vim: set tw=79 cc=80 ts=4 sw=4 sts=4 et :
+//
+// Copyright (c) 2025-2026 Murilo Ijanc' <murilo@ijanc.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+//! Synchronous IPC: endpoints, send, recv, call, reply.
+
+use core::cell::UnsafeCell;
+
+use crate::sched;
+use crate::serial;
+
+pub const MSG_MAX: usize = 256;
+const MAX_ENDPOINTS: usize = 32;
+
+/// A pending message: pointer to sender's buffer + length.
+/// Only valid while the sender is blocked.
+#[derive(Clone, Copy)]
+struct PendingMsg {
+    ptr: *const u8,
+    len: usize,
+}
+
+/// An IPC endpoint.
+#[derive(Clone, Copy)]
+struct Endpoint {
+    active: bool,
+    /// Task index blocked in send (waiting for receiver)
+    sender: Option<usize>,
+    sender_msg: Option<PendingMsg>,
+    /// Task index blocked in recv (waiting for sender)
+    receiver: Option<usize>,
+    receiver_buf: Option<PendingMsg>,
+}
+
+impl Endpoint {
+    const fn empty() -> Self {
+        Endpoint {
+            active: false,
+            sender: None,
+            sender_msg: None,
+            receiver: None,
+            receiver_buf: None,
+        }
+    }
+}
+
+struct IpcState {
+    endpoints: UnsafeCell<[Endpoint; MAX_ENDPOINTS]>,
+    next_id: UnsafeCell<usize>,
+}
+
+unsafe impl Sync for IpcState {}
+
+static IPC: IpcState = IpcState {
+    endpoints: UnsafeCell::new(
+        [Endpoint::empty(); MAX_ENDPOINTS],
+    ),
+    next_id: UnsafeCell::new(0),
+};
+
+/// Create a new endpoint. Returns the endpoint ID.
+pub fn endpoint_create() -> Option<usize> {
+    unsafe {
+        let eps = &mut *IPC.endpoints.get();
+        let next = &mut *IPC.next_id.get();
+        if *next >= MAX_ENDPOINTS {
+            return None;
+        }
+        let id = *next;
+        eps[id].active = true;
+        *next += 1;
+        Some(id)
+    }
+}
+
+/// Send a message to an endpoint. Blocks until a receiver
+/// is ready.
+pub fn send(ep_id: usize, msg: &[u8]) {
+    unsafe {
+        let eps = &mut *IPC.endpoints.get();
+
+        if ep_id >= MAX_ENDPOINTS || !eps[ep_id].active {
+            serial::print("ipc: invalid endpoint\n");
+            return;
+        }
+
+        let ep = &mut eps[ep_id];
+
+        // Is there a receiver already waiting?
+        if let Some(recv_task) = ep.receiver.take() {
+            // Direct transfer: copy msg to receiver's buffer
+            if let Some(rbuf) = ep.receiver_buf.take() {
+                let copy_len = msg.len().min(rbuf.len);
+                core::ptr::copy_nonoverlapping(
+                    msg.as_ptr(),
+                    rbuf.ptr as *mut u8,
+                    copy_len,
+                );
+            }
+            // Unblock receiver
+            sched::unblock(recv_task);
+        } else {
+            // No receiver yet — block sender
+            let me = sched::current_task_index();
+            ep.sender = Some(me);
+            ep.sender_msg = Some(PendingMsg {
+                ptr: msg.as_ptr(),
+                len: msg.len(),
+            });
+            sched::block();
+            // When we wake up, the receiver has already
+            // copied our message.
+        }
+    }
+}
+
+/// Receive a message from an endpoint. Blocks until a
+/// sender is ready. Returns the number of bytes received.
+pub fn recv(ep_id: usize, buf: &mut [u8]) -> usize {
+    unsafe {
+        let eps = &mut *IPC.endpoints.get();
+
+        if ep_id >= MAX_ENDPOINTS || !eps[ep_id].active {
+            serial::print("ipc: invalid endpoint\n");
+            return 0;
+        }
+
+        let ep = &mut eps[ep_id];
+
+        // Is there a sender already waiting?
+        if let Some(send_task) = ep.sender.take() {
+            if let Some(smsg) = ep.sender_msg.take() {
+                let copy_len = smsg.len.min(buf.len());
+                core::ptr::copy_nonoverlapping(
+                    smsg.ptr,
+                    buf.as_mut_ptr(),
+                    copy_len,
+                );
+                // Unblock sender
+                sched::unblock(send_task);
+                return copy_len;
+            }
+        }
+
+        // No sender yet — block receiver
+        let me = sched::current_task_index();
+        ep.receiver = Some(me);
+        ep.receiver_buf = Some(PendingMsg {
+            ptr: buf.as_mut_ptr(),
+            len: buf.len(),
+        });
+        sched::block();
+        // When we wake up, the sender has copied into buf.
+        // We don't know the exact length here, so return
+        // buf.len() as upper bound. A real implementation
+        // would store the actual length.
+        buf.len()
+    }
+}
+
+/// Call: send a message and wait for a reply on the same
+/// endpoint (RPC pattern). Returns bytes received in reply.
+pub fn call(
+    ep_id: usize,
+    msg: &[u8],
+    reply_buf: &mut [u8],
+) -> usize {
+    send(ep_id, msg);
+    recv(ep_id, reply_buf)
+}
+
+/// Read the CPU timestamp counter (cycles).
+pub fn rdtsc() -> u64 {
+    let lo: u32;
+    let hi: u32;
+    unsafe {
+        core::arch::asm!(
+            "rdtsc",
+            out("eax") lo,
+            out("edx") hi,
+            options(nostack, nomem),
+        );
+    }
+    ((hi as u64) << 32) | lo as u64
+}