commit - e1940d574f26f4e261757461655d7290fe1ff4e9
commit + 28328e7600e718a0b0b419e39f5bcdfe01cd17f4
blob - /dev/null
blob + f3c082a5af513b2829ef2c0fa93ed0d26da17839 (mode 644)
--- /dev/null
+++ src/ipc.rs
+// 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
+}