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::Signal(SignalError::NoMemory) => Errno::ENOMEM,
204            Self::Mm(error) => mm_errno(*error),
205            Self::Vfs(error) => vfs_errno(*error),
206            Self::Mapping(error) => mapping_errno(error),
207            Self::Paging(error) => match error {
208                PagingError::NoMemory => Errno::ENOMEM,
209                _ => Errno::EINVAL,
210            },
211            Self::TlbShootdown(error) => tlb_errno(*error),
212            Self::Alloc(error) => alloc_errno(*error),
213            Self::Cgroup(error) => cgroup_errno(*error),
214            Self::IoDomain(error) => io_errno(*error),
215            Self::Net(error) => io_errno((*error).into()),
216            Self::Task(error) => task_errno(*error),
217            Self::Runtime(error) => runtime_errno(error),
218            Self::Block(error) => block_errno(error),
219            Self::Driver(_) => Errno::EIO,
220            #[cfg(feature = "sg2002")]
221            Self::Ion(error) => ion_errno(*error),
222            #[cfg(feature = "sg2002")]
223            Self::Tpu(error) => tpu_errno(*error),
224            Self::Format(_) => Errno::EINVAL,
225            Self::Dma { operation, source } => dma_errno(*operation, source),
226            Self::AlreadyExists => Errno::EEXIST,
227            Self::ArgumentListTooLong => Errno::E2BIG,
228            Self::BadAddress | Self::BadState => Errno::EFAULT,
229            Self::BadFileDescriptor => Errno::EBADF,
230            Self::BrokenPipe => Errno::EPIPE,
231            Self::CrossesDevices => Errno::EXDEV,
232            Self::FilesystemLoop => Errno::ELOOP,
233            Self::IllegalBytes => Errno::EILSEQ,
234            Self::InProgress => Errno::EINPROGRESS,
235            Self::Interrupted | Self::InterruptedNoRestart => Errno::EINTR,
236            Self::InvalidData | Self::InvalidInput => Errno::EINVAL,
237            Self::InvalidExecutable | Self::MalformedExecutable => Errno::ENOEXEC,
238            Self::Io | Self::UnexpectedEof | Self::WriteZero => Errno::EIO,
239            Self::IsADirectory => Errno::EISDIR,
240            Self::NameTooLong => Errno::ENAMETOOLONG,
241            Self::NoMemory => Errno::ENOMEM,
242            Self::NoSuchDevice => Errno::ENODEV,
243            Self::NoSuchDeviceOrAddress => Errno::ENXIO,
244            Self::NoSuchProcess => Errno::ESRCH,
245            Self::NotADirectory => Errno::ENOTDIR,
246            Self::NotASocket => Errno::ENOTSOCK,
247            Self::NotATty => Errno::ENOTTY,
248            Self::NotFound => Errno::ENOENT,
249            Self::OperationNotPermitted => Errno::EPERM,
250            Self::OperationNotSupported => Errno::EOPNOTSUPP,
251            Self::OutOfRange => Errno::ERANGE,
252            Self::PermissionDenied => Errno::EACCES,
253            Self::ReadOnlyFilesystem => Errno::EROFS,
254            Self::ResourceBusy => Errno::EBUSY,
255            Self::StorageFull => Errno::ENOSPC,
256            Self::TimedOut => Errno::ETIMEDOUT,
257            Self::TooManyOpenFiles => Errno::EMFILE,
258            Self::Unsupported => Errno::ENOSYS,
259            Self::WouldBlock => Errno::EAGAIN,
260        }
261    }
262}
263
264fn vm_errno(error: VmError) -> Errno {
265    match error {
266        VmError::BadAddress | VmError::AccessDenied => Errno::EFAULT,
267        VmError::TooLong => Errno::ENAMETOOLONG,
268    }
269}
270
271fn mm_errno(error: MmError) -> Errno {
272    match error {
273        MmError::InvalidInput(_) => Errno::EINVAL,
274        MmError::NoMemory => Errno::ENOMEM,
275        MmError::AlreadyExists => Errno::EEXIST,
276        MmError::BadAddress | MmError::BadState(_) => Errno::EFAULT,
277        MmError::Unsupported => Errno::ENOSYS,
278        MmError::TlbShootdown(error) => tlb_errno(error),
279    }
280}
281
282fn mapping_errno(error: &MappingError) -> Errno {
283    match error {
284        MappingError::InvalidParam => Errno::EINVAL,
285        MappingError::AlreadyExists => Errno::EEXIST,
286        MappingError::BadState | MappingError::NeedsRepair => Errno::EFAULT,
287    }
288}
289
290fn tlb_errno(error: TlbShootdownError) -> Errno {
291    match error {
292        TlbShootdownError::CpuOffline | TlbShootdownError::Unsupported => Errno::ENOSYS,
293        TlbShootdownError::Timeout => Errno::ETIMEDOUT,
294        TlbShootdownError::GenerationExhausted => Errno::EOVERFLOW,
295        TlbShootdownError::Platform => Errno::EIO,
296    }
297}
298
299fn alloc_errno(error: AllocError) -> Errno {
300    match error {
301        AllocError::NoMemory => Errno::ENOMEM,
302        AllocError::NotFound => Errno::ENOENT,
303        AllocError::NotInitialized | AllocError::AlreadyInitialized => Errno::EFAULT,
304        AllocError::MemoryOverlap => Errno::EEXIST,
305        AllocError::InvalidParam | AllocError::NotAllocated => Errno::EINVAL,
306    }
307}
308
309fn cgroup_errno(error: CgroupError) -> Errno {
310    match error {
311        CgroupError::NotInitialized | CgroupError::InvalidInput => Errno::EINVAL,
312        CgroupError::NotFound => Errno::ENOENT,
313        CgroupError::AlreadyExists => Errno::EEXIST,
314        CgroupError::ResourceBusy => Errno::EBUSY,
315        CgroupError::LimitExceeded => Errno::EAGAIN,
316        CgroupError::NoSuchProcess => Errno::ESRCH,
317        CgroupError::DirectoryNotEmpty => Errno::ENOTEMPTY,
318    }
319}
320
321fn task_errno(error: TaskError) -> Errno {
322    match error {
323        TaskError::InvalidConfiguration
324        | TaskError::InvalidCpuCount(_)
325        | TaskError::InvalidCpu(_)
326        | TaskError::InvalidNice(_)
327        | TaskError::InvalidRtPriority(_)
328        | TaskError::InvalidRoundRobinQuantum
329        | TaskError::InvalidDeadline { .. }
330        | TaskError::UnsupportedDeadlineFlags(_) => Errno::EINVAL,
331        TaskError::DeadlineAdmission
332        | TaskError::DeadlineAffinity
333        | TaskError::ActiveTimerAffinity
334        | TaskError::ThreadBusy => Errno::EBUSY,
335        TaskError::StaleThreadId => Errno::ESRCH,
336        TaskError::TimerCapacity => Errno::ENOMEM,
337        TaskError::RuntimeFailure(status)
338            if status == ax_runtime::task::runtime::RuntimeStatus::NoMemory as u32 =>
339        {
340            Errno::ENOMEM
341        }
342        TaskError::UnsafeContext => Errno::EPERM,
343        TaskError::CpuOwnerMismatch { .. }
344        | TaskError::CpuOwnerBorrowed
345        | TaskError::CpuAlreadyOnline(_)
346        | TaskError::CpuOffline(_)
347        | TaskError::CpuNotQuiescent(_)
348        | TaskError::LastOnlineCpu(_)
349        | TaskError::ExecutorOwnerMismatch { .. }
350        | TaskError::InvalidTransition { .. }
351        | TaskError::AlreadyQueued
352        | TaskError::NotReady
353        | TaskError::NotExited
354        | TaskError::NoRunnableThread
355        | TaskError::ThreadCapacity
356        | TaskError::NotInitialized
357        | TaskError::InvalidRuntimeHandle
358        | TaskError::InvalidPiState
359        | TaskError::InvalidPiWaitState(_)
360        | TaskError::PiCycle
361        | TaskError::PiChainLimit { .. }
362        | TaskError::RuntimeFailure(_) => Errno::EFAULT,
363    }
364}
365
366fn vfs_error_from_errno(errno: Errno) -> VfsError {
367    match errno {
368        Errno::EEXIST => VfsError::AlreadyExists,
369        Errno::EFAULT => VfsError::BadAddress,
370        Errno::EBADF => VfsError::BadFileDescriptor,
371        Errno::EXDEV => VfsError::CrossesDevices,
372        Errno::ENODATA => VfsError::DataMissing,
373        Errno::ENOTEMPTY => VfsError::DirectoryNotEmpty,
374        Errno::EUCLEAN => VfsError::FilesystemCorrupted,
375        Errno::ELOOP => VfsError::FilesystemLoop,
376        Errno::EFBIG => VfsError::FileTooLarge,
377        Errno::EINVAL => VfsError::InvalidInput,
378        Errno::EINTR => VfsError::Interrupted,
379        Errno::EIO => VfsError::Io,
380        Errno::EISDIR => VfsError::IsADirectory,
381        Errno::ENAMETOOLONG => VfsError::NameTooLong,
382        Errno::ENOMEM => VfsError::NoMemory,
383        Errno::ENODEV => VfsError::NoSuchDevice,
384        Errno::ENXIO => VfsError::NoSuchDeviceOrAddress,
385        Errno::ENOTDIR => VfsError::NotADirectory,
386        Errno::ENOTTY => VfsError::NotATty,
387        Errno::ENOENT => VfsError::NotFound,
388        Errno::EPERM => VfsError::OperationNotPermitted,
389        Errno::EOPNOTSUPP => VfsError::OperationNotSupported,
390        Errno::EACCES => VfsError::PermissionDenied,
391        Errno::EDQUOT => VfsError::QuotaExceeded,
392        Errno::EROFS => VfsError::ReadOnlyFilesystem,
393        Errno::ETXTBSY => VfsError::TextFileBusy,
394        Errno::EBUSY => VfsError::ResourceBusy,
395        Errno::ENOSPC => VfsError::StorageFull,
396        Errno::ETIMEDOUT => VfsError::TimedOut,
397        Errno::EMLINK => VfsError::TooManyLinks,
398        Errno::ENOSYS => VfsError::Unsupported,
399        Errno::EOVERFLOW => VfsError::ValueOverflow,
400        Errno::EAGAIN => VfsError::WouldBlock,
401        _ => VfsError::Io,
402    }
403}
404
405fn runtime_errno(error: &RuntimeError) -> Errno {
406    match error {
407        RuntimeError::SerialConfig(error) => match error {
408            ConfigError::InvalidBaudrate
409            | ConfigError::UnsupportedDataBits
410            | ConfigError::UnsupportedStopBits
411            | ConfigError::UnsupportedParity => Errno::EINVAL,
412            ConfigError::Timeout => Errno::ETIMEDOUT,
413            ConfigError::RegisterError => Errno::EIO,
414        },
415        RuntimeError::SerialNotStarted => Errno::EFAULT,
416        RuntimeError::SerialControlBusy => Errno::EBUSY,
417        RuntimeError::Task(error) => task_errno(*error),
418        RuntimeError::WouldBlock => Errno::EAGAIN,
419        RuntimeError::OperationNotSupported => Errno::EOPNOTSUPP,
420        RuntimeError::InvalidCpu { .. } => Errno::EINVAL,
421        _ => Errno::EIO,
422    }
423}
424
425fn block_errno(error: &BlockError) -> Errno {
426    match error {
427        BlockError::InvalidRequest => Errno::EINVAL,
428        BlockError::InvalidState | BlockError::RuntimeUnavailable => Errno::EFAULT,
429        BlockError::WouldBlock => Errno::EAGAIN,
430        BlockError::NoMemory => Errno::ENOMEM,
431        BlockError::Unsupported => Errno::ENOSYS,
432        BlockError::TimedOut => Errno::ETIMEDOUT,
433        BlockError::ResourceBusy => Errno::EBUSY,
434        BlockError::NotFound => Errno::ENOENT,
435        BlockError::Io | BlockError::Irq(_) => Errno::EIO,
436        BlockError::Device { source, .. } => block_errno(&BlockError::from(*source)),
437    }
438}
439
440#[cfg(feature = "sg2002")]
441fn ion_errno(error: IonError) -> Errno {
442    match error {
443        IonError::InvalidArg => Errno::EINVAL,
444        IonError::NoMemory => Errno::ENOMEM,
445        IonError::InvalidBuffer | IonError::BufferNotFound => Errno::ENOENT,
446        IonError::BufferExists => Errno::EEXIST,
447        IonError::InvalidHeap | IonError::NotSupported => Errno::ENOSYS,
448        IonError::Internal => Errno::EINTR,
449    }
450}
451
452#[cfg(feature = "sg2002")]
453fn tpu_errno(error: TpuError) -> Errno {
454    match error {
455        TpuError::Timeout => Errno::ETIMEDOUT,
456        TpuError::InvalidDmabuf | TpuError::PmuBufferNotAligned | TpuError::DmabufNotAligned => {
457            Errno::EINVAL
458        }
459        TpuError::TdmaError(_) | TpuError::TiuError(_) => Errno::EIO,
460        TpuError::NotInitialized => Errno::ENODEV,
461        TpuError::Busy => Errno::EBUSY,
462        TpuError::Interrupted => Errno::EINTR,
463    }
464}
465
466fn dma_errno(operation: DmaOperation, source: &DmaError) -> Errno {
467    match operation {
468        DmaOperation::BufferAllocation => match source {
469            DmaError::LayoutError(_) => Errno::EINVAL,
470            _ => Errno::ENOMEM,
471        },
472        DmaOperation::DeviceIo => match source {
473            DmaError::NoMemory => Errno::ENOMEM,
474            DmaError::LayoutError(_) | DmaError::NullPointer | DmaError::ZeroSizedBuffer => {
475                Errno::EINVAL
476            }
477            _ => Errno::EIO,
478        },
479    }
480}
481
482fn io_errno(error: IoError) -> Errno {
483    match error {
484        IoError::AddrInUse => Errno::EADDRINUSE,
485        IoError::AlreadyConnected => Errno::EISCONN,
486        IoError::AddressFamilyUnsupported => Errno::EAFNOSUPPORT,
487        IoError::AlreadyExists => Errno::EEXIST,
488        IoError::ArgumentListTooLong => Errno::E2BIG,
489        IoError::BadAddress | IoError::BadState => Errno::EFAULT,
490        IoError::BadFileDescriptor => Errno::EBADF,
491        IoError::BrokenPipe => Errno::EPIPE,
492        IoError::ConnectionRefused => Errno::ECONNREFUSED,
493        IoError::ConnectionReset => Errno::ECONNRESET,
494        IoError::CrossesDevices => Errno::EXDEV,
495        IoError::DirectoryNotEmpty => Errno::ENOTEMPTY,
496        IoError::DestAddrRequired => Errno::EDESTADDRREQ,
497        IoError::FilesystemLoop => Errno::ELOOP,
498        IoError::FileTooLarge => Errno::EFBIG,
499        IoError::IllegalBytes => Errno::EILSEQ,
500        IoError::InProgress => Errno::EINPROGRESS,
501        IoError::Interrupted => Errno::EINTR,
502        IoError::InvalidData | IoError::InvalidInput => Errno::EINVAL,
503        IoError::InvalidExecutable => Errno::ENOEXEC,
504        IoError::Io | IoError::UnexpectedEof | IoError::WriteZero => Errno::EIO,
505        IoError::IsADirectory => Errno::EISDIR,
506        IoError::MessageTooLong => Errno::EMSGSIZE,
507        IoError::NameTooLong => Errno::ENAMETOOLONG,
508        IoError::NoMemory => Errno::ENOMEM,
509        IoError::NoSuchDevice => Errno::ENODEV,
510        IoError::NoSuchDeviceOrAddress => Errno::ENXIO,
511        IoError::NoSuchProcess => Errno::ESRCH,
512        IoError::NotADirectory => Errno::ENOTDIR,
513        IoError::NotASocket => Errno::ENOTSOCK,
514        IoError::NotATty => Errno::ENOTTY,
515        IoError::NotConnected => Errno::ENOTCONN,
516        IoError::NotFound => Errno::ENOENT,
517        IoError::OperationNotPermitted => Errno::EPERM,
518        IoError::OperationNotSupported => Errno::EOPNOTSUPP,
519        IoError::OutOfRange => Errno::ERANGE,
520        IoError::PermissionDenied => Errno::EACCES,
521        IoError::ProtocolOptionUnsupported => Errno::ENOPROTOOPT,
522        IoError::ReadOnlyFilesystem => Errno::EROFS,
523        IoError::ResourceBusy => Errno::EBUSY,
524        IoError::StorageFull => Errno::ENOSPC,
525        IoError::TimedOut => Errno::ETIMEDOUT,
526        IoError::TooManyOpenFiles => Errno::EMFILE,
527        IoError::Unsupported => Errno::ENOSYS,
528        IoError::WouldBlock => Errno::EAGAIN,
529    }
530}
531
532fn vfs_errno(error: VfsError) -> Errno {
533    match error {
534        VfsError::AlreadyExists => Errno::EEXIST,
535        VfsError::BadAddress | VfsError::BadState => Errno::EFAULT,
536        VfsError::BadFileDescriptor => Errno::EBADF,
537        VfsError::CrossesDevices => Errno::EXDEV,
538        VfsError::DataMissing => Errno::ENODATA,
539        VfsError::DirectoryNotEmpty => Errno::ENOTEMPTY,
540        VfsError::FilesystemCorrupted => Errno::EUCLEAN,
541        VfsError::FilesystemLoop => Errno::ELOOP,
542        VfsError::FileTooLarge => Errno::EFBIG,
543        VfsError::InvalidData | VfsError::InvalidInput => Errno::EINVAL,
544        VfsError::Interrupted => Errno::EINTR,
545        VfsError::Io => Errno::EIO,
546        VfsError::IsADirectory => Errno::EISDIR,
547        VfsError::NameTooLong => Errno::ENAMETOOLONG,
548        VfsError::NoMemory => Errno::ENOMEM,
549        VfsError::NoSuchDevice => Errno::ENODEV,
550        VfsError::NoSuchDeviceOrAddress => Errno::ENXIO,
551        VfsError::NotADirectory => Errno::ENOTDIR,
552        VfsError::NotATty => Errno::ENOTTY,
553        VfsError::NotFound => Errno::ENOENT,
554        VfsError::OperationNotPermitted => Errno::EPERM,
555        VfsError::OperationNotSupported => Errno::EOPNOTSUPP,
556        VfsError::PermissionDenied => Errno::EACCES,
557        VfsError::QuotaExceeded => Errno::EDQUOT,
558        VfsError::ReadOnlyFilesystem => Errno::EROFS,
559        VfsError::TextFileBusy => Errno::ETXTBSY,
560        VfsError::ResourceBusy => Errno::EBUSY,
561        VfsError::StorageFull => Errno::ENOSPC,
562        VfsError::TimedOut => Errno::ETIMEDOUT,
563        VfsError::TooManyLinks => Errno::EMLINK,
564        VfsError::Unsupported => Errno::ENOSYS,
565        VfsError::ValueOverflow => Errno::EOVERFLOW,
566        VfsError::WouldBlock => Errno::EAGAIN,
567    }
568}
569
570#[cfg(all(test, not(axtest)))]
571fn errno_cases_hold<const N: usize>(cases: [(StarryError, Errno); N]) -> bool {
572    cases
573        .into_iter()
574        .all(|(error, expected)| error.linux_errno() == expected)
575}
576
577#[cfg(all(test, not(axtest)))]
578fn memory_errno_mappings_hold() -> bool {
579    errno_cases_hold([
580        (VmError::BadAddress.into(), Errno::EFAULT),
581        (VmError::AccessDenied.into(), Errno::EFAULT),
582        (VmError::TooLong.into(), Errno::ENAMETOOLONG),
583        (
584            SignalError::UserMemory(VmError::BadAddress).into(),
585            Errno::EFAULT,
586        ),
587        (MmError::InvalidInput("test").into(), Errno::EINVAL),
588        (MmError::NoMemory.into(), Errno::ENOMEM),
589        (MmError::AlreadyExists.into(), Errno::EEXIST),
590        (MmError::BadAddress.into(), Errno::EFAULT),
591        (MmError::BadState("test").into(), Errno::EFAULT),
592        (MmError::Unsupported.into(), Errno::ENOSYS),
593        (
594            MmError::TlbShootdown(TlbShootdownError::Timeout).into(),
595            Errno::ETIMEDOUT,
596        ),
597        (MappingError::InvalidParam.into(), Errno::EINVAL),
598        (MappingError::AlreadyExists.into(), Errno::EEXIST),
599        (MappingError::BadState.into(), Errno::EFAULT),
600        (PagingError::NoMemory.into(), Errno::ENOMEM),
601        (PagingError::not_mapped().into(), Errno::EINVAL),
602        (TlbShootdownError::CpuOffline.into(), Errno::ENOSYS),
603        (TlbShootdownError::Timeout.into(), Errno::ETIMEDOUT),
604        (TlbShootdownError::Unsupported.into(), Errno::ENOSYS),
605        (TlbShootdownError::Platform.into(), Errno::EIO),
606        (
607            TlbShootdownError::GenerationExhausted.into(),
608            Errno::EOVERFLOW,
609        ),
610        (AllocError::InvalidParam.into(), Errno::EINVAL),
611        (AllocError::AlreadyInitialized.into(), Errno::EFAULT),
612        (AllocError::MemoryOverlap.into(), Errno::EEXIST),
613        (AllocError::NoMemory.into(), Errno::ENOMEM),
614        (AllocError::NotAllocated.into(), Errno::EINVAL),
615        (AllocError::NotInitialized.into(), Errno::EFAULT),
616        (AllocError::NotFound.into(), Errno::ENOENT),
617        (CgroupError::NotInitialized.into(), Errno::EINVAL),
618        (CgroupError::NotFound.into(), Errno::ENOENT),
619        (CgroupError::AlreadyExists.into(), Errno::EEXIST),
620        (CgroupError::ResourceBusy.into(), Errno::EBUSY),
621        (CgroupError::LimitExceeded.into(), Errno::EAGAIN),
622        (CgroupError::InvalidInput.into(), Errno::EINVAL),
623        (CgroupError::NoSuchProcess.into(), Errno::ESRCH),
624        (CgroupError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
625    ])
626}
627
628#[cfg(all(test, not(axtest)))]
629fn vfs_errno_mappings_hold() -> bool {
630    errno_cases_hold([
631        (VfsError::AlreadyExists.into(), Errno::EEXIST),
632        (VfsError::BadAddress.into(), Errno::EFAULT),
633        (VfsError::BadFileDescriptor.into(), Errno::EBADF),
634        (VfsError::BadState.into(), Errno::EFAULT),
635        (VfsError::CrossesDevices.into(), Errno::EXDEV),
636        (VfsError::DataMissing.into(), Errno::ENODATA),
637        (VfsError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
638        (VfsError::FilesystemCorrupted.into(), Errno::EUCLEAN),
639        (VfsError::FilesystemLoop.into(), Errno::ELOOP),
640        (VfsError::FileTooLarge.into(), Errno::EFBIG),
641        (VfsError::InvalidData.into(), Errno::EINVAL),
642        (VfsError::InvalidInput.into(), Errno::EINVAL),
643        (VfsError::Interrupted.into(), Errno::EINTR),
644        (VfsError::Io.into(), Errno::EIO),
645        (VfsError::IsADirectory.into(), Errno::EISDIR),
646        (VfsError::NameTooLong.into(), Errno::ENAMETOOLONG),
647        (VfsError::NoMemory.into(), Errno::ENOMEM),
648        (VfsError::NoSuchDevice.into(), Errno::ENODEV),
649        (VfsError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
650        (VfsError::NotADirectory.into(), Errno::ENOTDIR),
651        (VfsError::NotATty.into(), Errno::ENOTTY),
652        (VfsError::NotFound.into(), Errno::ENOENT),
653        (VfsError::OperationNotPermitted.into(), Errno::EPERM),
654        (VfsError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
655        (VfsError::PermissionDenied.into(), Errno::EACCES),
656        (VfsError::QuotaExceeded.into(), Errno::EDQUOT),
657        (VfsError::ReadOnlyFilesystem.into(), Errno::EROFS),
658        (VfsError::ResourceBusy.into(), Errno::EBUSY),
659        (VfsError::StorageFull.into(), Errno::ENOSPC),
660        (VfsError::TimedOut.into(), Errno::ETIMEDOUT),
661        (VfsError::TooManyLinks.into(), Errno::EMLINK),
662        (VfsError::Unsupported.into(), Errno::ENOSYS),
663        (VfsError::ValueOverflow.into(), Errno::EOVERFLOW),
664        (VfsError::WouldBlock.into(), Errno::EAGAIN),
665    ])
666}
667
668#[cfg(all(test, not(axtest)))]
669fn io_errno_mappings_hold() -> bool {
670    errno_cases_hold([
671        (IoError::AddrInUse.into(), Errno::EADDRINUSE),
672        (IoError::AlreadyConnected.into(), Errno::EISCONN),
673        (
674            IoError::AddressFamilyUnsupported.into(),
675            Errno::EAFNOSUPPORT,
676        ),
677        (IoError::AlreadyExists.into(), Errno::EEXIST),
678        (IoError::ArgumentListTooLong.into(), Errno::E2BIG),
679        (IoError::BadAddress.into(), Errno::EFAULT),
680        (IoError::BadFileDescriptor.into(), Errno::EBADF),
681        (IoError::BadState.into(), Errno::EFAULT),
682        (IoError::BrokenPipe.into(), Errno::EPIPE),
683        (IoError::ConnectionRefused.into(), Errno::ECONNREFUSED),
684        (IoError::ConnectionReset.into(), Errno::ECONNRESET),
685        (IoError::CrossesDevices.into(), Errno::EXDEV),
686        (IoError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
687        (IoError::DestAddrRequired.into(), Errno::EDESTADDRREQ),
688        (IoError::FilesystemLoop.into(), Errno::ELOOP),
689        (IoError::FileTooLarge.into(), Errno::EFBIG),
690        (IoError::IllegalBytes.into(), Errno::EILSEQ),
691        (IoError::InProgress.into(), Errno::EINPROGRESS),
692        (IoError::Interrupted.into(), Errno::EINTR),
693        (IoError::InvalidData.into(), Errno::EINVAL),
694        (IoError::InvalidExecutable.into(), Errno::ENOEXEC),
695        (IoError::InvalidInput.into(), Errno::EINVAL),
696        (IoError::Io.into(), Errno::EIO),
697        (IoError::IsADirectory.into(), Errno::EISDIR),
698        (IoError::NameTooLong.into(), Errno::ENAMETOOLONG),
699        (IoError::MessageTooLong.into(), Errno::EMSGSIZE),
700        (IoError::NoMemory.into(), Errno::ENOMEM),
701        (IoError::NoSuchDevice.into(), Errno::ENODEV),
702        (IoError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
703        (IoError::NoSuchProcess.into(), Errno::ESRCH),
704        (IoError::NotADirectory.into(), Errno::ENOTDIR),
705        (IoError::NotASocket.into(), Errno::ENOTSOCK),
706        (IoError::NotATty.into(), Errno::ENOTTY),
707        (IoError::NotConnected.into(), Errno::ENOTCONN),
708        (IoError::NotFound.into(), Errno::ENOENT),
709        (IoError::OperationNotPermitted.into(), Errno::EPERM),
710        (IoError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
711        (IoError::OutOfRange.into(), Errno::ERANGE),
712        (IoError::PermissionDenied.into(), Errno::EACCES),
713        (
714            IoError::ProtocolOptionUnsupported.into(),
715            Errno::ENOPROTOOPT,
716        ),
717        (IoError::ReadOnlyFilesystem.into(), Errno::EROFS),
718        (IoError::ResourceBusy.into(), Errno::EBUSY),
719        (IoError::StorageFull.into(), Errno::ENOSPC),
720        (IoError::TimedOut.into(), Errno::ETIMEDOUT),
721        (IoError::TooManyOpenFiles.into(), Errno::EMFILE),
722        (IoError::UnexpectedEof.into(), Errno::EIO),
723        (IoError::Unsupported.into(), Errno::ENOSYS),
724        (IoError::WouldBlock.into(), Errno::EAGAIN),
725        (IoError::WriteZero.into(), Errno::EIO),
726    ])
727}
728
729#[cfg(all(test, not(axtest)))]
730fn block_errno_mappings_hold() -> bool {
731    let device_error = |source| {
732        StarryError::from(BlockError::Device {
733            stage: "submit",
734            operation: RequestOp::Read,
735            lba: 9,
736            source,
737        })
738    };
739
740    errno_cases_hold([
741        (BlockError::InvalidRequest.into(), Errno::EINVAL),
742        (BlockError::InvalidState.into(), Errno::EFAULT),
743        (BlockError::RuntimeUnavailable.into(), Errno::EFAULT),
744        (BlockError::WouldBlock.into(), Errno::EAGAIN),
745        (BlockError::NoMemory.into(), Errno::ENOMEM),
746        (BlockError::Unsupported.into(), Errno::ENOSYS),
747        (BlockError::TimedOut.into(), Errno::ETIMEDOUT),
748        (BlockError::Io.into(), Errno::EIO),
749        (BlockError::ResourceBusy.into(), Errno::EBUSY),
750        (BlockError::NotFound.into(), Errno::ENOENT),
751        (device_error(BlkError::NotSupported), Errno::ENOSYS),
752        (device_error(BlkError::Retry), Errno::EAGAIN),
753        (device_error(BlkError::NoMemory), Errno::ENOMEM),
754        (device_error(BlkError::InvalidBlockIndex(9)), Errno::EINVAL),
755        (device_error(BlkError::InvalidRequest), Errno::EINVAL),
756        (device_error(BlkError::TimedOut), Errno::ETIMEDOUT),
757        (device_error(BlkError::Io), Errno::EIO),
758        (device_error(BlkError::Other("device failure")), Errno::EIO),
759    ])
760}
761
762#[cfg(all(test, not(axtest)))]
763fn leaf_errno_mappings_hold() -> bool {
764    errno_cases_hold([
765        (StarryError::AlreadyExists, Errno::EEXIST),
766        (StarryError::ArgumentListTooLong, Errno::E2BIG),
767        (StarryError::BadAddress, Errno::EFAULT),
768        (StarryError::BadFileDescriptor, Errno::EBADF),
769        (StarryError::BadState, Errno::EFAULT),
770        (StarryError::BrokenPipe, Errno::EPIPE),
771        (StarryError::CrossesDevices, Errno::EXDEV),
772        (StarryError::FilesystemLoop, Errno::ELOOP),
773        (StarryError::IllegalBytes, Errno::EILSEQ),
774        (StarryError::InProgress, Errno::EINPROGRESS),
775        (StarryError::Interrupted, Errno::EINTR),
776        (StarryError::InterruptedNoRestart, Errno::EINTR),
777        (StarryError::InvalidData, Errno::EINVAL),
778        (StarryError::InvalidExecutable, Errno::ENOEXEC),
779        (StarryError::MalformedExecutable, Errno::ENOEXEC),
780        (StarryError::InvalidInput, Errno::EINVAL),
781        (StarryError::Io, Errno::EIO),
782        (StarryError::IsADirectory, Errno::EISDIR),
783        (StarryError::NameTooLong, Errno::ENAMETOOLONG),
784        (StarryError::NoMemory, Errno::ENOMEM),
785        (StarryError::NoSuchDevice, Errno::ENODEV),
786        (StarryError::NoSuchDeviceOrAddress, Errno::ENXIO),
787        (StarryError::NoSuchProcess, Errno::ESRCH),
788        (StarryError::NotADirectory, Errno::ENOTDIR),
789        (StarryError::NotASocket, Errno::ENOTSOCK),
790        (StarryError::NotATty, Errno::ENOTTY),
791        (StarryError::NotFound, Errno::ENOENT),
792        (StarryError::OperationNotPermitted, Errno::EPERM),
793        (StarryError::OperationNotSupported, Errno::EOPNOTSUPP),
794        (StarryError::OutOfRange, Errno::ERANGE),
795        (StarryError::PermissionDenied, Errno::EACCES),
796        (StarryError::ReadOnlyFilesystem, Errno::EROFS),
797        (StarryError::ResourceBusy, Errno::EBUSY),
798        (StarryError::StorageFull, Errno::ENOSPC),
799        (StarryError::TimedOut, Errno::ETIMEDOUT),
800        (StarryError::TooManyOpenFiles, Errno::EMFILE),
801        (StarryError::UnexpectedEof, Errno::EIO),
802        (StarryError::Unsupported, Errno::ENOSYS),
803        (StarryError::WouldBlock, Errno::EAGAIN),
804        (StarryError::WriteZero, Errno::EIO),
805        (StarryError::Format(core::fmt::Error), Errno::EINVAL),
806        (StarryError::Task(TaskError::TimerCapacity), Errno::ENOMEM),
807        (
808            StarryError::Runtime(RuntimeError::SerialNotStarted),
809            Errno::EFAULT,
810        ),
811        (
812            StarryError::Runtime(RuntimeError::SerialControlBusy),
813            Errno::EBUSY,
814        ),
815        (
816            StarryError::Runtime(RuntimeError::Task(TaskError::UnsafeContext)),
817            Errno::EPERM,
818        ),
819        (
820            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::InvalidBaudrate)),
821            Errno::EINVAL,
822        ),
823        (
824            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedDataBits)),
825            Errno::EINVAL,
826        ),
827        (
828            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedStopBits)),
829            Errno::EINVAL,
830        ),
831        (
832            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedParity)),
833            Errno::EINVAL,
834        ),
835        (
836            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::Timeout)),
837            Errno::ETIMEDOUT,
838        ),
839        (
840            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::RegisterError)),
841            Errno::EIO,
842        ),
843        (
844            StarryError::Runtime(RuntimeError::WouldBlock),
845            Errno::EAGAIN,
846        ),
847        (
848            StarryError::Runtime(RuntimeError::OperationNotSupported),
849            Errno::EOPNOTSUPP,
850        ),
851        (
852            StarryError::Runtime(RuntimeError::InvalidCpu { cpu: 99 }),
853            Errno::EINVAL,
854        ),
855        (
856            StarryError::Dma {
857                operation: DmaOperation::BufferAllocation,
858                source: DmaError::NoMemory,
859            },
860            Errno::ENOMEM,
861        ),
862        (
863            StarryError::Dma {
864                operation: DmaOperation::DeviceIo,
865                source: DmaError::NullPointer,
866            },
867            Errno::EINVAL,
868        ),
869    ])
870}
871
872#[cfg(all(test, not(axtest)))]
873fn domain_errno_mappings_hold() -> bool {
874    memory_errno_mappings_hold()
875        && vfs_errno_mappings_hold()
876        && io_errno_mappings_hold()
877        && block_errno_mappings_hold()
878        && leaf_errno_mappings_hold()
879        && StarryError::from(Errno::EOWNERDEAD).linux_errno() == Errno::EOWNERDEAD
880        && StarryError::from(Errno::new(4094)).linux_errno().into_raw() == 4094
881}
882
883/// A result returned by Starry-owned kernel operations.
884pub type StarryResult<T = ()> = Result<T, StarryError>;
885
886#[cfg(all(test, not(axtest)))]
887mod tests {
888    use super::*;
889
890    #[test]
891    fn domain_errors_map_to_stable_linux_errno() {
892        assert!(domain_errno_mappings_hold());
893    }
894
895    #[test]
896    fn readiness_retry_recognizes_filesystem_would_block_without_flattening() {
897        assert!(StarryError::WouldBlock.is_would_block());
898        assert!(StarryError::from(VfsError::WouldBlock).is_would_block());
899        assert!(StarryError::from(IoError::WouldBlock).is_would_block());
900        assert!(!StarryError::from(VfsError::Io).is_would_block());
901        assert!(!StarryError::from(IoError::Io).is_would_block());
902    }
903
904    #[test]
905    fn native_linux_errno_is_not_canonicalized() {
906        let error = StarryError::from(Errno::EOWNERDEAD);
907        assert_eq!(error.linux_errno(), Errno::EOWNERDEAD);
908    }
909
910    #[test]
911    fn unknown_linux_errno_is_preserved() {
912        let error = StarryError::from(Errno::new(4094));
913        assert_eq!(error.linux_errno().into_raw(), 4094);
914    }
915
916    #[cfg(feature = "sg2002")]
917    #[test]
918    fn sg2002_tpu_errors_keep_their_linux_errno() {
919        let cases = [
920            (TpuError::Timeout, Errno::ETIMEDOUT),
921            (TpuError::InvalidDmabuf, Errno::EINVAL),
922            (TpuError::TdmaError(1), Errno::EIO),
923            (TpuError::TiuError(1), Errno::EIO),
924            (TpuError::NotInitialized, Errno::ENODEV),
925            (TpuError::Busy, Errno::EBUSY),
926            (TpuError::Interrupted, Errno::EINTR),
927            (TpuError::PmuBufferNotAligned, Errno::EINVAL),
928            (TpuError::DmabufNotAligned, Errno::EINVAL),
929        ];
930        for (error, expected) in cases {
931            assert_eq!(StarryError::from(error).linux_errno(), expected);
932        }
933    }
934}