kernel/ipc/
syscall.rs

1//! IPC system calls
2//!
3//! This module provides system call implementations for IPC operations
4//! such as pipe creation, message passing, and shared memory.
5
6use crate::{
7    arch::Trapframe,
8    ipc::event::{
9        Event, EventContent, EventManager, EventPayload, EventPriority, ProcessControlType,
10    },
11    ipc::pipe::UnidirectionalPipe,
12    ipc::shared_memory::SharedMemory,
13    library::std::string::parse_c_string_from_userspace,
14    object::KernelObject,
15    object::capability::EventSubscriber,
16    task::mytask,
17};
18use alloc::{string::ToString, sync::Arc};
19
20/// sys_pipe - Create a pipe pair
21///
22/// Creates a unidirectional pipe with read and write ends.
23///
24/// Arguments:
25/// - pipefd: Pointer to an array of 2 integers where file descriptors will be stored
26///   - pipefd[0] will contain the read end file descriptor
27///   - pipefd[1] will contain the write end file descriptor
28///
29/// Returns:
30/// - 0 on success
31/// - usize::MAX on error
32pub fn sys_pipe(trapframe: &mut Trapframe) -> usize {
33    let task = match mytask() {
34        Some(task) => task,
35        None => return usize::MAX,
36    };
37
38    let pipefd_ptr = trapframe.get_arg(0);
39
40    // Increment PC to avoid infinite loop if pipe creation fails
41    trapframe.increment_pc_next(task);
42
43    // Translate the pointer to get access to the pipefd array
44    let pipefd_vaddr = match task.vm_manager.translate_vaddr(pipefd_ptr) {
45        Some(addr) => addr as *mut u32,
46        None => return usize::MAX, // Invalid pointer
47    };
48
49    // Create pipe pair with default buffer size (4KB)
50    const DEFAULT_PIPE_BUFFER_SIZE: usize = 4096;
51    let (read_obj, write_obj) = UnidirectionalPipe::create_pair(DEFAULT_PIPE_BUFFER_SIZE);
52
53    // Insert into handle table with explicit IPC metadata
54    use crate::object::handle::{AccessMode, HandleMetadata, HandleType};
55
56    let read_metadata = HandleMetadata {
57        handle_type: HandleType::IpcChannel,
58        access_mode: AccessMode::ReadOnly,
59        special_semantics: None,
60    };
61
62    let write_metadata = HandleMetadata {
63        handle_type: HandleType::IpcChannel,
64        access_mode: AccessMode::WriteOnly,
65        special_semantics: None,
66    };
67
68    let read_handle = match task
69        .handle_table
70        .insert_with_metadata(read_obj, read_metadata)
71    {
72        Ok(handle) => handle,
73        Err(_) => return usize::MAX, // Too many open handles
74    };
75
76    let write_handle = match task
77        .handle_table
78        .insert_with_metadata(write_obj, write_metadata)
79    {
80        Ok(handle) => handle,
81        Err(_) => {
82            // Clean up the read handle if write handle allocation fails
83            let _ = task.handle_table.remove(read_handle);
84            return usize::MAX;
85        }
86    };
87
88    // Write the handles to user space
89    unsafe {
90        *pipefd_vaddr = read_handle;
91        *pipefd_vaddr.add(1) = write_handle;
92    }
93
94    0 // Success
95}
96
97/// sys_pipe2 - Create a pipe pair with flags (future implementation)
98///
99/// Extended version of sys_pipe that supports flags for controlling
100/// pipe behavior (e.g., O_NONBLOCK, O_CLOEXEC).
101pub fn sys_pipe2(trapframe: &mut Trapframe) -> usize {
102    let _pipefd_ptr = trapframe.get_arg(0);
103    let _flags = trapframe.get_arg(1);
104
105    // For now, just call the basic sys_pipe implementation
106    // TODO: Implement flag handling
107    sys_pipe(trapframe)
108}
109
110// === Event IPC (Handle-based) ===
111
112/// Create or open an event channel by name and return a handle (EventChannel)
113///
114/// Arguments:
115/// - name_ptr: const char* (C-string) channel name
116///
117/// Returns: handle on success, usize::MAX on error
118pub fn sys_event_channel_create(trapframe: &mut Trapframe) -> usize {
119    let task = match mytask() {
120        Some(task) => task,
121        None => return usize::MAX,
122    };
123
124    let name_ptr = trapframe.get_arg(0);
125    trapframe.increment_pc_next(task);
126
127    let name = match parse_c_string_from_userspace(task, name_ptr, 256) {
128        Ok(s) => s,
129        Err(_) => return usize::MAX,
130    };
131
132    let mgr = EventManager::get_manager();
133    let ko = mgr.create_channel(name);
134    match task.handle_table.insert(ko) {
135        Ok(h) => h as usize,
136        Err(_) => usize::MAX,
137    }
138}
139
140/// Subscribe current task to a channel by name, returning an EventSubscription handle
141///
142/// Arguments:
143/// - name_ptr: const char* (C-string) channel name
144///
145/// Returns: handle on success, usize::MAX on error
146pub fn sys_event_subscribe(trapframe: &mut Trapframe) -> usize {
147    let task = match mytask() {
148        Some(task) => task,
149        None => return usize::MAX,
150    };
151
152    let name_ptr = trapframe.get_arg(0);
153    trapframe.increment_pc_next(task);
154
155    let name = match parse_c_string_from_userspace(task, name_ptr, 256) {
156        Ok(s) => s,
157        Err(_) => return usize::MAX,
158    };
159
160    let mgr = EventManager::get_manager();
161    let ko = match mgr.create_subscription(name, task.get_id() as u32) {
162        Ok(ko) => ko,
163        Err(_) => return usize::MAX,
164    };
165    match task.handle_table.insert(ko) {
166        Ok(h) => h as usize,
167        Err(_) => usize::MAX,
168    }
169}
170
171/// Unsubscribe and close an EventSubscription handle
172///
173/// Arguments:
174/// - sub_handle: u32 subscription handle
175///
176/// Returns: 0 on success, usize::MAX on error
177pub fn sys_event_unsubscribe(trapframe: &mut Trapframe) -> usize {
178    let task = match mytask() {
179        Some(task) => task,
180        None => return usize::MAX,
181    };
182
183    let handle = trapframe.get_arg(0) as u32;
184    trapframe.increment_pc_next(task);
185
186    // Get the object first to extract identifiers
187    let (channel_name, subscription_id) = match task.handle_table.get(handle) {
188        Some(KernelObject::EventSubscription(sub)) => (
189            sub.channel_name().to_string(),
190            sub.subscription_id().to_string(),
191        ),
192        _ => return usize::MAX,
193    };
194
195    // Remove from channel registry via EventManager helper
196    let mgr = EventManager::get_manager();
197    let _ = mgr.remove_subscription_from_channel(&channel_name, &subscription_id);
198
199    // Finally remove handle (drop Arc)
200    match task.handle_table.remove(handle) {
201        Some(_) => 0,
202        None => usize::MAX,
203    }
204}
205
206/// Publish a custom integer event to a channel using a channel handle
207///
208/// Arguments:
209/// - channel_handle: u32 (EventChannel)
210/// - event_id: u32 (custom event id in "user" namespace)
211/// - payload: isize (integer payload)
212///
213/// Returns: 0 on success, usize::MAX on error
214pub fn sys_event_publish(trapframe: &mut Trapframe) -> usize {
215    let task = match mytask() {
216        Some(task) => task,
217        None => return usize::MAX,
218    };
219
220    let channel_handle = trapframe.get_arg(0) as u32;
221    let event_id = trapframe.get_arg(1) as u32;
222    let payload_val = trapframe.get_arg(2) as isize as i64;
223    trapframe.increment_pc_next(task);
224
225    let ko = match task.handle_table.get(channel_handle) {
226        Some(obj) => obj,
227        None => return usize::MAX,
228    };
229
230    let channel = match ko.as_event_channel() {
231        Some(ch) => ch,
232        None => return usize::MAX,
233    };
234
235    let ev = Event::channel(
236        channel.name().to_string(),
237        EventContent::Custom {
238            namespace: "user".into(),
239            event_id,
240        },
241        false,
242        EventPriority::Normal,
243        EventPayload::Integer(payload_val),
244    );
245
246    match channel.broadcast_to_subscribers(ev) {
247        Ok(()) => 0,
248        Err(_) => usize::MAX,
249    }
250}
251
252/// Register a filter on an EventSubscription handle
253///
254/// Arguments:
255/// - sub_handle: u32
256/// - handler_id: usize
257/// - filter_kind: u32
258///   - 0: All
259///   - 1: Sender(param0)
260///   - 2: EventId(param0)
261///   - 3: DirectType(param0)
262/// - param0: u32 (used depending on filter_kind)
263pub fn sys_event_handler_register(trapframe: &mut Trapframe) -> usize {
264    let task = match mytask() {
265        Some(task) => task,
266        None => return usize::MAX,
267    };
268
269    let sub_handle = trapframe.get_arg(0) as u32;
270    let handler_id = trapframe.get_arg(1);
271    let filter_kind = trapframe.get_arg(2) as u32;
272    let param0 = trapframe.get_arg(3) as u32;
273    trapframe.increment_pc_next(task);
274
275    let ko = match task.handle_table.get(sub_handle) {
276        Some(obj) => obj,
277        None => return usize::MAX,
278    };
279    let sub = match ko.as_event_subscription() {
280        Some(s) => s,
281        None => return usize::MAX,
282    };
283
284    use crate::ipc::event::{EventFilter, EventTypeFilter};
285    let filter = match filter_kind {
286        0 => EventFilter::All,
287        1 => EventFilter::Sender(param0),
288        2 => EventFilter::EventId(param0),
289        3 => EventFilter::EventType(EventTypeFilter::Direct(param0)),
290        _ => EventFilter::All,
291    };
292
293    match sub.register_filter(filter, handler_id) {
294        Ok(()) => 0,
295        Err(_) => usize::MAX,
296    }
297}
298
299/// Send a direct process control event to a target task
300///
301/// Arguments:
302/// - target_tid: u32
303/// - kind: u32 (0=Terminate,1=Kill,2=Stop,3=Continue,4=Interrupt,5=Quit,6=Hangup,7=ChildExit,8=PipeBroken,9=Alarm,10=IoReady,1000+=User(kind-1000))
304/// - reliable: u32 (0/1)
305/// - priority: u32 (1=Low,2=Normal,3=High,4=Critical)
306pub fn sys_event_send_direct(trapframe: &mut Trapframe) -> usize {
307    let task = match mytask() {
308        Some(task) => task,
309        None => return usize::MAX,
310    };
311    let target = trapframe.get_arg(0) as u32;
312    let kind = trapframe.get_arg(1) as u32;
313    let reliable = trapframe.get_arg(2) as u32 != 0;
314    let prio_raw = trapframe.get_arg(3) as u32;
315    trapframe.increment_pc_next(task);
316
317    let priority = match prio_raw {
318        1 => EventPriority::Low,
319        3 => EventPriority::High,
320        4 => EventPriority::Critical,
321        _ => EventPriority::Normal,
322    };
323
324    let event = if kind >= 1000 {
325        Event::direct_custom(
326            target,
327            "user".into(),
328            kind - 1000,
329            priority,
330            reliable,
331            EventPayload::Empty,
332        )
333    } else {
334        let ptype = match kind {
335            0 => ProcessControlType::Terminate,
336            1 => ProcessControlType::Kill,
337            2 => ProcessControlType::Stop,
338            3 => ProcessControlType::Continue,
339            4 => ProcessControlType::Interrupt,
340            5 => ProcessControlType::Quit,
341            6 => ProcessControlType::Hangup,
342            7 => ProcessControlType::ChildExit,
343            8 => ProcessControlType::PipeBroken,
344            9 => ProcessControlType::Alarm,
345            10 => ProcessControlType::IoReady,
346            _ => ProcessControlType::Terminate,
347        };
348        Event::direct_process_control(target, ptype, priority, reliable)
349    };
350
351    let mgr = EventManager::get_manager();
352    match mgr.send_event(event) {
353        Ok(()) => 0,
354        Err(_) => usize::MAX,
355    }
356}
357
358/// sys_shared_memory_create - Create a shared memory object
359///
360/// Creates a shared memory region that can be mapped into multiple processes.
361///
362/// Arguments:
363/// - size: Size of the shared memory region in bytes
364/// - permissions: Access permissions (read/write/execute flags)
365///   - 0x1: Read permission
366///   - 0x2: Write permission
367///   - 0x4: Execute permission
368///
369/// Returns:
370/// - Handle to the shared memory object on success
371/// - usize::MAX on error
372pub fn sys_shared_memory_create(trapframe: &mut Trapframe) -> usize {
373    let task = match mytask() {
374        Some(task) => task,
375        None => return usize::MAX,
376    };
377
378    let size = trapframe.get_arg(0);
379    let permissions = trapframe.get_arg(1);
380
381    // Increment PC to avoid infinite loop if creation fails
382    trapframe.increment_pc_next(task);
383
384    // Validate size (must be non-zero and reasonable)
385    if size == 0 || size > 1024 * 1024 * 1024 {
386        // Max 1GB
387        return usize::MAX;
388    }
389
390    // Create the shared memory object
391    let shmem = match SharedMemory::new(size, permissions) {
392        Ok(shmem) => shmem,
393        Err(_) => return usize::MAX,
394    };
395
396    // Wrap in KernelObject
397    let kernel_obj = KernelObject::from_shared_memory_object(Arc::new(shmem));
398
399    // Insert into handle table with IPC metadata
400    use crate::object::handle::{AccessMode, HandleMetadata, HandleType};
401
402    let metadata = HandleMetadata {
403        handle_type: HandleType::IpcChannel,
404        access_mode: if permissions & 0x3 == 0x3 {
405            AccessMode::ReadWrite
406        } else if permissions & 0x2 != 0 {
407            AccessMode::WriteOnly
408        } else {
409            AccessMode::ReadOnly
410        },
411        special_semantics: None,
412    };
413
414    let handle = match task.handle_table.insert_with_metadata(kernel_obj, metadata) {
415        Ok(handle) => handle,
416        Err(_) => return usize::MAX, // Too many open handles
417    };
418
419    handle as usize
420}
421
422/// sys_shared_memory_resize - Resize a shared memory object
423///
424/// Arguments:
425/// - handle: Handle to the shared memory object
426/// - size: New size in bytes (will be page-aligned in kernel)
427///
428/// Returns:
429/// - 0 on success
430/// - usize::MAX on error
431pub fn sys_shared_memory_resize(trapframe: &mut Trapframe) -> usize {
432    let task = match mytask() {
433        Some(task) => task,
434        None => return usize::MAX,
435    };
436
437    let handle = trapframe.get_arg(0) as u32;
438    let size = trapframe.get_arg(1);
439
440    crate::println!("[sys_shared_memory_resize] handle={} size={}", handle, size);
441
442    trapframe.increment_pc_next(task);
443
444    let kernel_obj = match task.handle_table.get(handle) {
445        Some(obj) => obj,
446        None => {
447            crate::println!("[sys_shared_memory_resize] handle not found");
448            return usize::MAX;
449        }
450    };
451
452    let shared_memory = match kernel_obj.as_shared_memory() {
453        Some(obj) => obj,
454        None => {
455            crate::println!("[sys_shared_memory_resize] not a shared memory object");
456            return usize::MAX;
457        }
458    };
459
460    if let Err(e) = shared_memory.resize(size) {
461        crate::println!("[sys_shared_memory_resize] resize failed: {}", e);
462        return usize::MAX;
463    }
464
465    crate::println!(
466        "[sys_shared_memory_resize] SUCCESS new_size={}",
467        shared_memory.size()
468    );
469    0
470}
471
472/// sys_socket_send_handle - Send a kernel object handle through a socket
473///
474/// Transfers a kernel object (like SharedMemoryObject) to another task
475/// through a connected socket, similar to Unix SCM_RIGHTS functionality.
476/// Uses dup() semantics - the handle is duplicated, not moved, so both
477/// sender and receiver will have independent references to the same object.
478///
479/// Arguments:
480/// - socket_handle: Handle to the connected socket
481/// - object_handle: Handle to the kernel object to send (remains valid after send)
482///
483/// Returns:
484/// - 0 on success
485/// - usize::MAX on error
486pub fn sys_socket_send_handle(trapframe: &mut Trapframe) -> usize {
487    let task = match mytask() {
488        Some(task) => task,
489        None => return usize::MAX,
490    };
491
492    let socket_handle = trapframe.get_arg(0) as u32;
493    let object_handle = trapframe.get_arg(1) as u32;
494
495    // Increment PC to avoid infinite loop
496    trapframe.increment_pc_next(task);
497
498    // Get the socket object (LocalSocket-only)
499    let socket_obj = match task.handle_table.get(socket_handle) {
500        Some(KernelObject::Socket(socket)) => socket.clone(),
501        _ => return usize::MAX, // Invalid socket handle
502    };
503
504    use crate::network::local::LocalSocket;
505    let local_socket = match LocalSocket::from_socket_object(&socket_obj) {
506        Some(s) => s,
507        None => return usize::MAX, // Not a LocalSocket
508    };
509
510    // Get the kernel object to send with dup semantics
511    // Use clone_for_dup() to properly increment reference counts for objects like Pipes
512    let object = match task.handle_table.clone_for_dup(object_handle) {
513        Some(obj) => obj,
514        None => return usize::MAX, // Invalid object handle
515    };
516
517    // Send the handle through the socket
518    match local_socket.send_handle(object) {
519        Ok(()) => 0,
520        Err(_) => usize::MAX,
521    }
522}
523
524/// sys_socket_recv_handle - Receive a kernel object handle from a socket
525///
526/// Receives a kernel object that was sent by a peer task through a
527/// connected socket.
528///
529/// Arguments:
530/// - socket_handle: Handle to the connected socket
531///
532/// Returns:
533/// - Handle to the received kernel object on success
534/// - usize::MAX on error (no handle available or other error)
535pub fn sys_socket_recv_handle(trapframe: &mut Trapframe) -> usize {
536    let task = match mytask() {
537        Some(task) => task,
538        None => return usize::MAX,
539    };
540
541    let socket_handle = trapframe.get_arg(0) as u32;
542
543    // Increment PC to avoid infinite loop
544    trapframe.increment_pc_next(task);
545
546    // Get the socket object (LocalSocket-only)
547    let socket_obj = match task.handle_table.get(socket_handle) {
548        Some(KernelObject::Socket(socket)) => socket.clone(),
549        _ => return usize::MAX, // Invalid socket handle
550    };
551
552    // For LocalSocket, we provide blocking semantics: if the handle queue is empty,
553    // block the task until a handle arrives or the peer is closed.
554    use crate::network::local::LocalSocket;
555    let local_socket = match LocalSocket::from_socket_object(&socket_obj) {
556        Some(s) => s,
557        None => return usize::MAX, // Not a LocalSocket
558    };
559
560    let object = match local_socket.recv_handle_blocking(task.get_id(), trapframe) {
561        Ok(obj) => obj,
562        Err(_) => return usize::MAX,
563    };
564
565    // Insert the received object into this task's handle table
566    match task.handle_table.insert(object) {
567        Ok(handle) => handle as usize,
568        Err(_) => usize::MAX, // Too many open handles
569    }
570}
571
572/// sys_socket_send_handle_and_data - Send a kernel object handle and data atomically
573///
574/// Sends a kernel object handle through a connected socket, along with data.
575/// This ensures both the handle and data are available before waking the peer,
576/// preventing race conditions in protocols like Wayland.
577///
578/// Arguments:
579/// - socket_handle: Handle to the connected socket
580/// - object_handle: Handle to the kernel object to send
581/// - data_ptr: Pointer to the data buffer
582/// - data_len: Length of the data buffer
583///
584/// Returns:
585/// - 0 on success
586/// - usize::MAX on error
587pub fn sys_socket_send_handle_and_data(trapframe: &mut Trapframe) -> usize {
588    let task = match mytask() {
589        Some(task) => task,
590        None => return usize::MAX,
591    };
592
593    let socket_handle = trapframe.get_arg(0) as u32;
594    let object_handle = trapframe.get_arg(1) as u32;
595    let data_ptr = trapframe.get_arg(2);
596    let data_len = trapframe.get_arg(3);
597
598    // Increment PC to avoid infinite loop
599    trapframe.increment_pc_next(task);
600
601    // Get the socket object (LocalSocket-only)
602    let socket_obj = match task.handle_table.get(socket_handle) {
603        Some(KernelObject::Socket(socket)) => socket.clone(),
604        _ => return usize::MAX, // Invalid socket handle
605    };
606
607    use crate::network::local::LocalSocket;
608    let local_socket = match LocalSocket::from_socket_object(&socket_obj) {
609        Some(s) => s,
610        None => return usize::MAX, // Not a LocalSocket
611    };
612
613    // Get the kernel object to send with dup semantics
614    let object = match task.handle_table.clone_for_dup(object_handle) {
615        Some(obj) => obj,
616        None => return usize::MAX, // Invalid object handle
617    };
618
619    // Validate and translate data pointer
620    if data_len == 0 {
621        // No data to send, just send the handle
622        match local_socket.send_handle(object) {
623            Ok(()) => return 0,
624            Err(_) => return usize::MAX,
625        }
626    }
627
628    let data_addr = match task.vm_manager.translate_vaddr(data_ptr) {
629        Some(addr) => addr,
630        None => return usize::MAX, // Invalid pointer
631    };
632
633    // Limit data size to prevent DoS attacks
634    const MAX_SEND_SIZE: usize = 65536; // 64 KB max
635    let data_len = data_len.min(MAX_SEND_SIZE);
636
637    // Read data from userspace
638    let data = unsafe { core::slice::from_raw_parts(data_addr as *const u8, data_len) };
639
640    // Send the handle and data atomically
641    match local_socket.send_handle_and_data(object, data) {
642        Ok(()) => 0,
643        Err(_) => usize::MAX,
644    }
645}
646
647/// sys_socket_recv_handle_and_data - Receive a kernel object handle and data atomically
648///
649/// Receives both a kernel object handle and data in a single atomic operation.
650/// This is the counterpart to send_handle_and_data.
651///
652/// Arguments:
653/// - socket_handle: Handle to the connected socket
654/// - handle_ptr: Pointer to store the received handle (output)
655/// - data_ptr: Pointer to store the received data (output)
656/// - max_data_len: Maximum amount of data to receive
657///
658/// Returns:
659/// - Number of bytes received on success
660/// - usize::MAX on error
661pub fn sys_socket_recv_handle_and_data(trapframe: &mut Trapframe) -> usize {
662    let task = match mytask() {
663        Some(task) => task,
664        None => return usize::MAX,
665    };
666
667    let socket_handle = trapframe.get_arg(0) as u32;
668    let handle_ptr = trapframe.get_arg(1);
669    let data_ptr = trapframe.get_arg(2);
670    let max_data_len = trapframe.get_arg(3);
671
672    // Increment PC to avoid infinite loop
673    trapframe.increment_pc_next(task);
674
675    // Get the socket object (LocalSocket-only)
676    let socket_obj = match task.handle_table.get(socket_handle) {
677        Some(KernelObject::Socket(socket)) => socket.clone(),
678        _ => return usize::MAX, // Invalid socket handle
679    };
680
681    use crate::network::local::LocalSocket;
682    let local_socket = match LocalSocket::from_socket_object(&socket_obj) {
683        Some(s) => s,
684        None => return usize::MAX, // Not a LocalSocket
685    };
686
687    // Limit data size to prevent DoS attacks
688    const MAX_RECV_SIZE: usize = 65536; // 64 KB max
689    let max_data_len = max_data_len.min(MAX_RECV_SIZE);
690
691    // Receive handle and data atomically
692    let (object, data) = match local_socket.recv_handle_and_data(max_data_len) {
693        Ok((h, d)) => (h, d),
694        Err(_) => return usize::MAX,
695    };
696
697    // Insert the received object into this task's handle table
698    let new_handle = match task.handle_table.insert(object) {
699        Ok(h) => h,
700        Err(_) => return usize::MAX, // Too many open handles
701    };
702
703    // Write the handle value to userspace
704    if handle_ptr != 0 {
705        let handle_addr = match task.vm_manager.translate_vaddr(handle_ptr) {
706            Some(addr) => addr as *mut u32,
707            None => return usize::MAX,
708        };
709        unsafe {
710            *handle_addr = new_handle;
711        }
712    }
713
714    // Write the data to userspace
715    if !data.is_empty() && data_ptr != 0 {
716        let data_addr = match task.vm_manager.translate_vaddr(data_ptr) {
717            Some(addr) => addr as *mut u8,
718            None => return usize::MAX,
719        };
720        unsafe {
721            core::ptr::copy_nonoverlapping(data.as_ptr(), data_addr, data.len());
722        }
723    }
724
725    data.len()
726}