Skip to main content

strat9_kernel/syscall/
error.rs

1//! Syscall error codes for Strat9-OS.
2//!
3//! Errors are returned as negative values in RAX, matching Linux errno conventions.
4//! The dispatcher converts `SyscallError` to a negative i64 stored in RAX as u64.
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive, thiserror::Error)]
9#[must_use]
10#[repr(i64)]
11pub enum SyscallError {
12    #[error("Operation not permitted")]
13    PermissionDenied = -1,
14    #[error("No such file or directory")]
15    NotFound = -2,
16    #[error("Interrupted system call")]
17    Interrupted = -4,
18    #[error("Input/output error")]
19    IoError = -5,
20    #[error("Argument list too long")]
21    ArgumentListTooLong = -7,
22    #[error("Exec format error")]
23    ExecFormatError = -8,
24    #[error("Bad file descriptor")]
25    BadHandle = -9,
26    #[error("No child processes")]
27    NoChildren = -10,
28    #[error("Resource temporarily unavailable")]
29    Again = -11,
30    #[error("Cannot allocate memory")]
31    OutOfMemory = -12,
32    #[error("Permission denied")]
33    AccessDenied = -13,
34    #[error("Bad address")]
35    Fault = -14,
36    #[error("File exists")]
37    AlreadyExists = -17,
38    #[error("Invalid argument")]
39    InvalidArgument = -22,
40    #[error("Not a typewriter")]
41    NotATty = -25,
42    #[error("Not a directory")]
43    NotADirectory = -20,
44    #[error("Is a directory")]
45    IsADirectory = -21,
46    #[error("No space left on device")]
47    NoSpace = -28,
48    #[error("Broken pipe")]
49    Pipe = -32,
50    #[error("Result too large")]
51    Range = -34,
52    #[error("File name too long")]
53    NameTooLong = -36,
54    #[error("Function not implemented")]
55    NotImplemented = -38,
56    #[error("Directory not empty")]
57    NotEmpty = -39,
58    #[error("Not supported")]
59    NotSupported = -52,
60    #[error("No buffer space available")]
61    QueueFull = -105,
62    #[error("Connection timed out")]
63    TimedOut = -110,
64}
65
66impl SyscallError {
67    /// Converts this to raw.
68    #[inline]
69    pub fn to_raw(self) -> u64 {
70        (self as i64) as u64
71    }
72
73    /// Builds this from code.
74    pub fn from_code(code: i64) -> Self {
75        Self::try_from(code).unwrap_or(SyscallError::InvalidArgument)
76    }
77
78    /// Returns whether retryable.
79    #[inline]
80    pub fn is_retryable(self) -> bool {
81        matches!(self, SyscallError::Interrupted | SyscallError::Again)
82    }
83
84    /// Performs the name operation.
85    #[inline]
86    pub fn name(self) -> &'static str {
87        match self {
88            SyscallError::PermissionDenied => "EPERM",
89            SyscallError::NotFound => "ENOENT",
90            SyscallError::Interrupted => "EINTR",
91            SyscallError::IoError => "EIO",
92            SyscallError::ArgumentListTooLong => "E2BIG",
93            SyscallError::ExecFormatError => "ENOEXEC",
94            SyscallError::BadHandle => "EBADF",
95            SyscallError::NoChildren => "ECHILD",
96            SyscallError::Again => "EAGAIN",
97            SyscallError::OutOfMemory => "ENOMEM",
98            SyscallError::AccessDenied => "EACCES",
99            SyscallError::Fault => "EFAULT",
100            SyscallError::AlreadyExists => "EEXIST",
101            SyscallError::InvalidArgument => "EINVAL",
102            SyscallError::NotADirectory => "ENOTDIR",
103            SyscallError::IsADirectory => "EISDIR",
104            SyscallError::NotATty => "ENOTTY",
105            SyscallError::NoSpace => "ENOSPC",
106            SyscallError::Pipe => "EPIPE",
107            SyscallError::Range => "ERANGE",
108            SyscallError::NameTooLong => "ENAMETOOLONG",
109            SyscallError::NotImplemented => "ENOSYS",
110            SyscallError::NotEmpty => "ENOTEMPTY",
111            SyscallError::NotSupported => "ENOTSUP",
112            SyscallError::QueueFull => "ENOBUFS",
113            SyscallError::TimedOut => "ETIMEDOUT",
114        }
115    }
116}
117
118// ── From impls for kernel-internal error types ──────────────────────────────
119
120impl From<core::str::Utf8Error> for SyscallError {
121    /// Performs the from operation.
122    #[inline]
123    fn from(_: core::str::Utf8Error) -> Self {
124        SyscallError::InvalidArgument
125    }
126}
127
128impl From<crate::ostd::util::Error> for SyscallError {
129    /// Performs the from operation.
130    fn from(err: crate::ostd::util::Error) -> Self {
131        use crate::ostd::util::Error;
132        match err {
133            Error::OutOfMemory => SyscallError::OutOfMemory,
134            Error::InvalidArgument => SyscallError::InvalidArgument,
135            Error::NotFound => SyscallError::NotFound,
136            Error::AlreadyExists => SyscallError::AlreadyExists,
137            Error::PermissionDenied => SyscallError::PermissionDenied,
138            Error::Busy => SyscallError::Again,
139            Error::PageFault => SyscallError::Fault,
140            Error::ArchError(_) => SyscallError::IoError,
141        }
142    }
143}
144
145impl From<crate::ostd::mm::MapError> for SyscallError {
146    /// Performs the from operation.
147    fn from(err: crate::ostd::mm::MapError) -> Self {
148        use crate::ostd::mm::MapError;
149        match err {
150            MapError::OutOfBounds => SyscallError::InvalidArgument,
151            MapError::NotOwner => SyscallError::PermissionDenied,
152            MapError::AlreadyMapped => SyscallError::AlreadyExists,
153            MapError::InvalidAddress => SyscallError::InvalidArgument,
154            MapError::OutOfMemory => SyscallError::OutOfMemory,
155            MapError::ArchError(_) => SyscallError::IoError,
156        }
157    }
158}
159
160impl From<crate::hardware::storage::virtio_block::BlockError> for SyscallError {
161    /// Performs the from operation.
162    fn from(err: crate::hardware::storage::virtio_block::BlockError) -> Self {
163        use crate::hardware::storage::virtio_block::BlockError;
164        match err {
165            BlockError::IoError => SyscallError::IoError,
166            BlockError::InvalidSector => SyscallError::InvalidArgument,
167            BlockError::BufferTooSmall => SyscallError::InvalidArgument,
168            BlockError::NotReady => SyscallError::Again,
169        }
170    }
171}
172
173impl From<crate::ipc::port::IpcError> for SyscallError {
174    /// Performs the from operation.
175    fn from(err: crate::ipc::port::IpcError) -> Self {
176        use crate::ipc::port::IpcError;
177        match err {
178            IpcError::PortNotFound => SyscallError::NotFound,
179            IpcError::NotOwner => SyscallError::PermissionDenied,
180            IpcError::PortDestroyed => SyscallError::Pipe,
181        }
182    }
183}
184
185impl From<net_core::NetError> for SyscallError {
186    /// Performs the from operation.
187    fn from(err: net_core::NetError) -> Self {
188        use net_core::NetError;
189        match err {
190            NetError::NoPacket => SyscallError::Again,
191            NetError::TxQueueFull => SyscallError::QueueFull,
192            NetError::BufferTooSmall => SyscallError::InvalidArgument,
193            NetError::NotReady => SyscallError::Again,
194            NetError::LinkDown => SyscallError::IoError,
195            NetError::DeviceNotFound => SyscallError::NotImplemented,
196        }
197    }
198}
199
200impl From<crate::ipc::channel::ChannelError> for SyscallError {
201    /// Performs the from operation.
202    fn from(err: crate::ipc::channel::ChannelError) -> Self {
203        use crate::ipc::channel::ChannelError;
204        match err {
205            ChannelError::WouldBlock => SyscallError::Again,
206            ChannelError::Disconnected => SyscallError::Pipe,
207        }
208    }
209}