veridian_kernel/pkg/sdk/syscall_api.rs
1//! Syscall Wrapper Types for VeridianOS
2//!
3//! Type-safe wrappers defining the VeridianOS system call interface. These are
4//! contract definitions for user-space libraries; the actual implementations
5//! use architecture-specific syscall instructions at runtime.
6//!
7//! NOTE: Many types in this module are forward declarations for user-space
8//! APIs. They will be exercised when user-space process execution is
9//! functional. See TODO(user-space) markers for specific activation points.
10
11// User-space SDK forward declarations -- see module doc TODO(user-space)
12
13#[cfg(feature = "alloc")]
14use alloc::string::String;
15use core::fmt;
16
17/// Error codes returned by VeridianOS system calls.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum SyscallError {
20 /// One or more arguments are invalid.
21 InvalidArgument,
22 /// Caller lacks the required capability or permission.
23 PermissionDenied,
24 /// The requested resource was not found.
25 NotFound,
26 /// Insufficient memory to complete the operation.
27 OutOfMemory,
28 /// The resource already exists.
29 AlreadyExists,
30 /// The operation timed out.
31 Timeout,
32 /// The operation would block and non-blocking mode was requested.
33 WouldBlock,
34 /// The operation is not supported on this object.
35 NotSupported,
36 /// The system call is not yet implemented.
37 NotImplemented,
38 /// An I/O error occurred.
39 IoError,
40 /// The file descriptor or handle is invalid.
41 BadDescriptor,
42 /// The buffer provided is too small.
43 BufferTooSmall,
44 /// An internal kernel error occurred.
45 InternalError,
46}
47
48impl fmt::Display for SyscallError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::InvalidArgument => write!(f, "invalid argument"),
52 Self::PermissionDenied => write!(f, "permission denied"),
53 Self::NotFound => write!(f, "not found"),
54 Self::OutOfMemory => write!(f, "out of memory"),
55 Self::AlreadyExists => write!(f, "already exists"),
56 Self::Timeout => write!(f, "operation timed out"),
57 Self::WouldBlock => write!(f, "operation would block"),
58 Self::NotSupported => write!(f, "not supported"),
59 Self::NotImplemented => write!(f, "not implemented"),
60 Self::IoError => write!(f, "I/O error"),
61 Self::BadDescriptor => write!(f, "bad descriptor"),
62 Self::BufferTooSmall => write!(f, "buffer too small"),
63 Self::InternalError => write!(f, "internal error"),
64 }
65 }
66}
67
68/// Result type for system call operations.
69pub type SyscallResult<T> = Result<T, SyscallError>;
70
71/// Basic package information returned by `sys_pkg_query`.
72#[cfg(feature = "alloc")]
73#[derive(Debug, Clone)]
74pub struct PackageInfo {
75 /// Package name.
76 pub name: String,
77 /// Version string (semver).
78 pub version: String,
79 /// Whether the package is currently installed.
80 pub installed: bool,
81}
82
83// ============================================================================
84// Process Syscalls
85// ============================================================================
86
87/// Fork the current process, creating a new child process.
88///
89/// Creates a near-identical copy of the calling process. The child process
90/// receives a copy of the parent's address space (using copy-on-write pages
91/// for efficiency), file descriptor table, and capability set. The child
92/// inherits all capabilities that have the `INHERIT` flag set on them.
93///
94/// Both the parent and child return from this call, but with different return
95/// values so they can distinguish themselves. The child process receives a
96/// new unique PID and has its parent PID set to the calling process.
97///
98/// # Returns
99///
100/// - `Ok(0)` in the child process.
101/// - `Ok(child_pid)` in the parent process, where `child_pid` is the PID of the
102/// newly created child.
103///
104/// # Errors
105///
106/// - [`SyscallError::OutOfMemory`] - Insufficient memory to create the child
107/// process control block or initial page tables.
108/// - [`SyscallError::PermissionDenied`] - The caller lacks the capability to
109/// create new processes.
110/// - [`SyscallError::InternalError`] - The process table is full or an
111/// unexpected kernel error occurred.
112///
113/// # Examples
114///
115/// ```no_run
116/// use veridian_kernel::pkg::sdk::syscall_api::{sys_exit, sys_fork};
117///
118/// let result = sys_fork();
119/// match result {
120/// Ok(0) => {
121/// // Child process: execute child-specific logic
122/// sys_exit(0);
123/// }
124/// Ok(child_pid) => {
125/// // Parent process: child_pid contains the new child's PID
126/// // Optionally wait for the child with sys_wait(child_pid)
127/// }
128/// Err(e) => {
129/// // Fork failed, handle the error
130/// }
131/// }
132/// ```
133pub fn sys_fork() -> SyscallResult<u64> {
134 // TODO(user-space): Requires user-space process execution.
135 // Implementation will use architecture-specific syscall instruction
136 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
137 Err(SyscallError::NotImplemented)
138}
139
140/// Replace the current process image with a new program loaded from an
141/// ELF executable.
142///
143/// Loads the ELF binary at the given `path`, replaces the calling process's
144/// address space with the new program's segments, and begins execution at the
145/// ELF entry point. The process PID remains the same, but the virtual memory
146/// layout, stack, and instruction pointer are all replaced.
147///
148/// Capabilities marked with the `INHERIT` flag are preserved across the exec
149/// boundary; all other capabilities are dropped. File descriptors marked
150/// close-on-exec are closed; remaining descriptors are inherited by the new
151/// program image.
152///
153/// On success this function does not return to the caller because the process
154/// image has been replaced. On failure, the original process continues
155/// execution and an error is returned.
156///
157/// # Arguments
158///
159/// * `path` - Absolute or relative path to the ELF executable to load. The path
160/// is resolved through the VFS and must point to a regular file with execute
161/// permission. Supports statically linked and dynamically linked ELF binaries
162/// (the dynamic linker is invoked automatically for the latter).
163/// * `args` - Slice of string arguments to pass to the new program. These are
164/// placed on the new program's initial stack and made available through the
165/// standard `argc`/`argv` mechanism. The first element is conventionally the
166/// program name.
167///
168/// # Returns
169///
170/// - `Ok(())` - Never actually returned; on success the process image is
171/// replaced and execution continues at the new entry point.
172///
173/// # Errors
174///
175/// - [`SyscallError::NotFound`] - The file at `path` does not exist.
176/// - [`SyscallError::PermissionDenied`] - The caller lacks execute permission
177/// on the file or lacks the required capability.
178/// - [`SyscallError::InvalidArgument`] - The file is not a valid ELF binary,
179/// targets an incompatible architecture, or `args` exceeds the maximum
180/// argument size (typically 2 MiB).
181/// - [`SyscallError::OutOfMemory`] - Insufficient memory to load the new
182/// program segments.
183/// - [`SyscallError::IoError`] - An I/O error occurred reading the executable.
184///
185/// # Examples
186///
187/// ```no_run
188/// use veridian_kernel::pkg::sdk::syscall_api::{sys_exec, sys_exit, sys_fork, sys_wait};
189///
190/// let pid = sys_fork().expect("fork failed");
191/// if pid == 0 {
192/// // Child: replace image with /bin/ls
193/// let result = sys_exec("/bin/ls", &["/bin/ls", "-l", "/home"]);
194/// // If exec returns, it failed
195/// sys_exit(1);
196/// } else {
197/// // Parent: wait for child to finish
198/// let exit_code = sys_wait(pid).expect("wait failed");
199/// }
200/// ```
201pub fn sys_exec(_path: &str, _args: &[&str]) -> SyscallResult<()> {
202 // TODO(user-space): Requires user-space process execution.
203 // Implementation will use architecture-specific syscall instruction
204 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
205 Err(SyscallError::NotImplemented)
206}
207
208/// Terminate the current process with the given exit code.
209///
210/// Immediately terminates the calling process. All resources held by the
211/// process are released in the following order:
212///
213/// 1. All open file descriptors are closed.
214/// 2. All memory mappings are unmapped and physical frames freed.
215/// 3. All capabilities not shared with other processes are revoked.
216/// 4. The process is moved to the zombie state until the parent calls
217/// `sys_wait` to collect the exit code.
218/// 5. If the process has children, they are re-parented to the init process
219/// (PID 1).
220/// 6. A `SIGCHLD`-equivalent notification is sent to the parent process.
221///
222/// This function never returns. After the cleanup sequence completes, the
223/// scheduler selects the next runnable process.
224///
225/// # Arguments
226///
227/// * `code` - Exit status code. By convention, `0` indicates success and
228/// non-zero values indicate an error. The low 8 bits are made available to
229/// the parent via `sys_wait`.
230///
231/// # Examples
232///
233/// ```no_run
234/// use veridian_kernel::pkg::sdk::syscall_api::sys_exit;
235///
236/// // Exit successfully
237/// sys_exit(0);
238///
239/// // Exit with error code (this line is unreachable)
240/// ```
241pub fn sys_exit(_code: i32) -> ! {
242 // TODO(user-space): Requires user-space process execution.
243 // Implementation will use architecture-specific syscall instruction
244 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
245 // In the stub we loop forever since this is a diverging function.
246 loop {
247 core::hint::spin_loop();
248 }
249}
250
251/// Wait for a child process to exit and collect its exit status.
252///
253/// Blocks the calling process until the specified child process has
254/// terminated. Once the child exits, its exit code is returned and the
255/// child's zombie entry is removed from the process table (reaped).
256///
257/// If the child has already exited before this call, the exit code is
258/// returned immediately without blocking (zombie reaping). If the specified
259/// PID does not correspond to a child of the calling process, an error is
260/// returned.
261///
262/// Future implementations may support `WNOHANG`-style behavior by passing
263/// special PID values:
264/// - `pid > 0`: Wait for the specific child with that PID.
265/// - `pid == 0`: Wait for any child in the same process group (future).
266/// - `pid == u64::MAX`: Wait for any child process (future).
267///
268/// # Arguments
269///
270/// * `pid` - Process ID of the child to wait for. Must be a direct child of the
271/// calling process.
272///
273/// # Returns
274///
275/// - `Ok(exit_code)` - The exit status code of the terminated child process. By
276/// convention, `0` indicates success and non-zero values indicate an error or
277/// signal termination.
278///
279/// # Errors
280///
281/// - [`SyscallError::NotFound`] - No child process with the given `pid` exists,
282/// or the specified process is not a child of the caller.
283/// - [`SyscallError::InvalidArgument`] - The `pid` value is invalid (e.g., 0
284/// when process group waiting is not yet supported).
285/// - [`SyscallError::InternalError`] - An unexpected kernel error occurred
286/// during the wait operation.
287///
288/// # Examples
289///
290/// ```no_run
291/// use veridian_kernel::pkg::sdk::syscall_api::{sys_exit, sys_fork, sys_wait};
292///
293/// let child_pid = sys_fork().expect("fork failed");
294/// if child_pid == 0 {
295/// // Child process work
296/// sys_exit(42);
297/// } else {
298/// // Parent waits for child and retrieves exit code
299/// let exit_code = sys_wait(child_pid).expect("wait failed");
300/// // exit_code == 42
301/// }
302/// ```
303pub fn sys_wait(_pid: u64) -> SyscallResult<i32> {
304 // TODO(user-space): Requires user-space process execution.
305 // Implementation will use architecture-specific syscall instruction
306 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
307 Err(SyscallError::NotImplemented)
308}
309
310/// Return the PID of the calling process.
311///
312/// Retrieves the unique process identifier for the currently executing
313/// process. This is a lightweight operation that always succeeds; the PID
314/// is read directly from the current process control block and requires no
315/// capability checks.
316///
317/// The PID is assigned at process creation time by `sys_fork` and remains
318/// constant for the lifetime of the process. PID 0 is reserved for the
319/// idle/swapper process and PID 1 is reserved for the init process.
320///
321/// # Returns
322///
323/// The process ID of the calling process as an unsigned 64-bit integer.
324/// This call always succeeds and never returns an error.
325///
326/// # Examples
327///
328/// ```no_run
329/// use veridian_kernel::pkg::sdk::syscall_api::sys_getpid;
330///
331/// let my_pid = sys_getpid();
332/// // my_pid is the unique identifier for this process
333/// ```
334pub fn sys_getpid() -> u64 {
335 // TODO(user-space): Requires user-space process execution.
336 // Implementation will use architecture-specific syscall instruction
337 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
338 0
339}
340
341// ============================================================================
342// Memory Syscalls
343// ============================================================================
344
345/// Map memory into the calling process's virtual address space.
346///
347/// Allocates a contiguous region of virtual memory and optionally backs it
348/// with physical frames. The mapping is anonymous (zero-initialized, not
349/// file-backed). Both the requested address and length are rounded to page
350/// boundaries (4 KiB on all supported architectures).
351///
352/// The protection flags control access permissions on the mapped region.
353/// The kernel enforces W^X (write XOR execute) policy: a region cannot be
354/// both writable and executable simultaneously. To load executable code,
355/// first map as writable, write the code, then use a separate call to
356/// change protection to read-execute.
357///
358/// # Arguments
359///
360/// * `addr` - Preferred virtual address for the mapping. Pass `0` to let the
361/// kernel choose a suitable address (recommended). If non-zero, the address
362/// must be page-aligned; the kernel may adjust it or return an error if the
363/// region is already in use.
364/// * `len` - Number of bytes to map. Will be rounded up to the nearest page
365/// boundary. Must be greater than zero.
366/// * `prot` - Protection flags as a bitmask:
367/// - `PROT_READ (0x1)` - Pages may be read.
368/// - `PROT_WRITE (0x2)` - Pages may be written.
369/// - `PROT_EXEC (0x4)` - Pages may be executed.
370/// - `0` (PROT_NONE) - Pages cannot be accessed (guard pages).
371///
372/// # Returns
373///
374/// - `Ok(base_addr)` - The starting virtual address of the mapped region. When
375/// `addr` was `0`, this is the kernel-chosen address.
376///
377/// # Errors
378///
379/// - [`SyscallError::InvalidArgument`] - `len` is zero, `addr` is not
380/// page-aligned, or `prot` contains both `PROT_WRITE` and `PROT_EXEC` (W^X
381/// violation).
382/// - [`SyscallError::OutOfMemory`] - Insufficient virtual address space or
383/// physical memory to satisfy the mapping.
384/// - [`SyscallError::AlreadyExists`] - The requested fixed address range
385/// overlaps an existing mapping.
386/// - [`SyscallError::PermissionDenied`] - The caller lacks the memory
387/// management capability.
388///
389/// # Examples
390///
391/// ```no_run
392/// use veridian_kernel::pkg::sdk::syscall_api::sys_mmap;
393///
394/// // Allocate 4 KiB of read-write memory at a kernel-chosen address
395/// let prot_rw = 0x1 | 0x2; // PROT_READ | PROT_WRITE
396/// let addr = sys_mmap(0, 4096, prot_rw).expect("mmap failed");
397///
398/// // Allocate a 64 KiB guard-page-bounded region
399/// let region = sys_mmap(0, 65536, prot_rw).expect("mmap failed");
400/// ```
401pub fn sys_mmap(_addr: usize, _len: usize, _prot: u32) -> SyscallResult<usize> {
402 // TODO(user-space): Requires user-space process execution.
403 // Implementation will use architecture-specific syscall instruction
404 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
405 Err(SyscallError::NotImplemented)
406}
407
408/// Unmap a previously mapped memory region from the calling process's
409/// address space.
410///
411/// Removes the virtual memory mapping starting at `addr` for `len` bytes.
412/// Any physical frames backing the region are freed (unless shared with
413/// another process via copy-on-write or explicit sharing). Both `addr` and
414/// `len` must be page-aligned.
415///
416/// Partial unmapping is supported: if the specified range covers only part
417/// of an existing mapping, that mapping is split and only the requested
418/// portion is removed. Accessing unmapped addresses after this call will
419/// result in a page fault.
420///
421/// # Arguments
422///
423/// * `addr` - Start address of the region to unmap. Must be page-aligned (4 KiB
424/// boundary).
425/// * `len` - Number of bytes to unmap. Must be page-aligned and greater than
426/// zero. The actual unmapped range is `[addr, addr + len)`.
427///
428/// # Returns
429///
430/// - `Ok(())` on successful unmapping.
431///
432/// # Errors
433///
434/// - [`SyscallError::InvalidArgument`] - `addr` or `len` is not page-aligned,
435/// or `len` is zero.
436/// - [`SyscallError::NotFound`] - No mapping exists at the specified address
437/// range.
438/// - [`SyscallError::PermissionDenied`] - The caller lacks the memory
439/// management capability.
440///
441/// # Examples
442///
443/// ```no_run
444/// use veridian_kernel::pkg::sdk::syscall_api::{sys_mmap, sys_munmap};
445///
446/// // Map and then unmap a 4 KiB region
447/// let addr = sys_mmap(0, 4096, 0x1 | 0x2).expect("mmap failed");
448/// sys_munmap(addr, 4096).expect("munmap failed");
449/// // Accessing `addr` after this point causes a page fault
450/// ```
451pub fn sys_munmap(_addr: usize, _len: usize) -> SyscallResult<()> {
452 // TODO(user-space): Requires user-space process execution.
453 // Implementation will use architecture-specific syscall instruction
454 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
455 Err(SyscallError::NotImplemented)
456}
457
458// ============================================================================
459// IPC Syscalls
460// ============================================================================
461
462/// Send a message to an IPC endpoint.
463///
464/// Transmits a message to the specified IPC endpoint. The endpoint is
465/// identified by a capability token that the sender must possess with
466/// `WRITE` rights. The kernel validates the capability before delivering
467/// the message.
468///
469/// For small messages (64 bytes or fewer), the kernel uses a fast-path
470/// register-based transfer achieving sub-microsecond latency. For larger
471/// messages, the kernel uses zero-copy delivery by remapping the sender's
472/// pages into the receiver's address space, avoiding any data copying.
473///
474/// This call blocks until the receiver has accepted the message or until
475/// the endpoint's send queue has space (for asynchronous endpoints). If the
476/// endpoint is a synchronous rendezvous endpoint, the sender blocks until a
477/// receiver calls `sys_ipc_receive` on the same endpoint.
478///
479/// # Arguments
480///
481/// * `endpoint` - Capability token identifying the target IPC endpoint. The
482/// caller must hold this capability with at least `WRITE` rights. The token
483/// is a 64-bit value obtained from the capability system.
484/// * `msg` - Message payload as a byte slice. Must not exceed the endpoint's
485/// maximum message size (default 64 KiB). For optimal performance, keep
486/// messages at or below 64 bytes to use the register-based fast path.
487///
488/// # Returns
489///
490/// - `Ok(())` on successful delivery of the message.
491///
492/// # Errors
493///
494/// - [`SyscallError::InvalidArgument`] - `msg` exceeds the endpoint's maximum
495/// message size or is empty.
496/// - [`SyscallError::PermissionDenied`] - The caller does not hold the endpoint
497/// capability or lacks `WRITE` rights on it.
498/// - [`SyscallError::NotFound`] - The endpoint capability token is invalid or
499/// has been revoked.
500/// - [`SyscallError::WouldBlock`] - The endpoint's send queue is full and
501/// non-blocking mode was requested.
502/// - [`SyscallError::Timeout`] - The operation timed out waiting for the
503/// receiver.
504///
505/// # Examples
506///
507/// ```no_run
508/// use veridian_kernel::pkg::sdk::syscall_api::sys_ipc_send;
509///
510/// let endpoint: u64 = 0x1234; // capability token for the endpoint
511/// let message = b"hello from sender";
512/// sys_ipc_send(endpoint, message).expect("ipc send failed");
513/// ```
514pub fn sys_ipc_send(_endpoint: u64, _msg: &[u8]) -> SyscallResult<()> {
515 // TODO(user-space): Requires user-space process execution.
516 // Implementation will use architecture-specific syscall instruction
517 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
518 Err(SyscallError::NotImplemented)
519}
520
521/// Receive a message from an IPC endpoint.
522///
523/// Blocks the calling process until a message is available on the specified
524/// IPC endpoint, then copies the message into the provided buffer. The
525/// endpoint is identified by a capability token that the receiver must
526/// possess with `READ` rights.
527///
528/// For small messages (64 bytes or fewer), the data is transferred via
529/// registers on the fast path. For larger messages, the kernel uses
530/// zero-copy page remapping so the receiver sees the sender's data without
531/// any memory copy overhead.
532///
533/// The caller must provide a buffer large enough to hold the incoming
534/// message. If the buffer is too small, the message is truncated and a
535/// `BufferTooSmall` error is returned (the message remains consumed from
536/// the queue).
537///
538/// # Arguments
539///
540/// * `endpoint` - Capability token identifying the IPC endpoint to receive
541/// from. The caller must hold this capability with at least `READ` rights.
542/// * `buf` - Mutable buffer to receive the message data. Should be at least as
543/// large as the expected maximum message size for the endpoint.
544///
545/// # Returns
546///
547/// - `Ok(bytes_received)` - The number of bytes written into `buf`.
548///
549/// # Errors
550///
551/// - [`SyscallError::PermissionDenied`] - The caller does not hold the endpoint
552/// capability or lacks `READ` rights on it.
553/// - [`SyscallError::NotFound`] - The endpoint capability token is invalid or
554/// has been revoked.
555/// - [`SyscallError::BufferTooSmall`] - The provided `buf` is smaller than the
556/// incoming message. The message is truncated to fit.
557/// - [`SyscallError::WouldBlock`] - No message is available and non-blocking
558/// mode was requested.
559/// - [`SyscallError::Timeout`] - The operation timed out waiting for a message.
560///
561/// # Examples
562///
563/// ```no_run
564/// use veridian_kernel::pkg::sdk::syscall_api::sys_ipc_receive;
565///
566/// let endpoint: u64 = 0x1234; // capability token for the endpoint
567/// let mut buf = [0u8; 4096];
568/// let bytes = sys_ipc_receive(endpoint, &mut buf).expect("ipc receive failed");
569/// let received = &buf[..bytes];
570/// // Process the received message data
571/// ```
572pub fn sys_ipc_receive(_endpoint: u64, _buf: &mut [u8]) -> SyscallResult<usize> {
573 // TODO(user-space): Requires user-space process execution.
574 // Implementation will use architecture-specific syscall instruction
575 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
576 Err(SyscallError::NotImplemented)
577}
578
579// ============================================================================
580// Filesystem Syscalls
581// ============================================================================
582
583/// Open a file or directory, returning a file descriptor.
584///
585/// Resolves the given path through the VeridianOS VFS (Virtual File System)
586/// and returns a new file descriptor for the opened file. The file descriptor
587/// is an index into the per-process file descriptor table and is valid until
588/// closed with `sys_close`.
589///
590/// The caller must hold a capability granting access to the target file or
591/// its parent directory. The required capability rights depend on the open
592/// flags: `READ` for `O_RDONLY`, `WRITE` for `O_WRONLY` or `O_RDWR`, and
593/// both for `O_RDWR`.
594///
595/// # Arguments
596///
597/// * `path` - Path to the file or directory to open. Can be absolute (starting
598/// with `/`) or relative to the process's current working directory. The path
599/// is resolved through the VFS layer (RamFS, DevFS, ProcFS, or BlockFS
600/// depending on the mount point).
601/// * `flags` - Bitwise OR of open mode flags:
602/// - `O_RDONLY (0x000)` - Open for reading only.
603/// - `O_WRONLY (0x001)` - Open for writing only.
604/// - `O_RDWR (0x002)` - Open for reading and writing.
605/// - `O_CREAT (0x100)` - Create the file if it does not exist.
606/// - `O_TRUNC (0x200)` - Truncate the file to zero length if it exists.
607/// - `O_APPEND (0x400)` - Writes always append to end of file.
608/// - `O_EXCL (0x800)` - With `O_CREAT`, fail if the file already exists.
609///
610/// # Returns
611///
612/// - `Ok(fd)` - A file descriptor (unsigned 64-bit integer) for the opened
613/// file. The descriptor is the lowest available unused fd in the process's
614/// table.
615///
616/// # Errors
617///
618/// - [`SyscallError::NotFound`] - The file does not exist and `O_CREAT` was not
619/// specified, or a path component does not exist.
620/// - [`SyscallError::PermissionDenied`] - The caller lacks the required
621/// capability for the requested access mode.
622/// - [`SyscallError::AlreadyExists`] - `O_CREAT | O_EXCL` was specified and the
623/// file already exists.
624/// - [`SyscallError::InvalidArgument`] - The path is empty, contains null
625/// bytes, or `flags` contains an invalid combination.
626/// - [`SyscallError::IoError`] - An I/O error occurred accessing the underlying
627/// storage.
628///
629/// # Examples
630///
631/// ```no_run
632/// use veridian_kernel::pkg::sdk::syscall_api::{sys_close, sys_open};
633///
634/// // Open an existing file for reading
635/// let fd = sys_open("/etc/config.toml", 0x000).expect("open failed");
636///
637/// // Create a new file for writing (O_WRONLY | O_CREAT | O_TRUNC)
638/// let fd_new = sys_open("/tmp/output.txt", 0x001 | 0x100 | 0x200).expect("create failed");
639///
640/// sys_close(fd).expect("close failed");
641/// sys_close(fd_new).expect("close failed");
642/// ```
643pub fn sys_open(_path: &str, _flags: u32) -> SyscallResult<u64> {
644 // TODO(user-space): Requires user-space process execution.
645 // Implementation will use architecture-specific syscall instruction
646 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
647 Err(SyscallError::NotImplemented)
648}
649
650/// Read data from an open file descriptor.
651///
652/// Reads up to `buf.len()` bytes from the file descriptor into the
653/// provided buffer, starting at the file's current offset. The file offset
654/// is advanced by the number of bytes read. Partial reads are possible and
655/// normal: the kernel may return fewer bytes than requested if fewer are
656/// available (e.g., near end-of-file or for device files).
657///
658/// A return value of `Ok(0)` indicates end-of-file (EOF): the current offset
659/// is at or past the end of the file and no more data can be read.
660///
661/// For special file descriptors: fd 0 (stdin) reads from the process's
662/// standard input source (terminal or pipe).
663///
664/// # Arguments
665///
666/// * `fd` - File descriptor returned by a prior call to `sys_open`. Must be
667/// opened with at least `O_RDONLY` or `O_RDWR` access.
668/// * `buf` - Mutable buffer to read data into. The kernel reads at most
669/// `buf.len()` bytes. Must be non-empty.
670///
671/// # Returns
672///
673/// - `Ok(bytes_read)` - The number of bytes actually read, which may be less
674/// than `buf.len()`. Returns `Ok(0)` at end-of-file.
675///
676/// # Errors
677///
678/// - [`SyscallError::BadDescriptor`] - `fd` is not a valid open file
679/// descriptor.
680/// - [`SyscallError::PermissionDenied`] - The file descriptor was not opened
681/// for reading.
682/// - [`SyscallError::InvalidArgument`] - `buf` has zero length.
683/// - [`SyscallError::IoError`] - An I/O error occurred reading from the
684/// underlying storage or device.
685///
686/// # Examples
687///
688/// ```no_run
689/// use veridian_kernel::pkg::sdk::syscall_api::{sys_close, sys_open, sys_read};
690///
691/// let fd = sys_open("/etc/hostname", 0x000).expect("open failed");
692/// let mut buf = [0u8; 256];
693/// let bytes = sys_read(fd, &mut buf).expect("read failed");
694/// if bytes == 0 {
695/// // End of file reached
696/// }
697/// let data = &buf[..bytes];
698/// sys_close(fd).expect("close failed");
699/// ```
700pub fn sys_read(_fd: u64, _buf: &mut [u8]) -> SyscallResult<usize> {
701 // TODO(user-space): Requires user-space process execution.
702 // Implementation will use architecture-specific syscall instruction
703 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
704 Err(SyscallError::NotImplemented)
705}
706
707/// Write data to an open file descriptor.
708///
709/// Writes up to `data.len()` bytes from the provided buffer to the file
710/// descriptor, starting at the file's current offset (or at the end of
711/// file if `O_APPEND` was set). The file offset is advanced by the number
712/// of bytes written. Partial writes are possible: the kernel may write
713/// fewer bytes than requested if storage is full or for device files.
714///
715/// For special file descriptors: fd 1 (stdout) and fd 2 (stderr) write to
716/// the process's standard output, which is typically the serial console
717/// in VeridianOS.
718///
719/// # Arguments
720///
721/// * `fd` - File descriptor returned by a prior call to `sys_open`. Must be
722/// opened with at least `O_WRONLY` or `O_RDWR` access.
723/// * `data` - Byte slice containing the data to write. The kernel writes at
724/// most `data.len()` bytes. Must be non-empty.
725///
726/// # Returns
727///
728/// - `Ok(bytes_written)` - The number of bytes actually written, which may be
729/// less than `data.len()` (partial write). The caller should retry with the
730/// remaining data if a partial write occurs.
731///
732/// # Errors
733///
734/// - [`SyscallError::BadDescriptor`] - `fd` is not a valid open file
735/// descriptor.
736/// - [`SyscallError::PermissionDenied`] - The file descriptor was not opened
737/// for writing.
738/// - [`SyscallError::InvalidArgument`] - `data` has zero length.
739/// - [`SyscallError::IoError`] - An I/O error occurred writing to the
740/// underlying storage or device.
741/// - [`SyscallError::OutOfMemory`] - The filesystem has no remaining free
742/// space.
743///
744/// # Examples
745///
746/// ```no_run
747/// use veridian_kernel::pkg::sdk::syscall_api::{sys_close, sys_open, sys_write};
748///
749/// // Write to a file
750/// let fd = sys_open("/tmp/log.txt", 0x001 | 0x100).expect("open failed");
751/// let data = b"VeridianOS kernel log entry\n";
752/// let written = sys_write(fd, data).expect("write failed");
753/// sys_close(fd).expect("close failed");
754///
755/// // Write to stdout (serial console)
756/// let stdout_fd = 1;
757/// sys_write(stdout_fd, b"Hello, VeridianOS!\n").expect("write to stdout failed");
758/// ```
759pub fn sys_write(_fd: u64, _data: &[u8]) -> SyscallResult<usize> {
760 // TODO(user-space): Requires user-space process execution.
761 // Implementation will use architecture-specific syscall instruction
762 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
763 Err(SyscallError::NotImplemented)
764}
765
766/// Close an open file descriptor, releasing associated resources.
767///
768/// Releases the file descriptor so it can be reused by subsequent calls to
769/// `sys_open` or other fd-allocating syscalls. If this is the last file
770/// descriptor referring to the underlying file description, the file
771/// description is also released and any pending writes are flushed.
772///
773/// Closing a file descriptor that has already been closed is an error.
774/// Closing an fd does not guarantee data has been persisted to disk; use
775/// a sync operation for durability guarantees.
776///
777/// # Arguments
778///
779/// * `fd` - File descriptor to close. Must be a valid, currently open
780/// descriptor in the process's file descriptor table.
781///
782/// # Returns
783///
784/// - `Ok(())` on successful close.
785///
786/// # Errors
787///
788/// - [`SyscallError::BadDescriptor`] - `fd` is not a valid open file descriptor
789/// (including the case where it was already closed).
790/// - [`SyscallError::IoError`] - An I/O error occurred while flushing buffered
791/// data during close.
792///
793/// # Examples
794///
795/// ```no_run
796/// use veridian_kernel::pkg::sdk::syscall_api::{sys_close, sys_open};
797///
798/// let fd = sys_open("/etc/motd", 0x000).expect("open failed");
799/// // ... use the file descriptor ...
800/// sys_close(fd).expect("close failed");
801///
802/// // Attempting to close again would return BadDescriptor
803/// let result = sys_close(fd);
804/// assert!(result.is_err());
805/// ```
806pub fn sys_close(_fd: u64) -> SyscallResult<()> {
807 // TODO(user-space): Requires user-space process execution.
808 // Implementation will use architecture-specific syscall instruction
809 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
810 Err(SyscallError::NotImplemented)
811}
812
813// ============================================================================
814// Capability Syscalls
815// ============================================================================
816
817/// Create a new capability with the specified rights bitmask.
818///
819/// Allocates a new capability token and inserts it into the calling
820/// process's capability space. The new capability is derived from the
821/// caller's parent (root) capability; the caller can only create
822/// capabilities with a subset of rights that it already holds.
823///
824/// Capability tokens are unforgeable 64-bit values with an embedded
825/// generation counter used for efficient revocation. The token uniquely
826/// identifies the capability across the entire system.
827///
828/// # Arguments
829///
830/// * `rights` - Bitmask of rights to assign to the new capability:
831/// - `CAP_READ (0x01)` - Permission to read or receive.
832/// - `CAP_WRITE (0x02)` - Permission to write or send.
833/// - `CAP_EXECUTE (0x04)` - Permission to execute.
834/// - `CAP_GRANT (0x08)` - Permission to grant this capability to other
835/// processes via `sys_cap_grant`.
836/// - `CAP_REVOKE (0x10)` - Permission to revoke derived capabilities.
837/// - `CAP_MAP (0x20)` - Permission to map associated memory regions.
838///
839/// Rights are subtractive: you cannot create a capability with rights
840/// exceeding those of the parent capability.
841///
842/// # Returns
843///
844/// - `Ok(cap_token)` - The 64-bit capability token for the newly created
845/// capability.
846///
847/// # Errors
848///
849/// - [`SyscallError::PermissionDenied`] - The caller does not hold a parent
850/// capability with the requested rights, or lacks the ability to create new
851/// capabilities.
852/// - [`SyscallError::InvalidArgument`] - `rights` is zero or contains undefined
853/// bits.
854/// - [`SyscallError::OutOfMemory`] - The capability table is full and cannot
855/// accommodate a new entry.
856///
857/// # Examples
858///
859/// ```no_run
860/// use veridian_kernel::pkg::sdk::syscall_api::sys_cap_create;
861///
862/// // Create a read-only capability
863/// let cap_ro = sys_cap_create(0x01).expect("cap_create failed");
864///
865/// // Create a read-write-grant capability
866/// let cap_rwg = sys_cap_create(0x01 | 0x02 | 0x08).expect("cap_create failed");
867/// ```
868pub fn sys_cap_create(_rights: u64) -> SyscallResult<u64> {
869 // TODO(user-space): Requires user-space process execution.
870 // Implementation will use architecture-specific syscall instruction
871 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
872 Err(SyscallError::NotImplemented)
873}
874
875/// Grant a capability to another process, enabling cross-process resource
876/// sharing.
877///
878/// Copies the specified capability into the target process's capability
879/// space. The caller must hold the `GRANT` right (`0x08`) on the capability
880/// being transferred. The granted capability may have equal or fewer rights
881/// than the original; it can never have more rights (rights are monotonically
882/// non-increasing through the delegation chain).
883///
884/// The granted capability becomes a derived child of the original. If the
885/// original is later revoked via `sys_cap_revoke`, the derived capability
886/// in the target process is also revoked (cascade revocation).
887///
888/// # Arguments
889///
890/// * `cap` - Capability token to grant. The caller must hold this capability
891/// with the `GRANT` right.
892/// * `target` - Process ID of the target process that will receive the
893/// capability.
894///
895/// # Returns
896///
897/// - `Ok(())` on successful transfer.
898///
899/// # Errors
900///
901/// - [`SyscallError::PermissionDenied`] - The caller does not hold the `GRANT`
902/// right on the specified capability.
903/// - [`SyscallError::NotFound`] - The capability token is invalid or revoked,
904/// or the target PID does not correspond to a running process.
905/// - [`SyscallError::InvalidArgument`] - `target` is the caller's own PID
906/// (self-grant is a no-op error) or an invalid PID value.
907/// - [`SyscallError::OutOfMemory`] - The target process's capability table is
908/// full.
909///
910/// # Examples
911///
912/// ```no_run
913/// use veridian_kernel::pkg::sdk::syscall_api::{sys_cap_create, sys_cap_grant};
914///
915/// // Create a grantable read-write capability
916/// let cap = sys_cap_create(0x01 | 0x02 | 0x08).expect("cap_create failed");
917///
918/// // Grant it to process with PID 42
919/// let target_pid: u64 = 42;
920/// sys_cap_grant(cap, target_pid).expect("cap_grant failed");
921/// ```
922pub fn sys_cap_grant(_cap: u64, _target: u64) -> SyscallResult<()> {
923 // TODO(user-space): Requires user-space process execution.
924 // Implementation will use architecture-specific syscall instruction
925 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
926 Err(SyscallError::NotImplemented)
927}
928
929/// Revoke a capability and all capabilities derived from it.
930///
931/// Invalidates the specified capability token and performs cascade
932/// revocation: all capabilities that were derived from it (via
933/// `sys_cap_grant` or `sys_cap_create`) are also revoked, recursively.
934/// This ensures that once a capability is revoked, no process in the
935/// system retains access through that delegation chain.
936///
937/// The generation counter embedded in the capability token is incremented
938/// so that any cached references to the revoked token are immediately
939/// detected as stale by the kernel's O(1) capability lookup.
940///
941/// After revocation, any attempt to use the revoked token (or its
942/// derivatives) in a syscall will return `NotFound`.
943///
944/// # Arguments
945///
946/// * `cap` - Capability token to revoke. The caller must hold this capability
947/// (or its parent with `REVOKE` rights).
948///
949/// # Returns
950///
951/// - `Ok(())` on successful revocation of the capability and all its
952/// descendants.
953///
954/// # Errors
955///
956/// - [`SyscallError::NotFound`] - The capability token is invalid or was
957/// already revoked.
958/// - [`SyscallError::PermissionDenied`] - The caller does not own the
959/// capability and does not hold a parent with `REVOKE` rights.
960///
961/// # Examples
962///
963/// ```no_run
964/// use veridian_kernel::pkg::sdk::syscall_api::{sys_cap_create, sys_cap_grant, sys_cap_revoke};
965///
966/// // Create and grant a capability
967/// let cap = sys_cap_create(0x01 | 0x08).expect("cap_create failed");
968/// sys_cap_grant(cap, 42).expect("cap_grant failed");
969///
970/// // Revoke the capability -- process 42's derived copy is also revoked
971/// sys_cap_revoke(cap).expect("cap_revoke failed");
972/// ```
973pub fn sys_cap_revoke(_cap: u64) -> SyscallResult<()> {
974 // TODO(user-space): Requires user-space process execution.
975 // Implementation will use architecture-specific syscall instruction
976 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
977 Err(SyscallError::NotImplemented)
978}
979
980// ============================================================================
981// Package Syscalls
982// ============================================================================
983
984/// Install a package by name from the configured repositories.
985///
986/// Initiates a transactional package installation. The package manager
987/// resolves all dependencies using the DPLL-based SAT solver, downloads
988/// the package and its dependencies from the configured repository mirrors,
989/// verifies cryptographic signatures, and installs the files into the
990/// system sysroot. The entire operation is atomic: if any step fails, all
991/// changes are rolled back.
992///
993/// The installation process follows these steps:
994/// 1. Query configured repositories for the package and its metadata.
995/// 2. Resolve dependencies (transitive closure) using the SAT solver.
996/// 3. Download package archives (with delta updates when available).
997/// 4. Verify Ed25519/ML-DSA signatures on each archive.
998/// 5. Extract files into the target locations within the VFS.
999/// 6. Update the package database with installation records.
1000/// 7. Run post-install hooks if defined in the package manifest.
1001///
1002/// # Arguments
1003///
1004/// * `name` - Name of the package to install. Must match a package name in at
1005/// least one configured repository. Version constraints can be appended
1006/// (e.g., `"foo>=1.2.0"`) or the latest available version is selected.
1007///
1008/// # Returns
1009///
1010/// - `Ok(())` on successful installation of the package and all its
1011/// dependencies.
1012///
1013/// # Errors
1014///
1015/// - [`SyscallError::NotFound`] - The package name was not found in any
1016/// configured repository.
1017/// - [`SyscallError::PermissionDenied`] - The caller lacks the package
1018/// management capability.
1019/// - [`SyscallError::InvalidArgument`] - The package name is empty or contains
1020/// invalid characters.
1021/// - [`SyscallError::AlreadyExists`] - The exact version of the package is
1022/// already installed.
1023/// - [`SyscallError::IoError`] - A network or disk I/O error occurred during
1024/// download or extraction.
1025/// - [`SyscallError::OutOfMemory`] - Insufficient disk space to install the
1026/// package.
1027/// - [`SyscallError::InternalError`] - Dependency resolution failed (e.g.,
1028/// unsatisfiable constraints) or signature verification failed.
1029///
1030/// # Examples
1031///
1032/// ```no_run
1033/// use veridian_kernel::pkg::sdk::syscall_api::sys_pkg_install;
1034///
1035/// // Install a package by name (latest version)
1036/// sys_pkg_install("libveridian-dev").expect("install failed");
1037///
1038/// // Install with version constraint
1039/// sys_pkg_install("openssl>=3.0.0").expect("install failed");
1040/// ```
1041pub fn sys_pkg_install(_name: &str) -> SyscallResult<()> {
1042 // TODO(user-space): Requires user-space process execution.
1043 // Implementation will use architecture-specific syscall instruction
1044 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
1045 Err(SyscallError::NotImplemented)
1046}
1047
1048/// Remove an installed package from the system.
1049///
1050/// Performs a transactional package removal. Configuration files installed
1051/// by the package are preserved by default (saved with a `.veridian-save`
1052/// suffix) so the user does not lose customizations. After removal, the
1053/// package manager checks for orphaned dependencies (packages that were
1054/// installed only as dependencies of the removed package and are no longer
1055/// required) and offers to remove them.
1056///
1057/// The removal process follows these steps:
1058/// 1. Verify the package is installed and not required by other packages.
1059/// 2. Run pre-removal hooks if defined in the package manifest.
1060/// 3. Remove installed files from the VFS (preserving config files).
1061/// 4. Update the package database to mark the package as removed.
1062/// 5. Detect and optionally clean up orphaned dependencies.
1063/// 6. Run post-removal hooks if defined.
1064///
1065/// # Arguments
1066///
1067/// * `name` - Name of the installed package to remove.
1068///
1069/// # Returns
1070///
1071/// - `Ok(())` on successful removal of the package.
1072///
1073/// # Errors
1074///
1075/// - [`SyscallError::NotFound`] - The package is not currently installed.
1076/// - [`SyscallError::PermissionDenied`] - The caller lacks the package
1077/// management capability.
1078/// - [`SyscallError::InvalidArgument`] - The package name is empty or contains
1079/// invalid characters.
1080/// - [`SyscallError::NotSupported`] - The package is a critical system package
1081/// that cannot be removed, or other installed packages depend on it and
1082/// `--force` was not specified.
1083/// - [`SyscallError::IoError`] - A disk I/O error occurred during file removal.
1084///
1085/// # Examples
1086///
1087/// ```no_run
1088/// use veridian_kernel::pkg::sdk::syscall_api::sys_pkg_remove;
1089///
1090/// // Remove a package (config files are preserved)
1091/// sys_pkg_remove("libfoo").expect("remove failed");
1092/// ```
1093pub fn sys_pkg_remove(_name: &str) -> SyscallResult<()> {
1094 // TODO(user-space): Requires user-space process execution.
1095 // Implementation will use architecture-specific syscall instruction
1096 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
1097 Err(SyscallError::NotImplemented)
1098}
1099
1100/// Query information about a package by name.
1101///
1102/// Looks up the specified package in both the installed package database
1103/// and the configured repositories. Returns a [`PackageInfo`] struct
1104/// containing the package's name, version, and installation status.
1105///
1106/// This is a read-only operation that does not modify the system. It can
1107/// be used to check whether a package is installed, discover available
1108/// versions, or retrieve metadata before performing an install or remove
1109/// operation.
1110///
1111/// # Arguments
1112///
1113/// * `name` - Name of the package to query. Searches the installed package
1114/// database first, then falls back to configured repository indices.
1115///
1116/// # Returns
1117///
1118/// - `Ok(PackageInfo)` - Package metadata including:
1119/// - `name`: The canonical package name.
1120/// - `version`: The installed or latest available version string (semver).
1121/// - `installed`: `true` if the package is currently installed, `false` if it
1122/// is only available in repositories.
1123///
1124/// # Errors
1125///
1126/// - [`SyscallError::NotFound`] - The package name was not found in the
1127/// installed database or any configured repository.
1128/// - [`SyscallError::InvalidArgument`] - The package name is empty or contains
1129/// invalid characters.
1130/// - [`SyscallError::PermissionDenied`] - The caller lacks the capability to
1131/// query package information.
1132/// - [`SyscallError::IoError`] - An I/O error occurred reading the package
1133/// database or repository index.
1134///
1135/// # Examples
1136///
1137/// ```no_run
1138/// use veridian_kernel::pkg::sdk::syscall_api::sys_pkg_query;
1139///
1140/// let info = sys_pkg_query("libveridian").expect("query failed");
1141/// if info.installed {
1142/// // Package is installed, version info.version
1143/// } else {
1144/// // Package is available but not installed
1145/// }
1146/// ```
1147#[cfg(feature = "alloc")]
1148pub fn sys_pkg_query(_name: &str) -> SyscallResult<PackageInfo> {
1149 // TODO(user-space): Requires user-space process execution.
1150 // Implementation will use architecture-specific syscall instruction
1151 // (syscall on x86_64, svc on AArch64, ecall on RISC-V).
1152 Err(SyscallError::NotImplemented)
1153}