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::EBUSY => VfsError::ResourceBusy,
394        Errno::ENOSPC => VfsError::StorageFull,
395        Errno::ETIMEDOUT => VfsError::TimedOut,
396        Errno::EMLINK => VfsError::TooManyLinks,
397        Errno::ENOSYS => VfsError::Unsupported,
398        Errno::EOVERFLOW => VfsError::ValueOverflow,
399        Errno::EAGAIN => VfsError::WouldBlock,
400        _ => VfsError::Io,
401    }
402}
403
404fn runtime_errno(error: &RuntimeError) -> Errno {
405    match error {
406        RuntimeError::SerialConfig(error) => match error {
407            ConfigError::InvalidBaudrate
408            | ConfigError::UnsupportedDataBits
409            | ConfigError::UnsupportedStopBits
410            | ConfigError::UnsupportedParity => Errno::EINVAL,
411            ConfigError::Timeout => Errno::ETIMEDOUT,
412            ConfigError::RegisterError => Errno::EIO,
413        },
414        RuntimeError::SerialNotStarted => Errno::EFAULT,
415        RuntimeError::SerialControlBusy => Errno::EBUSY,
416        RuntimeError::Task(error) => task_errno(*error),
417        RuntimeError::WouldBlock => Errno::EAGAIN,
418        RuntimeError::OperationNotSupported => Errno::EOPNOTSUPP,
419        RuntimeError::InvalidCpu { .. } => Errno::EINVAL,
420        _ => Errno::EIO,
421    }
422}
423
424fn block_errno(error: &BlockError) -> Errno {
425    match error {
426        BlockError::InvalidRequest => Errno::EINVAL,
427        BlockError::InvalidState | BlockError::RuntimeUnavailable => Errno::EFAULT,
428        BlockError::WouldBlock => Errno::EAGAIN,
429        BlockError::NoMemory => Errno::ENOMEM,
430        BlockError::Unsupported => Errno::ENOSYS,
431        BlockError::TimedOut => Errno::ETIMEDOUT,
432        BlockError::ResourceBusy => Errno::EBUSY,
433        BlockError::NotFound => Errno::ENOENT,
434        BlockError::Io | BlockError::Irq(_) => Errno::EIO,
435        BlockError::Device { source, .. } => block_errno(&BlockError::from(*source)),
436    }
437}
438
439#[cfg(feature = "sg2002")]
440fn ion_errno(error: IonError) -> Errno {
441    match error {
442        IonError::InvalidArg => Errno::EINVAL,
443        IonError::NoMemory => Errno::ENOMEM,
444        IonError::InvalidBuffer | IonError::BufferNotFound => Errno::ENOENT,
445        IonError::BufferExists => Errno::EEXIST,
446        IonError::InvalidHeap | IonError::NotSupported => Errno::ENOSYS,
447        IonError::Internal => Errno::EINTR,
448    }
449}
450
451#[cfg(feature = "sg2002")]
452fn tpu_errno(error: TpuError) -> Errno {
453    match error {
454        TpuError::Timeout => Errno::ETIMEDOUT,
455        TpuError::InvalidDmabuf | TpuError::PmuBufferNotAligned | TpuError::DmabufNotAligned => {
456            Errno::EINVAL
457        }
458        TpuError::TdmaError(_) | TpuError::TiuError(_) => Errno::EIO,
459        TpuError::NotInitialized => Errno::ENODEV,
460        TpuError::Busy => Errno::EBUSY,
461        TpuError::Interrupted => Errno::EINTR,
462    }
463}
464
465fn dma_errno(operation: DmaOperation, source: &DmaError) -> Errno {
466    match operation {
467        DmaOperation::BufferAllocation => match source {
468            DmaError::LayoutError(_) => Errno::EINVAL,
469            _ => Errno::ENOMEM,
470        },
471        DmaOperation::DeviceIo => match source {
472            DmaError::NoMemory => Errno::ENOMEM,
473            DmaError::LayoutError(_) | DmaError::NullPointer | DmaError::ZeroSizedBuffer => {
474                Errno::EINVAL
475            }
476            _ => Errno::EIO,
477        },
478    }
479}
480
481fn io_errno(error: IoError) -> Errno {
482    match error {
483        IoError::AddrInUse => Errno::EADDRINUSE,
484        IoError::AlreadyConnected => Errno::EISCONN,
485        IoError::AddressFamilyUnsupported => Errno::EAFNOSUPPORT,
486        IoError::AlreadyExists => Errno::EEXIST,
487        IoError::ArgumentListTooLong => Errno::E2BIG,
488        IoError::BadAddress | IoError::BadState => Errno::EFAULT,
489        IoError::BadFileDescriptor => Errno::EBADF,
490        IoError::BrokenPipe => Errno::EPIPE,
491        IoError::ConnectionRefused => Errno::ECONNREFUSED,
492        IoError::ConnectionReset => Errno::ECONNRESET,
493        IoError::CrossesDevices => Errno::EXDEV,
494        IoError::DirectoryNotEmpty => Errno::ENOTEMPTY,
495        IoError::DestAddrRequired => Errno::EDESTADDRREQ,
496        IoError::FilesystemLoop => Errno::ELOOP,
497        IoError::FileTooLarge => Errno::EFBIG,
498        IoError::IllegalBytes => Errno::EILSEQ,
499        IoError::InProgress => Errno::EINPROGRESS,
500        IoError::Interrupted => Errno::EINTR,
501        IoError::InvalidData | IoError::InvalidInput => Errno::EINVAL,
502        IoError::InvalidExecutable => Errno::ENOEXEC,
503        IoError::Io | IoError::UnexpectedEof | IoError::WriteZero => Errno::EIO,
504        IoError::IsADirectory => Errno::EISDIR,
505        IoError::MessageTooLong => Errno::EMSGSIZE,
506        IoError::NameTooLong => Errno::ENAMETOOLONG,
507        IoError::NoMemory => Errno::ENOMEM,
508        IoError::NoSuchDevice => Errno::ENODEV,
509        IoError::NoSuchDeviceOrAddress => Errno::ENXIO,
510        IoError::NoSuchProcess => Errno::ESRCH,
511        IoError::NotADirectory => Errno::ENOTDIR,
512        IoError::NotASocket => Errno::ENOTSOCK,
513        IoError::NotATty => Errno::ENOTTY,
514        IoError::NotConnected => Errno::ENOTCONN,
515        IoError::NotFound => Errno::ENOENT,
516        IoError::OperationNotPermitted => Errno::EPERM,
517        IoError::OperationNotSupported => Errno::EOPNOTSUPP,
518        IoError::OutOfRange => Errno::ERANGE,
519        IoError::PermissionDenied => Errno::EACCES,
520        IoError::ProtocolOptionUnsupported => Errno::ENOPROTOOPT,
521        IoError::ReadOnlyFilesystem => Errno::EROFS,
522        IoError::ResourceBusy => Errno::EBUSY,
523        IoError::StorageFull => Errno::ENOSPC,
524        IoError::TimedOut => Errno::ETIMEDOUT,
525        IoError::TooManyOpenFiles => Errno::EMFILE,
526        IoError::Unsupported => Errno::ENOSYS,
527        IoError::WouldBlock => Errno::EAGAIN,
528    }
529}
530
531fn vfs_errno(error: VfsError) -> Errno {
532    match error {
533        VfsError::AlreadyExists => Errno::EEXIST,
534        VfsError::BadAddress | VfsError::BadState => Errno::EFAULT,
535        VfsError::BadFileDescriptor => Errno::EBADF,
536        VfsError::CrossesDevices => Errno::EXDEV,
537        VfsError::DataMissing => Errno::ENODATA,
538        VfsError::DirectoryNotEmpty => Errno::ENOTEMPTY,
539        VfsError::FilesystemCorrupted => Errno::EUCLEAN,
540        VfsError::FilesystemLoop => Errno::ELOOP,
541        VfsError::FileTooLarge => Errno::EFBIG,
542        VfsError::InvalidData | VfsError::InvalidInput => Errno::EINVAL,
543        VfsError::Interrupted => Errno::EINTR,
544        VfsError::Io => Errno::EIO,
545        VfsError::IsADirectory => Errno::EISDIR,
546        VfsError::NameTooLong => Errno::ENAMETOOLONG,
547        VfsError::NoMemory => Errno::ENOMEM,
548        VfsError::NoSuchDevice => Errno::ENODEV,
549        VfsError::NoSuchDeviceOrAddress => Errno::ENXIO,
550        VfsError::NotADirectory => Errno::ENOTDIR,
551        VfsError::NotATty => Errno::ENOTTY,
552        VfsError::NotFound => Errno::ENOENT,
553        VfsError::OperationNotPermitted => Errno::EPERM,
554        VfsError::OperationNotSupported => Errno::EOPNOTSUPP,
555        VfsError::PermissionDenied => Errno::EACCES,
556        VfsError::QuotaExceeded => Errno::EDQUOT,
557        VfsError::ReadOnlyFilesystem => Errno::EROFS,
558        VfsError::ResourceBusy => Errno::EBUSY,
559        VfsError::StorageFull => Errno::ENOSPC,
560        VfsError::TimedOut => Errno::ETIMEDOUT,
561        VfsError::TooManyLinks => Errno::EMLINK,
562        VfsError::Unsupported => Errno::ENOSYS,
563        VfsError::ValueOverflow => Errno::EOVERFLOW,
564        VfsError::WouldBlock => Errno::EAGAIN,
565    }
566}
567
568#[cfg(all(test, not(axtest)))]
569fn errno_cases_hold<const N: usize>(cases: [(StarryError, Errno); N]) -> bool {
570    cases
571        .into_iter()
572        .all(|(error, expected)| error.linux_errno() == expected)
573}
574
575#[cfg(all(test, not(axtest)))]
576fn memory_errno_mappings_hold() -> bool {
577    errno_cases_hold([
578        (VmError::BadAddress.into(), Errno::EFAULT),
579        (VmError::AccessDenied.into(), Errno::EFAULT),
580        (VmError::TooLong.into(), Errno::ENAMETOOLONG),
581        (
582            SignalError::UserMemory(VmError::BadAddress).into(),
583            Errno::EFAULT,
584        ),
585        (MmError::InvalidInput("test").into(), Errno::EINVAL),
586        (MmError::NoMemory.into(), Errno::ENOMEM),
587        (MmError::AlreadyExists.into(), Errno::EEXIST),
588        (MmError::BadAddress.into(), Errno::EFAULT),
589        (MmError::BadState("test").into(), Errno::EFAULT),
590        (MmError::Unsupported.into(), Errno::ENOSYS),
591        (
592            MmError::TlbShootdown(TlbShootdownError::Timeout).into(),
593            Errno::ETIMEDOUT,
594        ),
595        (MappingError::InvalidParam.into(), Errno::EINVAL),
596        (MappingError::AlreadyExists.into(), Errno::EEXIST),
597        (MappingError::BadState.into(), Errno::EFAULT),
598        (PagingError::NoMemory.into(), Errno::ENOMEM),
599        (PagingError::not_mapped().into(), Errno::EINVAL),
600        (TlbShootdownError::CpuOffline.into(), Errno::ENOSYS),
601        (TlbShootdownError::Timeout.into(), Errno::ETIMEDOUT),
602        (TlbShootdownError::Unsupported.into(), Errno::ENOSYS),
603        (TlbShootdownError::Platform.into(), Errno::EIO),
604        (
605            TlbShootdownError::GenerationExhausted.into(),
606            Errno::EOVERFLOW,
607        ),
608        (AllocError::InvalidParam.into(), Errno::EINVAL),
609        (AllocError::AlreadyInitialized.into(), Errno::EFAULT),
610        (AllocError::MemoryOverlap.into(), Errno::EEXIST),
611        (AllocError::NoMemory.into(), Errno::ENOMEM),
612        (AllocError::NotAllocated.into(), Errno::EINVAL),
613        (AllocError::NotInitialized.into(), Errno::EFAULT),
614        (AllocError::NotFound.into(), Errno::ENOENT),
615        (CgroupError::NotInitialized.into(), Errno::EINVAL),
616        (CgroupError::NotFound.into(), Errno::ENOENT),
617        (CgroupError::AlreadyExists.into(), Errno::EEXIST),
618        (CgroupError::ResourceBusy.into(), Errno::EBUSY),
619        (CgroupError::LimitExceeded.into(), Errno::EAGAIN),
620        (CgroupError::InvalidInput.into(), Errno::EINVAL),
621        (CgroupError::NoSuchProcess.into(), Errno::ESRCH),
622        (CgroupError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
623    ])
624}
625
626#[cfg(all(test, not(axtest)))]
627fn vfs_errno_mappings_hold() -> bool {
628    errno_cases_hold([
629        (VfsError::AlreadyExists.into(), Errno::EEXIST),
630        (VfsError::BadAddress.into(), Errno::EFAULT),
631        (VfsError::BadFileDescriptor.into(), Errno::EBADF),
632        (VfsError::BadState.into(), Errno::EFAULT),
633        (VfsError::CrossesDevices.into(), Errno::EXDEV),
634        (VfsError::DataMissing.into(), Errno::ENODATA),
635        (VfsError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
636        (VfsError::FilesystemCorrupted.into(), Errno::EUCLEAN),
637        (VfsError::FilesystemLoop.into(), Errno::ELOOP),
638        (VfsError::FileTooLarge.into(), Errno::EFBIG),
639        (VfsError::InvalidData.into(), Errno::EINVAL),
640        (VfsError::InvalidInput.into(), Errno::EINVAL),
641        (VfsError::Interrupted.into(), Errno::EINTR),
642        (VfsError::Io.into(), Errno::EIO),
643        (VfsError::IsADirectory.into(), Errno::EISDIR),
644        (VfsError::NameTooLong.into(), Errno::ENAMETOOLONG),
645        (VfsError::NoMemory.into(), Errno::ENOMEM),
646        (VfsError::NoSuchDevice.into(), Errno::ENODEV),
647        (VfsError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
648        (VfsError::NotADirectory.into(), Errno::ENOTDIR),
649        (VfsError::NotATty.into(), Errno::ENOTTY),
650        (VfsError::NotFound.into(), Errno::ENOENT),
651        (VfsError::OperationNotPermitted.into(), Errno::EPERM),
652        (VfsError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
653        (VfsError::PermissionDenied.into(), Errno::EACCES),
654        (VfsError::QuotaExceeded.into(), Errno::EDQUOT),
655        (VfsError::ReadOnlyFilesystem.into(), Errno::EROFS),
656        (VfsError::ResourceBusy.into(), Errno::EBUSY),
657        (VfsError::StorageFull.into(), Errno::ENOSPC),
658        (VfsError::TimedOut.into(), Errno::ETIMEDOUT),
659        (VfsError::TooManyLinks.into(), Errno::EMLINK),
660        (VfsError::Unsupported.into(), Errno::ENOSYS),
661        (VfsError::ValueOverflow.into(), Errno::EOVERFLOW),
662        (VfsError::WouldBlock.into(), Errno::EAGAIN),
663    ])
664}
665
666#[cfg(all(test, not(axtest)))]
667fn io_errno_mappings_hold() -> bool {
668    errno_cases_hold([
669        (IoError::AddrInUse.into(), Errno::EADDRINUSE),
670        (IoError::AlreadyConnected.into(), Errno::EISCONN),
671        (
672            IoError::AddressFamilyUnsupported.into(),
673            Errno::EAFNOSUPPORT,
674        ),
675        (IoError::AlreadyExists.into(), Errno::EEXIST),
676        (IoError::ArgumentListTooLong.into(), Errno::E2BIG),
677        (IoError::BadAddress.into(), Errno::EFAULT),
678        (IoError::BadFileDescriptor.into(), Errno::EBADF),
679        (IoError::BadState.into(), Errno::EFAULT),
680        (IoError::BrokenPipe.into(), Errno::EPIPE),
681        (IoError::ConnectionRefused.into(), Errno::ECONNREFUSED),
682        (IoError::ConnectionReset.into(), Errno::ECONNRESET),
683        (IoError::CrossesDevices.into(), Errno::EXDEV),
684        (IoError::DirectoryNotEmpty.into(), Errno::ENOTEMPTY),
685        (IoError::DestAddrRequired.into(), Errno::EDESTADDRREQ),
686        (IoError::FilesystemLoop.into(), Errno::ELOOP),
687        (IoError::FileTooLarge.into(), Errno::EFBIG),
688        (IoError::IllegalBytes.into(), Errno::EILSEQ),
689        (IoError::InProgress.into(), Errno::EINPROGRESS),
690        (IoError::Interrupted.into(), Errno::EINTR),
691        (IoError::InvalidData.into(), Errno::EINVAL),
692        (IoError::InvalidExecutable.into(), Errno::ENOEXEC),
693        (IoError::InvalidInput.into(), Errno::EINVAL),
694        (IoError::Io.into(), Errno::EIO),
695        (IoError::IsADirectory.into(), Errno::EISDIR),
696        (IoError::NameTooLong.into(), Errno::ENAMETOOLONG),
697        (IoError::MessageTooLong.into(), Errno::EMSGSIZE),
698        (IoError::NoMemory.into(), Errno::ENOMEM),
699        (IoError::NoSuchDevice.into(), Errno::ENODEV),
700        (IoError::NoSuchDeviceOrAddress.into(), Errno::ENXIO),
701        (IoError::NoSuchProcess.into(), Errno::ESRCH),
702        (IoError::NotADirectory.into(), Errno::ENOTDIR),
703        (IoError::NotASocket.into(), Errno::ENOTSOCK),
704        (IoError::NotATty.into(), Errno::ENOTTY),
705        (IoError::NotConnected.into(), Errno::ENOTCONN),
706        (IoError::NotFound.into(), Errno::ENOENT),
707        (IoError::OperationNotPermitted.into(), Errno::EPERM),
708        (IoError::OperationNotSupported.into(), Errno::EOPNOTSUPP),
709        (IoError::OutOfRange.into(), Errno::ERANGE),
710        (IoError::PermissionDenied.into(), Errno::EACCES),
711        (
712            IoError::ProtocolOptionUnsupported.into(),
713            Errno::ENOPROTOOPT,
714        ),
715        (IoError::ReadOnlyFilesystem.into(), Errno::EROFS),
716        (IoError::ResourceBusy.into(), Errno::EBUSY),
717        (IoError::StorageFull.into(), Errno::ENOSPC),
718        (IoError::TimedOut.into(), Errno::ETIMEDOUT),
719        (IoError::TooManyOpenFiles.into(), Errno::EMFILE),
720        (IoError::UnexpectedEof.into(), Errno::EIO),
721        (IoError::Unsupported.into(), Errno::ENOSYS),
722        (IoError::WouldBlock.into(), Errno::EAGAIN),
723        (IoError::WriteZero.into(), Errno::EIO),
724    ])
725}
726
727#[cfg(all(test, not(axtest)))]
728fn block_errno_mappings_hold() -> bool {
729    let device_error = |source| {
730        StarryError::from(BlockError::Device {
731            stage: "submit",
732            operation: RequestOp::Read,
733            lba: 9,
734            source,
735        })
736    };
737
738    errno_cases_hold([
739        (BlockError::InvalidRequest.into(), Errno::EINVAL),
740        (BlockError::InvalidState.into(), Errno::EFAULT),
741        (BlockError::RuntimeUnavailable.into(), Errno::EFAULT),
742        (BlockError::WouldBlock.into(), Errno::EAGAIN),
743        (BlockError::NoMemory.into(), Errno::ENOMEM),
744        (BlockError::Unsupported.into(), Errno::ENOSYS),
745        (BlockError::TimedOut.into(), Errno::ETIMEDOUT),
746        (BlockError::Io.into(), Errno::EIO),
747        (BlockError::ResourceBusy.into(), Errno::EBUSY),
748        (BlockError::NotFound.into(), Errno::ENOENT),
749        (device_error(BlkError::NotSupported), Errno::ENOSYS),
750        (device_error(BlkError::Retry), Errno::EAGAIN),
751        (device_error(BlkError::NoMemory), Errno::ENOMEM),
752        (device_error(BlkError::InvalidBlockIndex(9)), Errno::EINVAL),
753        (device_error(BlkError::InvalidRequest), Errno::EINVAL),
754        (device_error(BlkError::TimedOut), Errno::ETIMEDOUT),
755        (device_error(BlkError::Io), Errno::EIO),
756        (device_error(BlkError::Other("device failure")), Errno::EIO),
757    ])
758}
759
760#[cfg(all(test, not(axtest)))]
761fn leaf_errno_mappings_hold() -> bool {
762    errno_cases_hold([
763        (StarryError::AlreadyExists, Errno::EEXIST),
764        (StarryError::ArgumentListTooLong, Errno::E2BIG),
765        (StarryError::BadAddress, Errno::EFAULT),
766        (StarryError::BadFileDescriptor, Errno::EBADF),
767        (StarryError::BadState, Errno::EFAULT),
768        (StarryError::BrokenPipe, Errno::EPIPE),
769        (StarryError::CrossesDevices, Errno::EXDEV),
770        (StarryError::FilesystemLoop, Errno::ELOOP),
771        (StarryError::IllegalBytes, Errno::EILSEQ),
772        (StarryError::InProgress, Errno::EINPROGRESS),
773        (StarryError::Interrupted, Errno::EINTR),
774        (StarryError::InterruptedNoRestart, Errno::EINTR),
775        (StarryError::InvalidData, Errno::EINVAL),
776        (StarryError::InvalidExecutable, Errno::ENOEXEC),
777        (StarryError::MalformedExecutable, Errno::ENOEXEC),
778        (StarryError::InvalidInput, Errno::EINVAL),
779        (StarryError::Io, Errno::EIO),
780        (StarryError::IsADirectory, Errno::EISDIR),
781        (StarryError::NameTooLong, Errno::ENAMETOOLONG),
782        (StarryError::NoMemory, Errno::ENOMEM),
783        (StarryError::NoSuchDevice, Errno::ENODEV),
784        (StarryError::NoSuchDeviceOrAddress, Errno::ENXIO),
785        (StarryError::NoSuchProcess, Errno::ESRCH),
786        (StarryError::NotADirectory, Errno::ENOTDIR),
787        (StarryError::NotASocket, Errno::ENOTSOCK),
788        (StarryError::NotATty, Errno::ENOTTY),
789        (StarryError::NotFound, Errno::ENOENT),
790        (StarryError::OperationNotPermitted, Errno::EPERM),
791        (StarryError::OperationNotSupported, Errno::EOPNOTSUPP),
792        (StarryError::OutOfRange, Errno::ERANGE),
793        (StarryError::PermissionDenied, Errno::EACCES),
794        (StarryError::ReadOnlyFilesystem, Errno::EROFS),
795        (StarryError::ResourceBusy, Errno::EBUSY),
796        (StarryError::StorageFull, Errno::ENOSPC),
797        (StarryError::TimedOut, Errno::ETIMEDOUT),
798        (StarryError::TooManyOpenFiles, Errno::EMFILE),
799        (StarryError::UnexpectedEof, Errno::EIO),
800        (StarryError::Unsupported, Errno::ENOSYS),
801        (StarryError::WouldBlock, Errno::EAGAIN),
802        (StarryError::WriteZero, Errno::EIO),
803        (StarryError::Format(core::fmt::Error), Errno::EINVAL),
804        (StarryError::Task(TaskError::TimerCapacity), Errno::ENOMEM),
805        (
806            StarryError::Runtime(RuntimeError::SerialNotStarted),
807            Errno::EFAULT,
808        ),
809        (
810            StarryError::Runtime(RuntimeError::SerialControlBusy),
811            Errno::EBUSY,
812        ),
813        (
814            StarryError::Runtime(RuntimeError::Task(TaskError::UnsafeContext)),
815            Errno::EPERM,
816        ),
817        (
818            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::InvalidBaudrate)),
819            Errno::EINVAL,
820        ),
821        (
822            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedDataBits)),
823            Errno::EINVAL,
824        ),
825        (
826            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedStopBits)),
827            Errno::EINVAL,
828        ),
829        (
830            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::UnsupportedParity)),
831            Errno::EINVAL,
832        ),
833        (
834            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::Timeout)),
835            Errno::ETIMEDOUT,
836        ),
837        (
838            StarryError::Runtime(RuntimeError::SerialConfig(ConfigError::RegisterError)),
839            Errno::EIO,
840        ),
841        (
842            StarryError::Runtime(RuntimeError::WouldBlock),
843            Errno::EAGAIN,
844        ),
845        (
846            StarryError::Runtime(RuntimeError::OperationNotSupported),
847            Errno::EOPNOTSUPP,
848        ),
849        (
850            StarryError::Runtime(RuntimeError::InvalidCpu { cpu: 99 }),
851            Errno::EINVAL,
852        ),
853        (
854            StarryError::Dma {
855                operation: DmaOperation::BufferAllocation,
856                source: DmaError::NoMemory,
857            },
858            Errno::ENOMEM,
859        ),
860        (
861            StarryError::Dma {
862                operation: DmaOperation::DeviceIo,
863                source: DmaError::NullPointer,
864            },
865            Errno::EINVAL,
866        ),
867    ])
868}
869
870#[cfg(all(test, not(axtest)))]
871fn domain_errno_mappings_hold() -> bool {
872    memory_errno_mappings_hold()
873        && vfs_errno_mappings_hold()
874        && io_errno_mappings_hold()
875        && block_errno_mappings_hold()
876        && leaf_errno_mappings_hold()
877        && StarryError::from(Errno::EOWNERDEAD).linux_errno() == Errno::EOWNERDEAD
878        && StarryError::from(Errno::new(4094)).linux_errno().into_raw() == 4094
879}
880
881/// A result returned by Starry-owned kernel operations.
882pub type StarryResult<T = ()> = Result<T, StarryError>;
883
884#[cfg(all(test, not(axtest)))]
885mod tests {
886    use super::*;
887
888    #[test]
889    fn domain_errors_map_to_stable_linux_errno() {
890        assert!(domain_errno_mappings_hold());
891    }
892
893    #[test]
894    fn readiness_retry_recognizes_filesystem_would_block_without_flattening() {
895        assert!(StarryError::WouldBlock.is_would_block());
896        assert!(StarryError::from(VfsError::WouldBlock).is_would_block());
897        assert!(StarryError::from(IoError::WouldBlock).is_would_block());
898        assert!(!StarryError::from(VfsError::Io).is_would_block());
899        assert!(!StarryError::from(IoError::Io).is_would_block());
900    }
901
902    #[test]
903    fn native_linux_errno_is_not_canonicalized() {
904        let error = StarryError::from(Errno::EOWNERDEAD);
905        assert_eq!(error.linux_errno(), Errno::EOWNERDEAD);
906    }
907
908    #[test]
909    fn unknown_linux_errno_is_preserved() {
910        let error = StarryError::from(Errno::new(4094));
911        assert_eq!(error.linux_errno().into_raw(), 4094);
912    }
913
914    #[cfg(feature = "sg2002")]
915    #[test]
916    fn sg2002_tpu_errors_keep_their_linux_errno() {
917        let cases = [
918            (TpuError::Timeout, Errno::ETIMEDOUT),
919            (TpuError::InvalidDmabuf, Errno::EINVAL),
920            (TpuError::TdmaError(1), Errno::EIO),
921            (TpuError::TiuError(1), Errno::EIO),
922            (TpuError::NotInitialized, Errno::ENODEV),
923            (TpuError::Busy, Errno::EBUSY),
924            (TpuError::Interrupted, Errno::EINTR),
925            (TpuError::PmuBufferNotAligned, Errno::EINVAL),
926            (TpuError::DmabufNotAligned, Errno::EINVAL),
927        ];
928        for (error, expected) in cases {
929            assert_eq!(StarryError::from(error).linux_errno(), expected);
930        }
931    }
932}