Skip to main content

starry_kernel/
error.rs

1use ax_alloc::AllocError;
2use ax_cgroup::CgroupError;
3use ax_fs_ng::BlockError;
4use ax_hal::{cache::TlbShootdownError, paging::PagingError};
5use ax_io::IoError;
6use ax_memory_set::MappingError;
7use ax_mm::MmError;
8use ax_net::NetError;
9use ax_runtime::{RuntimeError, serial::ConfigError, task::thread::TaskError};
10use axfs_ng_vfs::VfsError;
11use dma_api::DmaError;
12#[cfg(all(test, not(axtest)))]
13use rdif_block::{BlkError, RequestOp};
14#[cfg(feature = "sg2002")]
15use sg2002_tpu::{ion::IonError, tpu::error::TpuError};
16use starry_signal::SignalError;
17use starry_vm::VmError;
18use syscalls::Errno;
19
20/// The operation that gives a DMA-domain failure its Linux ABI meaning.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum DmaOperation {
23    /// Allocate storage for an exported or imported dma-buf.
24    BufferAllocation,
25    /// Program or execute a device DMA transaction.
26    DeviceIo,
27}
28
29/// Errors owned by the Starry kernel below the Linux syscall ABI boundary.
30#[derive(Debug, thiserror::Error)]
31pub enum StarryError {
32    #[error("Linux ABI operation failed: {0}")]
33    Errno(Errno),
34    #[error(transparent)]
35    Vm(#[from] VmError),
36    #[error(transparent)]
37    Signal(#[from] SignalError),
38    #[error(transparent)]
39    Mm(#[from] MmError),
40    #[error(transparent)]
41    Vfs(#[from] VfsError),
42    #[error(transparent)]
43    Mapping(#[from] MappingError),
44    #[error(transparent)]
45    Paging(#[from] PagingError),
46    /// A pending address-space TLB quarantine could not be confirmed before
47    /// the requested operation began.
48    #[error("pending address-space TLB quarantine blocked the operation: {0}")]
49    TlbShootdown(#[from] TlbShootdownError),
50    #[error(transparent)]
51    Alloc(#[from] AllocError),
52    #[error(transparent)]
53    Cgroup(#[from] CgroupError),
54    #[error(transparent)]
55    IoDomain(#[from] IoError),
56    #[error(transparent)]
57    Net(#[from] NetError),
58    #[error(transparent)]
59    Task(#[from] TaskError),
60    #[error(transparent)]
61    Runtime(#[from] RuntimeError),
62    #[error(transparent)]
63    Block(#[from] BlockError),
64    #[error(transparent)]
65    Driver(#[from] ax_driver::Error),
66    #[cfg(feature = "sg2002")]
67    #[error(transparent)]
68    Ion(#[from] IonError),
69    #[cfg(feature = "sg2002")]
70    #[error(transparent)]
71    Tpu(#[from] TpuError),
72    #[error(transparent)]
73    Format(#[from] core::fmt::Error),
74    #[error("DMA {operation:?} failed: {source}")]
75    Dma {
76        operation: DmaOperation,
77        #[source]
78        source: DmaError,
79    },
80    #[error("kernel object already exists")]
81    AlreadyExists,
82    #[error("argument list is too long")]
83    ArgumentListTooLong,
84    #[error("bad address")]
85    BadAddress,
86    #[error("bad file descriptor")]
87    BadFileDescriptor,
88    #[error("kernel object is in an invalid state")]
89    BadState,
90    #[error("broken pipe")]
91    BrokenPipe,
92    #[error("operation crosses devices")]
93    CrossesDevices,
94    #[error("filesystem traversal loop detected")]
95    FilesystemLoop,
96    #[error("illegal byte sequence")]
97    IllegalBytes,
98    #[error("operation is in progress")]
99    InProgress,
100    #[error("operation was interrupted")]
101    Interrupted,
102    /// An interrupted operation that Linux exposes as EINTR even when the
103    /// delivered handler uses SA_RESTART, such as socket I/O with SO_*TIMEO.
104    #[error("operation was interrupted without restart")]
105    InterruptedNoRestart,
106    #[error("invalid kernel data")]
107    InvalidData,
108    #[error("invalid executable image")]
109    InvalidExecutable,
110    #[error("malformed executable image")]
111    MalformedExecutable,
112    #[error("invalid kernel input")]
113    InvalidInput,
114    #[error("kernel I/O failed")]
115    Io,
116    #[error("object is a directory")]
117    IsADirectory,
118    #[error("name is too long")]
119    NameTooLong,
120    #[error("kernel allocation failed")]
121    NoMemory,
122    #[error("device does not exist")]
123    NoSuchDevice,
124    #[error("device or address does not exist")]
125    NoSuchDeviceOrAddress,
126    #[error("process does not exist")]
127    NoSuchProcess,
128    #[error("object is not a directory")]
129    NotADirectory,
130    #[error("object is not a socket")]
131    NotASocket,
132    #[error("object is not a tty")]
133    NotATty,
134    #[error("object was not found")]
135    NotFound,
136    #[error("operation is not permitted")]
137    OperationNotPermitted,
138    #[error("operation is not supported by this object")]
139    OperationNotSupported,
140    #[error("result is out of range")]
141    OutOfRange,
142    #[error("permission denied")]
143    PermissionDenied,
144    #[error("filesystem is read-only")]
145    ReadOnlyFilesystem,
146    #[error("kernel resource is busy")]
147    ResourceBusy,
148    #[error("storage is full")]
149    StorageFull,
150    #[error("operation timed out")]
151    TimedOut,
152    #[error("too many files are open")]
153    TooManyOpenFiles,
154    #[error("unexpected end of input")]
155    UnexpectedEof,
156    #[error("kernel capability is unavailable")]
157    Unsupported,
158    #[error("operation would block")]
159    WouldBlock,
160    #[error("write made no progress")]
161    WriteZero,
162}
163
164impl From<Errno> for StarryError {
165    fn from(errno: Errno) -> Self {
166        Self::Errno(errno)
167    }
168}
169
170impl From<StarryError> for VfsError {
171    fn from(error: StarryError) -> Self {
172        match error {
173            StarryError::Vfs(error) => error,
174            StarryError::BadState => Self::BadState,
175            StarryError::InvalidData => Self::InvalidData,
176            error => vfs_error_from_errno(error.linux_errno()),
177        }
178    }
179}
180
181impl StarryError {
182    /// Reports whether a readiness-driven operation must register and retry.
183    ///
184    /// Filesystem handles adapt VFS failures to `IoError` before returning,
185    /// while direct VFS operations retain `VfsError`. Readiness polling must
186    /// recognize both wrapped states without flattening unrelated failures
187    /// into kernel-owned leaf errors.
188    pub(crate) const fn is_would_block(&self) -> bool {
189        matches!(
190            self,
191            Self::WouldBlock
192                | Self::Vfs(VfsError::WouldBlock)
193                | Self::IoDomain(IoError::WouldBlock)
194        )
195    }
196
197    /// Convert an internal domain failure to its Linux syscall ABI errno.
198    pub fn linux_errno(&self) -> Errno {
199        match self {
200            Self::Errno(errno) => *errno,
201            Self::Vm(error) => vm_errno(*error),
202            Self::Signal(SignalError::UserMemory(error)) => vm_errno(*error),
203            Self::Mm(error) => mm_errno(*error),
204            Self::Vfs(error) => vfs_errno(*error),
205            Self::Mapping(error) => mapping_errno(error),
206            Self::Paging(error) => match error {
207                PagingError::NoMemory => Errno::ENOMEM,
208                _ => Errno::EINVAL,
209            },
210            Self::TlbShootdown(error) => tlb_errno(*error),
211            Self::Alloc(error) => alloc_errno(*error),
212            Self::Cgroup(error) => cgroup_errno(*error),
213            Self::IoDomain(error) => io_errno(*error),
214            Self::Net(error) => io_errno((*error).into()),
215            Self::Task(error) => task_errno(*error),
216            Self::Runtime(error) => runtime_errno(error),
217            Self::Block(error) => block_errno(error),
218            Self::Driver(_) => Errno::EIO,
219            #[cfg(feature = "sg2002")]
220            Self::Ion(error) => ion_errno(*error),
221            #[cfg(feature = "sg2002")]
222            Self::Tpu(error) => tpu_errno(*error),
223            Self::Format(_) => Errno::EINVAL,
224            Self::Dma { operation, source } => dma_errno(*operation, source),
225            Self::AlreadyExists => Errno::EEXIST,
226            Self::ArgumentListTooLong => Errno::E2BIG,
227            Self::BadAddress | Self::BadState => Errno::EFAULT,
228            Self::BadFileDescriptor => Errno::EBADF,
229            Self::BrokenPipe => Errno::EPIPE,
230            Self::CrossesDevices => Errno::EXDEV,
231            Self::FilesystemLoop => Errno::ELOOP,
232            Self::IllegalBytes => Errno::EILSEQ,
233            Self::InProgress => Errno::EINPROGRESS,
234            Self::Interrupted | Self::InterruptedNoRestart => Errno::EINTR,
235            Self::InvalidData | Self::InvalidInput => Errno::EINVAL,
236            Self::InvalidExecutable | Self::MalformedExecutable => Errno::ENOEXEC,
237            Self::Io | Self::UnexpectedEof | Self::WriteZero => Errno::EIO,
238            Self::IsADirectory => Errno::EISDIR,
239            Self::NameTooLong => Errno::ENAMETOOLONG,
240            Self::NoMemory => Errno::ENOMEM,
241            Self::NoSuchDevice => Errno::ENODEV,
242            Self::NoSuchDeviceOrAddress => Errno::ENXIO,
243            Self::NoSuchProcess => Errno::ESRCH,
244            Self::NotADirectory => Errno::ENOTDIR,
245            Self::NotASocket => Errno::ENOTSOCK,
246            Self::NotATty => Errno::ENOTTY,
247            Self::NotFound => Errno::ENOENT,
248            Self::OperationNotPermitted => Errno::EPERM,
249            Self::OperationNotSupported => Errno::EOPNOTSUPP,
250            Self::OutOfRange => Errno::ERANGE,
251            Self::PermissionDenied => Errno::EACCES,
252            Self::ReadOnlyFilesystem => Errno::EROFS,
253            Self::ResourceBusy => Errno::EBUSY,
254            Self::StorageFull => Errno::ENOSPC,
255            Self::TimedOut => Errno::ETIMEDOUT,
256            Self::TooManyOpenFiles => Errno::EMFILE,
257            Self::Unsupported => Errno::ENOSYS,
258            Self::WouldBlock => Errno::EAGAIN,
259        }
260    }
261}
262
263fn vm_errno(error: VmError) -> Errno {
264    match error {
265        VmError::BadAddress | VmError::AccessDenied => Errno::EFAULT,
266        VmError::TooLong => Errno::ENAMETOOLONG,
267    }
268}
269
270fn mm_errno(error: MmError) -> Errno {
271    match error {
272        MmError::InvalidInput(_) => Errno::EINVAL,
273        MmError::NoMemory => Errno::ENOMEM,
274        MmError::AlreadyExists => Errno::EEXIST,
275        MmError::BadAddress | MmError::BadState(_) => Errno::EFAULT,
276        MmError::Unsupported => Errno::ENOSYS,
277        MmError::TlbShootdown(error) => tlb_errno(error),
278    }
279}
280
281fn mapping_errno(error: &MappingError) -> Errno {
282    match error {
283        MappingError::InvalidParam => Errno::EINVAL,
284        MappingError::AlreadyExists => Errno::EEXIST,
285        MappingError::BadState | MappingError::NeedsRepair => Errno::EFAULT,
286    }
287}
288
289fn tlb_errno(error: TlbShootdownError) -> Errno {
290    match error {
291        TlbShootdownError::CpuOffline | TlbShootdownError::Unsupported => Errno::ENOSYS,
292        TlbShootdownError::Timeout => Errno::ETIMEDOUT,
293        TlbShootdownError::GenerationExhausted => Errno::EOVERFLOW,
294        TlbShootdownError::Platform => Errno::EIO,
295    }
296}
297
298fn alloc_errno(error: AllocError) -> Errno {
299    match error {
300        AllocError::NoMemory => Errno::ENOMEM,
301        AllocError::NotFound => Errno::ENOENT,
302        AllocError::NotInitialized | AllocError::AlreadyInitialized => Errno::EFAULT,
303        AllocError::MemoryOverlap => Errno::EEXIST,
304        AllocError::InvalidParam | AllocError::NotAllocated => Errno::EINVAL,
305    }
306}
307
308fn cgroup_errno(error: CgroupError) -> Errno {
309    match error {
310        CgroupError::NotInitialized | CgroupError::InvalidInput => Errno::EINVAL,
311        CgroupError::NotFound => Errno::ENOENT,
312        CgroupError::AlreadyExists => Errno::EEXIST,
313        CgroupError::ResourceBusy => Errno::EBUSY,
314        CgroupError::LimitExceeded => Errno::EAGAIN,
315        CgroupError::NoSuchProcess => Errno::ESRCH,
316        CgroupError::DirectoryNotEmpty => Errno::ENOTEMPTY,
317    }
318}
319
320fn task_errno(error: TaskError) -> Errno {
321    match error {
322        TaskError::InvalidConfiguration
323        | TaskError::InvalidCpuCount(_)
324        | TaskError::InvalidCpu(_)
325        | TaskError::InvalidNice(_)
326        | TaskError::InvalidRtPriority(_)
327        | TaskError::InvalidRoundRobinQuantum
328        | TaskError::InvalidDeadline { .. }
329        | TaskError::UnsupportedDeadlineFlags(_) => Errno::EINVAL,
330        TaskError::DeadlineAdmission
331        | TaskError::DeadlineAffinity
332        | TaskError::ActiveTimerAffinity
333        | TaskError::ThreadBusy => Errno::EBUSY,
334        TaskError::StaleThreadId => Errno::ESRCH,
335        TaskError::TimerCapacity => Errno::ENOMEM,
336        TaskError::UnsafeContext => Errno::EPERM,
337        TaskError::CpuOwnerMismatch { .. }
338        | TaskError::CpuOwnerBorrowed
339        | TaskError::CpuAlreadyOnline(_)
340        | TaskError::CpuOffline(_)
341        | TaskError::CpuNotQuiescent(_)
342        | TaskError::LastOnlineCpu(_)
343        | TaskError::ExecutorOwnerMismatch { .. }
344        | TaskError::InvalidTransition { .. }
345        | TaskError::AlreadyQueued
346        | TaskError::NotReady
347        | TaskError::NotExited
348        | TaskError::NoRunnableThread
349        | TaskError::ThreadCapacity
350        | TaskError::NotInitialized
351        | TaskError::InvalidRuntimeHandle
352        | TaskError::InvalidPiState
353        | TaskError::InvalidPiWaitState(_)
354        | TaskError::PiCycle
355        | TaskError::PiChainLimit { .. }
356        | TaskError::RuntimeFailure(_) => Errno::EFAULT,
357    }
358}
359
360fn vfs_error_from_errno(errno: Errno) -> VfsError {
361    match errno {
362        Errno::EEXIST => VfsError::AlreadyExists,
363        Errno::EFAULT => VfsError::BadAddress,
364        Errno::EBADF => VfsError::BadFileDescriptor,
365        Errno::EXDEV => VfsError::CrossesDevices,
366        Errno::ENODATA => VfsError::DataMissing,
367        Errno::ENOTEMPTY => VfsError::DirectoryNotEmpty,
368        Errno::EUCLEAN => VfsError::FilesystemCorrupted,
369        Errno::ELOOP => VfsError::FilesystemLoop,
370        Errno::EFBIG => VfsError::FileTooLarge,
371        Errno::EINVAL => VfsError::InvalidInput,
372        Errno::EINTR => VfsError::Interrupted,
373        Errno::EIO => VfsError::Io,
374        Errno::EISDIR => VfsError::IsADirectory,
375        Errno::ENAMETOOLONG => VfsError::NameTooLong,
376        Errno::ENOMEM => VfsError::NoMemory,
377        Errno::ENODEV => VfsError::NoSuchDevice,
378        Errno::ENXIO => VfsError::NoSuchDeviceOrAddress,
379        Errno::ENOTDIR => VfsError::NotADirectory,
380        Errno::ENOTTY => VfsError::NotATty,
381        Errno::ENOENT => VfsError::NotFound,
382        Errno::EPERM => VfsError::OperationNotPermitted,
383        Errno::EOPNOTSUPP => VfsError::OperationNotSupported,
384        Errno::EACCES => VfsError::PermissionDenied,
385        Errno::EDQUOT => VfsError::QuotaExceeded,
386        Errno::EROFS => VfsError::ReadOnlyFilesystem,
387        Errno::EBUSY => VfsError::ResourceBusy,
388        Errno::ENOSPC => VfsError::StorageFull,
389        Errno::ETIMEDOUT => VfsError::TimedOut,
390        Errno::EMLINK => VfsError::TooManyLinks,
391        Errno::ENOSYS => VfsError::Unsupported,
392        Errno::EOVERFLOW => VfsError::ValueOverflow,
393        Errno::EAGAIN => VfsError::WouldBlock,
394        _ => VfsError::Io,
395    }
396}
397
398fn runtime_errno(error: &RuntimeError) -> Errno {
399    match error {
400        RuntimeError::SerialConfig(error) => match error {
401            ConfigError::InvalidBaudrate
402            | ConfigError::UnsupportedDataBits
403            | ConfigError::UnsupportedStopBits
404            | ConfigError::UnsupportedParity => Errno::EINVAL,
405            ConfigError::Timeout => Errno::ETIMEDOUT,
406            ConfigError::RegisterError => Errno::EIO,
407        },
408        RuntimeError::SerialNotStarted => Errno::EFAULT,
409        RuntimeError::SerialControlBusy => Errno::EBUSY,
410        RuntimeError::Task(error) => task_errno(*error),
411        RuntimeError::WouldBlock => Errno::EAGAIN,
412        RuntimeError::OperationNotSupported => Errno::EOPNOTSUPP,
413        RuntimeError::InvalidCpu { .. } => Errno::EINVAL,
414        _ => Errno::EIO,
415    }
416}
417
418fn block_errno(error: &BlockError) -> Errno {
419    match error {
420        BlockError::InvalidRequest => Errno::EINVAL,
421        BlockError::InvalidState | BlockError::RuntimeUnavailable => Errno::EFAULT,
422        BlockError::WouldBlock => Errno::EAGAIN,
423        BlockError::NoMemory => Errno::ENOMEM,
424        BlockError::Unsupported => Errno::ENOSYS,
425        BlockError::TimedOut => Errno::ETIMEDOUT,
426        BlockError::ResourceBusy => Errno::EBUSY,
427        BlockError::NotFound => Errno::ENOENT,
428        BlockError::Io | BlockError::Irq(_) => Errno::EIO,
429        BlockError::Device { source, .. } => block_errno(&BlockError::from(*source)),
430    }
431}
432
433#[cfg(feature = "sg2002")]
434fn ion_errno(error: IonError) -> Errno {
435    match error {
436        IonError::InvalidArg => Errno::EINVAL,
437        IonError::NoMemory => Errno::ENOMEM,
438        IonError::InvalidBuffer | IonError::BufferNotFound => Errno::ENOENT,
439        IonError::BufferExists => Errno::EEXIST,
440        IonError::InvalidHeap | IonError::NotSupported => Errno::ENOSYS,
441        IonError::Internal => Errno::EINTR,
442    }
443}
444
445#[cfg(feature = "sg2002")]
446fn tpu_errno(error: TpuError) -> Errno {
447    match error {
448        TpuError::Timeout => Errno::ETIMEDOUT,
449        TpuError::InvalidDmabuf | TpuError::PmuBufferNotAligned | TpuError::DmabufNotAligned => {
450            Errno::EINVAL
451        }
452        TpuError::TdmaError(_) | TpuError::TiuError(_) => Errno::EIO,
453        TpuError::NotInitialized => Errno::ENODEV,
454        TpuError::Busy => Errno::EBUSY,
455        TpuError::Interrupted => Errno::EINTR,
456    }
457}
458
459fn dma_errno(operation: DmaOperation, source: &DmaError) -> Errno {
460    match operation {
461        DmaOperation::BufferAllocation => match source {
462            DmaError::LayoutError(_) => Errno::EINVAL,
463            _ => Errno::ENOMEM,
464        },
465        DmaOperation::DeviceIo => match source {
466            DmaError::NoMemory => Errno::ENOMEM,
467            DmaError::LayoutError(_) | DmaError::NullPointer | DmaError::ZeroSizedBuffer => {
468                Errno::EINVAL
469            }
470            _ => Errno::EIO,
471        },
472    }
473}
474
475fn io_errno(error: IoError) -> Errno {
476    match error {
477        IoError::AddrInUse => Errno::EADDRINUSE,
478        IoError::AlreadyConnected => Errno::EISCONN,
479        IoError::AddressFamilyUnsupported => Errno::EAFNOSUPPORT,
480        IoError::AlreadyExists => Errno::EEXIST,
481        IoError::ArgumentListTooLong => Errno::E2BIG,
482        IoError::BadAddress | IoError::BadState => Errno::EFAULT,
483        IoError::BadFileDescriptor => Errno::EBADF,
484        IoError::BrokenPipe => Errno::EPIPE,
485        IoError::ConnectionRefused => Errno::ECONNREFUSED,
486        IoError::ConnectionReset => Errno::ECONNRESET,
487        IoError::CrossesDevices => Errno::EXDEV,
488        IoError::DirectoryNotEmpty => Errno::ENOTEMPTY,
489        IoError::DestAddrRequired => Errno::EDESTADDRREQ,
490        IoError::FilesystemLoop => Errno::ELOOP,
491        IoError::FileTooLarge => Errno::EFBIG,
492        IoError::IllegalBytes => Errno::EILSEQ,
493        IoError::InProgress => Errno::EINPROGRESS,
494        IoError::Interrupted => Errno::EINTR,
495        IoError::InvalidData | IoError::InvalidInput => Errno::EINVAL,
496        IoError::InvalidExecutable => Errno::ENOEXEC,
497        IoError::Io | IoError::UnexpectedEof | IoError::WriteZero => Errno::EIO,
498        IoError::IsADirectory => Errno::EISDIR,
499        IoError::MessageTooLong => Errno::EMSGSIZE,
500        IoError::NameTooLong => Errno::ENAMETOOLONG,
501        IoError::NoMemory => Errno::ENOMEM,
502        IoError::NoSuchDevice => Errno::ENODEV,
503        IoError::NoSuchDeviceOrAddress => Errno::ENXIO,
504        IoError::NoSuchProcess => Errno::ESRCH,
505        IoError::NotADirectory => Errno::ENOTDIR,
506        IoError::NotASocket => Errno::ENOTSOCK,
507        IoError::NotATty => Errno::ENOTTY,
508        IoError::NotConnected => Errno::ENOTCONN,
509        IoError::NotFound => Errno::ENOENT,
510        IoError::OperationNotPermitted => Errno::EPERM,
511        IoError::OperationNotSupported => Errno::EOPNOTSUPP,
512        IoError::OutOfRange => Errno::ERANGE,
513        IoError::PermissionDenied => Errno::EACCES,
514        IoError::ProtocolOptionUnsupported => Errno::ENOPROTOOPT,
515        IoError::ReadOnlyFilesystem => Errno::EROFS,
516        IoError::ResourceBusy => Errno::EBUSY,
517        IoError::StorageFull => Errno::ENOSPC,
518        IoError::TimedOut => Errno::ETIMEDOUT,
519        IoError::TooManyOpenFiles => Errno::EMFILE,
520        IoError::Unsupported => Errno::ENOSYS,
521        IoError::WouldBlock => Errno::EAGAIN,
522    }
523}
524
525fn vfs_errno(error: VfsError) -> Errno {
526    match error {
527        VfsError::AlreadyExists => Errno::EEXIST,
528        VfsError::BadAddress | VfsError::BadState => Errno::EFAULT,
529        VfsError::BadFileDescriptor => Errno::EBADF,
530        VfsError::CrossesDevices => Errno::EXDEV,
531        VfsError::DataMissing => Errno::ENODATA,
532        VfsError::DirectoryNotEmpty => Errno::ENOTEMPTY,
533        VfsError::FilesystemCorrupted => Errno::EUCLEAN,
534        VfsError::FilesystemLoop => Errno::ELOOP,
535        VfsError::FileTooLarge => Errno::EFBIG,
536        VfsError::InvalidData | VfsError::InvalidInput => Errno::EINVAL,
537        VfsError::Interrupted => Errno::EINTR,
538        VfsError::Io => Errno::EIO,
539        VfsError::IsADirectory => Errno::EISDIR,
540        VfsError::NameTooLong => Errno::ENAMETOOLONG,
541        VfsError::NoMemory => Errno::ENOMEM,
542        VfsError::NoSuchDevice => Errno::ENODEV,
543        VfsError::NoSuchDeviceOrAddress => Errno::ENXIO,
544        VfsError::NotADirectory => Errno::ENOTDIR,
545        VfsError::NotATty => Errno::ENOTTY,
546        VfsError::NotFound => Errno::ENOENT,
547        VfsError::OperationNotPermitted => Errno::EPERM,
548        VfsError::OperationNotSupported => Errno::EOPNOTSUPP,
549        VfsError::PermissionDenied => Errno::EACCES,
550        VfsError::QuotaExceeded => Errno::EDQUOT,
551        VfsError::ReadOnlyFilesystem => Errno::EROFS,
552        VfsError::ResourceBusy => Errno::EBUSY,
553        VfsError::StorageFull => Errno::ENOSPC,
554        VfsError::TimedOut => Errno::ETIMEDOUT,
555        VfsError::TooManyLinks => Errno::EMLINK,
556        VfsError::Unsupported => Errno::ENOSYS,
557        VfsError::ValueOverflow => Errno::EOVERFLOW,
558        VfsError::WouldBlock => Errno::EAGAIN,
559    }
560}
561
562#[cfg(all(test, not(axtest)))]
563fn errno_cases_hold<const N: usize>(cases: [(StarryError, Errno); N]) -> bool {
564    cases
565        .into_iter()
566        .all(|(error, expected)| error.linux_errno() == expected)
567}
568
569#[cfg(all(test, not(axtest)))]
570fn memory_errno_mappings_hold() -> bool {
571    errno_cases_hold([
572        (VmError::BadAddress.into(), Errno::EFAULT),
573        (VmError::AccessDenied.into(), Errno::EFAULT),
574        (VmError::TooLong.into(), Errno::ENAMETOOLONG),
575        (
576            SignalError::UserMemory(VmError::BadAddress).into(),
577            Errno::EFAULT,
578        ),
579        (MmError::InvalidInput("test").into(), Errno::EINVAL),
580        (MmError::NoMemory.into(), Errno::ENOMEM),
581        (MmError::AlreadyExists.into(), Errno::EEXIST),
582        (MmError::BadAddress.into(), Errno::EFAULT),
583        (MmError::BadState("test").into(), Errno::EFAULT),
584        (MmError::Unsupported.into(), Errno::ENOSYS),
585        (
586            MmError::TlbShootdown(TlbShootdownError::Timeout).into(),
587            Errno::ETIMEDOUT,
588        ),
589        (MappingError::InvalidParam.into(), Errno::EINVAL),
590        (MappingError::AlreadyExists.into(), Errno::EEXIST),
591        (MappingError::BadState.into(), Errno::EFAULT),
592        (PagingError::NoMemory.into(), Errno::ENOMEM),
593        (PagingError::not_mapped().into(), Errno::EINVAL),
594        (TlbShootdownError::CpuOffline.into(), Errno::ENOSYS),
595        (TlbShootdownError::Timeout.into(), Errno::ETIMEDOUT),
596        (TlbShootdownError::Unsupported.into(), Errno::ENOSYS),
597        (TlbShootdownError::Platform.into(), Errno::EIO),
598        (
599            TlbShootdownError::GenerationExhausted.into(),
600            Errno::EOVERFLOW,
601        ),
602        (AllocError::InvalidParam.into(), Errno::EINVAL),
603        (AllocError::AlreadyInitialized.into(), Errno::EFAULT),
604        (AllocError::MemoryOverlap.into(), Errno::EEXIST),
605        (AllocError::NoMemory.into(), Errno::ENOMEM),
606        (AllocError::NotAllocated.into(), Errno::EINVAL),
607        (AllocError::NotInitialized.into(), Errno::EFAULT),
608        (AllocError::NotFound.into(), Errno::ENOENT),
609        (CgroupError::NotInitialized.into(), Errno::EINVAL),
610        (CgroupError::NotFound.into(), Errno::ENOENT),
611        (CgroupError::AlreadyExists.into(), Errno::EEXIST),
612        (CgroupError::ResourceBusy.into(), Errno::EBUSY),
613        (CgroupError::LimitExceeded.into(), Errno::EAGAIN),
614        (CgroupError::InvalidInput.into(), Errno::EINVAL),
615        (CgroupError::NoSuchProcess.into(), Errno::ESRCH),
616        (CgroupError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
617    ])
618}
619
620#[cfg(all(test, not(axtest)))]
621fn vfs_errno_mappings_hold() -> bool {
622    errno_cases_hold([
623        (VfsError::AlreadyExists.into(), Errno::EEXIST),
624        (VfsError::BadAddress.into(), Errno::EFAULT),
625        (VfsError::BadFileDescriptor.into(), Errno::EBADF),
626        (VfsError::BadState.into(), Errno::EFAULT),
627        (VfsError::CrossesDevices.into(), Errno::EXDEV),
628        (VfsError::DataMissing.into(), Errno::ENODATA),
629        (VfsError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
630        (VfsError::FilesystemCorrupted.into(), Errno::EUCLEAN),
631        (VfsError::FilesystemLoop.into(), Errno::ELOOP),
632        (VfsError::FileTooLarge.into(), Errno::EFBIG),
633        (VfsError::InvalidData.into(), Errno::EINVAL),
634        (VfsError::InvalidInput.into(), Errno::EINVAL),
635        (VfsError::Interrupted.into(), Errno::EINTR),
636        (VfsError::Io.into(), Errno::EIO),
637        (VfsError::IsADirectory.into(), Errno::EISDIR),
638        (VfsError::NameTooLong.into(), Errno::ENAMETOOLONG),
639        (VfsError::NoMemory.into(), Errno::ENOMEM),
640        (VfsError::NoSuchDevice.into(), Errno::ENODEV),
641        (VfsError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
642        (VfsError::NotADirectory.into(), Errno::ENOTDIR),
643        (VfsError::NotATty.into(), Errno::ENOTTY),
644        (VfsError::NotFound.into(), Errno::ENOENT),
645        (VfsError::OperationNotPermitted.into(), Errno::EPERM),
646        (VfsError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
647        (VfsError::PermissionDenied.into(), Errno::EACCES),
648        (VfsError::QuotaExceeded.into(), Errno::EDQUOT),
649        (VfsError::ReadOnlyFilesystem.into(), Errno::EROFS),
650        (VfsError::ResourceBusy.into(), Errno::EBUSY),
651        (VfsError::StorageFull.into(), Errno::ENOSPC),
652        (VfsError::TimedOut.into(), Errno::ETIMEDOUT),
653        (VfsError::TooManyLinks.into(), Errno::EMLINK),
654        (VfsError::Unsupported.into(), Errno::ENOSYS),
655        (VfsError::ValueOverflow.into(), Errno::EOVERFLOW),
656        (VfsError::WouldBlock.into(), Errno::EAGAIN),
657    ])
658}
659
660#[cfg(all(test, not(axtest)))]
661fn io_errno_mappings_hold() -> bool {
662    errno_cases_hold([
663        (IoError::AddrInUse.into(), Errno::EADDRINUSE),
664        (IoError::AlreadyConnected.into(), Errno::EISCONN),
665        (
666            IoError::AddressFamilyUnsupported.into(),
667            Errno::EAFNOSUPPORT,
668        ),
669        (IoError::AlreadyExists.into(), Errno::EEXIST),
670        (IoError::ArgumentListTooLong.into(), Errno::E2BIG),
671        (IoError::BadAddress.into(), Errno::EFAULT),
672        (IoError::BadFileDescriptor.into(), Errno::EBADF),
673        (IoError::BadState.into(), Errno::EFAULT),
674        (IoError::BrokenPipe.into(), Errno::EPIPE),
675        (IoError::ConnectionRefused.into(), Errno::ECONNREFUSED),
676        (IoError::ConnectionReset.into(), Errno::ECONNRESET),
677        (IoError::CrossesDevices.into(), Errno::EXDEV),
678        (IoError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
679        (IoError::DestAddrRequired.into(), Errno::EDESTADDRREQ),
680        (IoError::FilesystemLoop.into(), Errno::ELOOP),
681        (IoError::FileTooLarge.into(), Errno::EFBIG),
682        (IoError::IllegalBytes.into(), Errno::EILSEQ),
683        (IoError::InProgress.into(), Errno::EINPROGRESS),
684        (IoError::Interrupted.into(), Errno::EINTR),
685        (IoError::InvalidData.into(), Errno::EINVAL),
686        (IoError::InvalidExecutable.into(), Errno::ENOEXEC),
687        (IoError::InvalidInput.into(), Errno::EINVAL),
688        (IoError::Io.into(), Errno::EIO),
689        (IoError::IsADirectory.into(), Errno::EISDIR),
690        (IoError::NameTooLong.into(), Errno::ENAMETOOLONG),
691        (IoError::MessageTooLong.into(), Errno::EMSGSIZE),
692        (IoError::NoMemory.into(), Errno::ENOMEM),
693        (IoError::NoSuchDevice.into(), Errno::ENODEV),
694        (IoError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
695        (IoError::NoSuchProcess.into(), Errno::ESRCH),
696        (IoError::NotADirectory.into(), Errno::ENOTDIR),
697        (IoError::NotASocket.into(), Errno::ENOTSOCK),
698        (IoError::NotATty.into(), Errno::ENOTTY),
699        (IoError::NotConnected.into(), Errno::ENOTCONN),
700        (IoError::NotFound.into(), Errno::ENOENT),
701        (IoError::OperationNotPermitted.into(), Errno::EPERM),
702        (IoError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
703        (IoError::OutOfRange.into(), Errno::ERANGE),
704        (IoError::PermissionDenied.into(), Errno::EACCES),
705        (
706            IoError::ProtocolOptionUnsupported.into(),
707            Errno::ENOPROTOOPT,
708        ),
709        (IoError::ReadOnlyFilesystem.into(), Errno::EROFS),
710        (IoError::ResourceBusy.into(), Errno::EBUSY),
711        (IoError::StorageFull.into(), Errno::ENOSPC),
712        (IoError::TimedOut.into(), Errno::ETIMEDOUT),
713        (IoError::TooManyOpenFiles.into(), Errno::EMFILE),
714        (IoError::UnexpectedEof.into(), Errno::EIO),
715        (IoError::Unsupported.into(), Errno::ENOSYS),
716        (IoError::WouldBlock.into(), Errno::EAGAIN),
717        (IoError::WriteZero.into(), Errno::EIO),
718    ])
719}
720
721#[cfg(all(test, not(axtest)))]
722fn block_errno_mappings_hold() -> bool {
723    let device_error = |source| {
724        StarryError::from(BlockError::Device {
725            stage: "submit",
726            operation: RequestOp::Read,
727            lba: 9,
728            source,
729        })
730    };
731
732    errno_cases_hold([
733        (BlockError::InvalidRequest.into(), Errno::EINVAL),
734        (BlockError::InvalidState.into(), Errno::EFAULT),
735        (BlockError::RuntimeUnavailable.into(), Errno::EFAULT),
736        (BlockError::WouldBlock.into(), Errno::EAGAIN),
737        (BlockError::NoMemory.into(), Errno::ENOMEM),
738        (BlockError::Unsupported.into(), Errno::ENOSYS),
739        (BlockError::TimedOut.into(), Errno::ETIMEDOUT),
740        (BlockError::Io.into(), Errno::EIO),
741        (BlockError::ResourceBusy.into(), Errno::EBUSY),
742        (BlockError::NotFound.into(), Errno::ENOENT),
743        (device_error(BlkError::NotSupported), Errno::ENOSYS),
744        (device_error(BlkError::Retry), Errno::EAGAIN),
745        (device_error(BlkError::NoMemory), Errno::ENOMEM),
746        (device_error(BlkError::InvalidBlockIndex(9)), Errno::EINVAL),
747        (device_error(BlkError::InvalidRequest), Errno::EINVAL),
748        (device_error(BlkError::TimedOut), Errno::ETIMEDOUT),
749        (device_error(BlkError::Io), Errno::EIO),
750        (device_error(BlkError::Other("device failure")), Errno::EIO),
751    ])
752}
753
754#[cfg(all(test, not(axtest)))]
755fn leaf_errno_mappings_hold() -> bool {
756    errno_cases_hold([
757        (StarryError::AlreadyExists, Errno::EEXIST),
758        (StarryError::ArgumentListTooLong, Errno::E2BIG),
759        (StarryError::BadAddress, Errno::EFAULT),
760        (StarryError::BadFileDescriptor, Errno::EBADF),
761        (StarryError::BadState, Errno::EFAULT),
762        (StarryError::BrokenPipe, Errno::EPIPE),
763        (StarryError::CrossesDevices, Errno::EXDEV),
764        (StarryError::FilesystemLoop, Errno::ELOOP),
765        (StarryError::IllegalBytes, Errno::EILSEQ),
766        (StarryError::InProgress, Errno::EINPROGRESS),
767        (StarryError::Interrupted, Errno::EINTR),
768        (StarryError::InterruptedNoRestart, Errno::EINTR),
769        (StarryError::InvalidData, Errno::EINVAL),
770        (StarryError::InvalidExecutable, Errno::ENOEXEC),
771        (StarryError::MalformedExecutable, Errno::ENOEXEC),
772        (StarryError::InvalidInput, Errno::EINVAL),
773        (StarryError::Io, Errno::EIO),
774        (StarryError::IsADirectory, Errno::EISDIR),
775        (StarryError::NameTooLong, Errno::ENAMETOOLONG),
776        (StarryError::NoMemory, Errno::ENOMEM),
777        (StarryError::NoSuchDevice, Errno::ENODEV),
778        (StarryError::NoSuchDeviceOrAddress, Errno::ENXIO),
779        (StarryError::NoSuchProcess, Errno::ESRCH),
780        (StarryError::NotADirectory, Errno::ENOTDIR),
781        (StarryError::NotASocket, Errno::ENOTSOCK),
782        (StarryError::NotATty, Errno::ENOTTY),
783        (StarryError::NotFound, Errno::ENOENT),
784        (StarryError::OperationNotPermitted, Errno::EPERM),
785        (StarryError::OperationNotSupported, Errno::EOPNOTSUPP),
786        (StarryError::OutOfRange, Errno::ERANGE),
787        (StarryError::PermissionDenied, Errno::EACCES),
788        (StarryError::ReadOnlyFilesystem, Errno::EROFS),
789        (StarryError::ResourceBusy, Errno::EBUSY),
790        (StarryError::StorageFull, Errno::ENOSPC),
791        (StarryError::TimedOut, Errno::ETIMEDOUT),
792        (StarryError::TooManyOpenFiles, Errno::EMFILE),
793        (StarryError::UnexpectedEof, Errno::EIO),
794        (StarryError::Unsupported, Errno::ENOSYS),
795        (StarryError::WouldBlock, Errno::EAGAIN),
796        (StarryError::WriteZero, Errno::EIO),
797        (StarryError::Format(core::fmt::Error), Errno::EINVAL),
798        (StarryError::Task(TaskError::TimerCapacity), Errno::ENOMEM),
799        (
800            StarryError::Runtime(RuntimeError::SerialNotStarted),
801            Errno::EFAULT,
802        ),
803        (
804            StarryError::Runtime(RuntimeError::SerialControlBusy),
805            Errno::EBUSY,
806        ),
807        (
808            StarryError::Runtime(RuntimeError::Task(TaskError::UnsafeContext)),
809            Errno::EPERM,
810        ),
811        (
812            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::InvalidBaudrate)),
813            Errno::EINVAL,
814        ),
815        (
816            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedDataBits)),
817            Errno::EINVAL,
818        ),
819        (
820            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedStopBits)),
821            Errno::EINVAL,
822        ),
823        (
824            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedParity)),
825            Errno::EINVAL,
826        ),
827        (
828            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::Timeout)),
829            Errno::ETIMEDOUT,
830        ),
831        (
832            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::RegisterError)),
833            Errno::EIO,
834        ),
835        (
836            StarryError::Runtime(RuntimeError::WouldBlock),
837            Errno::EAGAIN,
838        ),
839        (
840            StarryError::Runtime(RuntimeError::OperationNotSupported),
841            Errno::EOPNOTSUPP,
842        ),
843        (
844            StarryError::Runtime(RuntimeError::InvalidCpu { cpu: 99 }),
845            Errno::EINVAL,
846        ),
847        (
848            StarryError::Dma {
849                operation: DmaOperation::BufferAllocation,
850                source: DmaError::NoMemory,
851            },
852            Errno::ENOMEM,
853        ),
854        (
855            StarryError::Dma {
856                operation: DmaOperation::DeviceIo,
857                source: DmaError::NullPointer,
858            },
859            Errno::EINVAL,
860        ),
861    ])
862}
863
864#[cfg(all(test, not(axtest)))]
865fn domain_errno_mappings_hold() -> bool {
866    memory_errno_mappings_hold()
867        && vfs_errno_mappings_hold()
868        && io_errno_mappings_hold()
869        && block_errno_mappings_hold()
870        && leaf_errno_mappings_hold()
871        && StarryError::from(Errno::EOWNERDEAD).linux_errno() == Errno::EOWNERDEAD
872        && StarryError::from(Errno::new(4094)).linux_errno().into_raw() == 4094
873}
874
875/// A result returned by Starry-owned kernel operations.
876pub type StarryResult<T = ()> = Result<T, StarryError>;
877
878#[cfg(all(test, not(axtest)))]
879mod tests {
880    use super::*;
881
882    #[test]
883    fn domain_errors_map_to_stable_linux_errno() {
884        assert!(domain_errno_mappings_hold());
885    }
886
887    #[test]
888    fn readiness_retry_recognizes_filesystem_would_block_without_flattening() {
889        assert!(StarryError::WouldBlock.is_would_block());
890        assert!(StarryError::from(VfsError::WouldBlock).is_would_block());
891        assert!(StarryError::from(IoError::WouldBlock).is_would_block());
892        assert!(!StarryError::from(VfsError::Io).is_would_block());
893        assert!(!StarryError::from(IoError::Io).is_would_block());
894    }
895
896    #[test]
897    fn native_linux_errno_is_not_canonicalized() {
898        let error = StarryError::from(Errno::EOWNERDEAD);
899        assert_eq!(error.linux_errno(), Errno::EOWNERDEAD);
900    }
901
902    #[test]
903    fn unknown_linux_errno_is_preserved() {
904        let error = StarryError::from(Errno::new(4094));
905        assert_eq!(error.linux_errno().into_raw(), 4094);
906    }
907
908    #[cfg(feature = "sg2002")]
909    #[test]
910    fn sg2002_tpu_errors_keep_their_linux_errno() {
911        let cases = [
912            (TpuError::Timeout, Errno::ETIMEDOUT),
913            (TpuError::InvalidDmabuf, Errno::EINVAL),
914            (TpuError::TdmaError(1), Errno::EIO),
915            (TpuError::TiuError(1), Errno::EIO),
916            (TpuError::NotInitialized, Errno::ENODEV),
917            (TpuError::Busy, Errno::EBUSY),
918            (TpuError::Interrupted, Errno::EINTR),
919            (TpuError::PmuBufferNotAligned, Errno::EINVAL),
920            (TpuError::DmabufNotAligned, Errno::EINVAL),
921        ];
922        for (error, expected) in cases {
923            assert_eq!(StarryError::from(error).linux_errno(), expected);
924        }
925    }
926}