Add 'lib/abi/' from commit 'fdb3e18b598f9250f9f5a6443390e0aac1f57071'

git-subtree-dir: lib/abi
git-subtree-mainline: 18fa8b954a2b9372920035b0d4cdcf0d2d5c0902
git-subtree-split: fdb3e18b598f9250f9f5a6443390e0aac1f57071
This commit is contained in:
Mark Poliakov 2024-03-12 15:53:19 +02:00
commit 22e2a992dd
36 changed files with 3630 additions and 0 deletions

1
lib/abi/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

34
lib/abi/Cargo.toml Normal file
View File

@ -0,0 +1,34 @@
[package]
name = "yggdrasil-abi"
version = "0.1.0"
edition = "2021"
authors = ["Mark Poliakov <mark@alnyan.me>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
rustc_std_alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" }
compiler_builtins = { version = "0.1", optional = true }
serde = { version = "1.0.193", features = ["derive"], default-features = false, optional = true }
bytemuck = { version = "1.14.0", features = ["derive"], optional = true }
abi-lib = { git = "https://git.alnyan.me/yggdrasil/abi-generator.git" }
[build-dependencies]
yggdrasil-abi-def = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi-def.git" }
abi-generator = { git = "https://git.alnyan.me/yggdrasil/abi-generator.git" }
prettyplease = "0.2.15"
[features]
default = []
alloc = []
abi-private = []
serde_kernel = ["serde", "serde/alloc"]
rustc-dep-of-std = [
"core",
"rustc_std_alloc",
"compiler_builtins/rustc-dep-of-std",
"abi-lib/rustc-dep-of-std"
]

32
lib/abi/build.rs Normal file
View File

@ -0,0 +1,32 @@
use std::{fs, path::Path};
use abi_generator::{
abi::{ty::TypeWidth, AbiBuilder},
syntax::UnwrapFancy,
TargetEnv,
};
fn generate_abi() {
let output_dir = std::env::var("OUT_DIR").expect("$OUT_DIR not set");
let generated_types = Path::new(&output_dir).join("generated_types.rs");
let abi = AbiBuilder::from_string(
yggdrasil_abi_def::ABI_FILE,
TargetEnv {
thin_pointer_width: TypeWidth::U64,
fat_pointer_width: TypeWidth::U64,
},
)
.unwrap_fancy("Could not parse/read ABI file");
let types = prettyplease::unparse(
&abi.emit_file(true, false)
.unwrap_fancy("Could not emit types"),
);
fs::write(generated_types, types).unwrap();
}
fn main() {
generate_abi();
}

View File

@ -0,0 +1,9 @@
#[allow(missing_docs)]
#[derive(Clone, Debug)]
#[repr(C)]
pub struct SavedFrame {
pub gp_regs: [u64; 32],
pub spsr_el1: u64,
pub elr_el1: u64,
pub sp_el0: u64,
}

13
lib/abi/src/arch/mod.rs Normal file
View File

@ -0,0 +1,13 @@
//! Architecture-specific ABI implementation details
#[cfg(target_arch = "aarch64")]
pub(crate) mod aarch64;
#[cfg(target_arch = "aarch64")]
use aarch64 as arch_impl;
#[cfg(target_arch = "x86_64")]
pub(crate) mod x86_64;
#[cfg(target_arch = "x86_64")]
use x86_64 as arch_impl;
pub use arch_impl::SavedFrame;

View File

@ -0,0 +1,26 @@
#![allow(missing_docs)]
#[derive(Clone, Debug)]
#[repr(C)]
pub struct SavedFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rsi: u64,
pub rdi: u64,
pub rbp: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub user_ip: u64,
pub user_sp: u64,
pub rflags: u64,
}

175
lib/abi/src/error.rs Normal file
View File

@ -0,0 +1,175 @@
//! Error definitions and conversion functions
use crate::{io::RawFd, primitive_enum};
primitive_enum!(
#[doc = "Describes an error reported by the kernel"]
pub enum Error: u32 {
#[doc = "Kernel ran out of virtual address space or physical memory"]
OutOfMemory = 1,
#[doc = "Attempted to perform an illegal memory operation"]
InvalidMemoryOperation = 2,
#[doc = "Entry/file already exists"]
AlreadyExists = 3,
#[doc = "Operation timed out"]
TimedOut = 4,
#[doc = "Invalid argument supplied to a kernel function"]
InvalidArgument = 5,
#[doc = "Entry/file does not exist"]
DoesNotExist = 6,
#[doc = "A directory found where something else was expected"]
IsADirectory = 7,
#[doc = "Invalid file descriptor supplied to a kernel function"]
InvalidFile = 8,
#[doc = "Kernel/driver function is not implemented"]
NotImplemented = 9,
#[doc = "Entry is not a directory"]
NotADirectory = 10,
#[doc = "Directory is not empty"]
DirectoryNotEmpty = 11,
#[doc = "Operation was interrupted"]
Interrupted = 12,
#[doc = "Operation has not completed yet"]
WouldBlock = 13,
#[doc = "Entry/file cannot be written to"]
ReadOnly = 14,
#[doc = "Requested operation cannot be performed or is invalid"]
InvalidOperation = 15,
#[doc = "No permission to access the entry/file"]
PermissionDenied = 16,
#[doc = "Unrecognized or invalid executable file format"]
UnrecognizedExecutable = 17,
#[doc = "Data received was less than expected"]
MissingData = 18,
#[doc = "Too many data received"]
QueueFull = 19,
#[doc = "Provided buffer was too small"]
BufferTooSmall = 20,
// Networking errors
#[doc = "No route to host"]
HostUnreachable = 128,
#[doc = "Network unreachable"]
NetworkUnreachable = 129,
#[doc = "Connection reset"]
ConnectionReset = 130,
#[doc = "Address already used"]
AddrInUse = 131,
#[doc = "Connection refused"]
ConnectionRefused = 132,
}
);
/// Interface for converting between system call result representations
pub trait SyscallResult: Sized {
/// Converts raw value into a system call result
fn from_syscall_result(value: usize) -> Result<Self, Error>;
/// Converts system call result into a raw value
fn into_syscall_result(self) -> usize;
}
/// Interface for converting between system call error representations
pub trait SyscallError {
/// Converts raw value into a system call error
fn from_syscall_error(value: usize) -> Self;
/// Converts system call error into a raw value
fn into_syscall_error(self) -> usize;
}
impl SyscallError for Error {
fn from_syscall_error(value: usize) -> Self {
Error::try_from((-(value as isize)) as u32).unwrap_or(Error::InvalidArgument)
}
fn into_syscall_error(self) -> usize {
(-((self as u32) as isize)) as usize
}
}
impl<T: SyscallResult> SyscallResult for Result<T, Error> {
fn into_syscall_result(self) -> usize {
match self {
Ok(t) => t.into_syscall_result(),
Err(e) => e.into_syscall_error(),
}
}
fn from_syscall_result(_value: usize) -> Result<Self, Error> {
todo!()
}
}
impl SyscallResult for () {
fn from_syscall_result(value: usize) -> Result<Self, Error> {
if (value as isize) < 0 {
Err(Error::from_syscall_error(value))
} else {
// TODO assert value == 0
Ok(())
}
}
fn into_syscall_result(self) -> usize {
0
}
}
impl SyscallResult for usize {
fn from_syscall_result(value: usize) -> Result<Self, Error> {
if (value as isize) < 0 {
Err(Error::from_syscall_error(value))
} else {
Ok(value)
}
}
fn into_syscall_result(self) -> usize {
assert!((self as isize) >= 0);
self
}
}
impl SyscallResult for RawFd {
fn from_syscall_result(value: usize) -> Result<Self, Error> {
if (value as isize) < 0 {
Err(Error::from_syscall_error(value))
} else {
// TODO assert value < u32::MAX
Ok(RawFd(value as u32))
}
}
fn into_syscall_result(self) -> usize {
self.0 as usize
}
}
impl SyscallResult for u64 {
fn from_syscall_result(value: usize) -> Result<Self, Error> {
if (value as isize) < 0 {
Err(Error::from_syscall_error(value))
} else {
Ok(value as _)
}
}
fn into_syscall_result(self) -> usize {
assert_eq!(self >> 63, 0);
self as _
}
}
impl SyscallResult for u32 {
fn from_syscall_result(value: usize) -> Result<Self, Error> {
if (value as isize) < 0 {
Err(Error::from_syscall_error(value))
} else {
Ok(value as _)
}
}
fn into_syscall_result(self) -> usize {
self as _
}
}

60
lib/abi/src/io/channel.rs Normal file
View File

@ -0,0 +1,60 @@
use abi_lib::SyscallRegister;
pub use crate::generated::ChannelPublisherId;
use super::RawFd;
/// Describes a message sent using SendMessage
#[derive(Clone, Debug)]
pub enum SentMessage<'m> {
/// A file is sent
File(RawFd),
/// Binary data is sent
Data(&'m [u8]),
}
/// Describes a message received using ReceiveMessage
#[derive(Debug)]
pub enum ReceivedMessageMetadata {
/// A file was received
File(RawFd),
/// Binary data was received (contains data length)
Data(usize),
}
/// Specifies where a message should be delivered on a channel
#[derive(Clone, Copy, Debug)]
pub enum MessageDestination {
/// Broadcast everywhere, including the sender.
///
/// # Note
///
/// May cause a feedback loop if the sender is also a subscriber of the channel.
All,
/// Broadcast everywhere, except the sender
AllExceptSelf,
/// Send to a specific subscriber ID
Specific(u32),
}
impl SyscallRegister for MessageDestination {
fn from_syscall_register(value: usize) -> Self {
match value {
0 => Self::All,
1 => Self::AllExceptSelf,
n => Self::Specific((n - 2) as _),
}
}
fn into_syscall_register(self) -> usize {
match self {
Self::All => 0,
Self::AllExceptSelf => 1,
Self::Specific(v) => (v + 2) as usize,
}
}
}
impl ChannelPublisherId {
pub const ZERO: Self = Self(0);
}

47
lib/abi/src/io/device.rs Normal file
View File

@ -0,0 +1,47 @@
use core::mem::MaybeUninit;
use crate::generated::ProcessId;
use super::terminal;
/// Describes how a mount operation should be performed
#[derive(Clone, Debug)]
#[repr(C)]
pub struct MountOptions<'a> {
/// Block device to use as a mount source. May be empty when mounting virtual filesystems.
pub source: Option<&'a str>,
/// Filesystem to use when mounting. Empty means "deduce automatically" and source must be set.
pub filesystem: Option<&'a str>,
/// Path to a directory to mount the filesystem to
pub target: &'a str,
}
/// Describes how an unmount operation should be performed
#[derive(Clone, Debug)]
#[repr(C)]
pub struct UnmountOptions {
/// Path to the mountpoint to unmount
pub mountpoint: &'static str,
}
// TODO make this accept references when "reading" something
/// Describes device-specific requests
#[derive(Clone, Debug)]
#[repr(C)]
#[non_exhaustive]
pub enum DeviceRequest {
/// Configure the terminal's options
SetTerminalOptions(terminal::TerminalOptions),
/// Read the terminal's options
GetTerminalOptions(MaybeUninit<terminal::TerminalOptions>),
/// Set a new terminal size
SetTerminalSize(terminal::TerminalSize),
/// Return the terminal's size
GetTerminalSize(MaybeUninit<terminal::TerminalSize>),
/// Sets a foreground process group ID for the terminal
SetTerminalGroup(ProcessId),
/// "Acquires" ownership of the device, preventing others from accessing it
AcquireDevice,
/// "Releases" ownership of the device
ReleaseDevice,
}

103
lib/abi/src/io/file.rs Normal file
View File

@ -0,0 +1,103 @@
use core::fmt;
use abi_lib::SyscallRegister;
use super::{FileMode, FileType, GroupId, UserId};
/// Describes an operation to perform when updating certain file metadata elements
#[derive(Debug, Clone)]
pub enum FileMetadataUpdateMode {
/// value = new
Set,
/// value = old | new
Or,
/// value = old & new
And,
/// value = old & !new
AndNot,
}
/// Describes a modification to a file's metadata
#[derive(Debug, Clone)]
pub enum FileMetadataUpdate {
/// Changes file permissions
Permissions(FileMode, FileMetadataUpdateMode),
}
/// Describes metadata associated with a filesystem entry
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct FileAttr {
/// Data size for files, UNSPECIFIED for the rest
pub size: u64,
/// Entry type
pub ty: FileType,
/// Entry access permissions
pub mode: FileMode,
/// Owner user ID
pub uid: UserId,
/// Owner group ID
pub gid: GroupId,
}
/// Describes a seek operation for a regular file
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SeekFrom {
/// Argument is an offset from the start of the file
Start(u64),
/// Argument is an offset from the end of the file
End(i64),
/// Argument is an offset from the current position
Current(i64),
}
impl SyscallRegister for SeekFrom {
fn into_syscall_register(self) -> usize {
match self {
SeekFrom::Start(value) => (value as usize) << 2,
SeekFrom::End(value) => (value << 2) as usize | 1,
SeekFrom::Current(value) => (value << 2) as usize | 2,
}
}
fn from_syscall_register(value: usize) -> Self {
let dir = value & 0x3;
match dir {
0 => Self::Start((value as u64) >> 2),
1 => Self::End((value as i64) >> 2),
2 => Self::Current((value as i64) >> 2),
_ => unreachable!(),
}
}
}
impl fmt::Display for FileMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
macro_rules! print_bit {
($res:expr, $val:expr, $self:expr, $bit:expr) => {
if $self.contains($bit) {
$res = $val;
}
};
}
let mut buf = ['-'; 9];
print_bit!(buf[0], 'r', self, Self::USER_READ);
print_bit!(buf[1], 'w', self, Self::USER_WRITE);
print_bit!(buf[2], 'x', self, Self::USER_EXEC);
print_bit!(buf[3], 'r', self, Self::GROUP_READ);
print_bit!(buf[4], 'w', self, Self::GROUP_WRITE);
print_bit!(buf[5], 'x', self, Self::GROUP_EXEC);
print_bit!(buf[6], 'r', self, Self::OTHER_READ);
print_bit!(buf[7], 'w', self, Self::OTHER_WRITE);
print_bit!(buf[8], 'x', self, Self::OTHER_EXEC);
for ch in buf.iter() {
fmt::Display::fmt(ch, f)?;
}
Ok(())
}
}

134
lib/abi/src/io/input.rs Normal file
View File

@ -0,0 +1,134 @@
/// Describes a key pressed/released on a keyboard device
// Missing docs: self-explanatory names
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum KeyboardKey {
Char(u8),
F(u8),
Enter,
LShift,
RShift,
LControl,
RControl,
LAlt,
RAlt,
CapsLock,
Escape,
Backspace,
Tab,
Unknown,
}
/// Alternative representation for [KeyboardKey]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct KeyboardKeyCode(u16);
/// Descibes a single event produced by a keyboard device
// Missing docs: self-explanatory names
#[allow(missing_docs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug)]
pub enum KeyboardKeyEvent {
Pressed(KeyboardKey),
Released(KeyboardKey),
}
impl KeyboardKey {
/// Converts [KeyboardKey] to its related [KeyboardKeyCode]
pub const fn code(self) -> KeyboardKeyCode {
// KeyCode:
// 0x00..0x80: chars
// 0x80..0x8C: f1..f12
// 0x90: Enter
// 0x91: LShift
// 0x92: RShift
// 0x93: LControl
// 0x94: RControl
// 0x95: LAlt
// 0x96: RAlt
// 0x97: CapsLock
// 0x98: Escape
// 0x99: Backspace
// 0x9A: Tab
// 0xFF: Unknown
KeyboardKeyCode(match self {
Self::Char(b) => b as u16,
Self::F(b) => (b - 1) as u16 | 0x80,
Self::Enter => 0x90,
Self::LShift => 0x91,
Self::RShift => 0x92,
Self::LControl => 0x93,
Self::RControl => 0x94,
Self::LAlt => 0x95,
Self::RAlt => 0x96,
Self::CapsLock => 0x97,
Self::Escape => 0x98,
Self::Backspace => 0x99,
Self::Tab => 0x9A,
Self::Unknown => 0xFF,
})
}
/// Converts a [KeyboardKeyCode] to the [KeyboardKey] it represents. Unknown keycodes are
/// replaced with [KeyboardKey::Unknown].
pub const fn from_code(KeyboardKeyCode(code): KeyboardKeyCode) -> Self {
if code < 0x80 {
Self::Char(code as u8)
} else if code < 0x8C {
Self::F(code as u8 - 0x79)
} else {
match code {
0x90 => Self::Enter,
0x91 => Self::LShift,
0x92 => Self::RShift,
0x93 => Self::LControl,
0x94 => Self::RControl,
0x95 => Self::LAlt,
0x96 => Self::RAlt,
0x97 => Self::CapsLock,
0x98 => Self::Escape,
0x99 => Self::Backspace,
0x9A => Self::Tab,
_ => Self::Unknown,
}
}
}
}
impl KeyboardKeyEvent {
/// Splits the event into its state and key
#[inline]
pub const fn split(self) -> (KeyboardKey, bool) {
match self {
Self::Pressed(k) => (k, true),
Self::Released(k) => (k, false),
}
}
/// Converts a [KeyEvent] to its byte representation
#[inline]
pub const fn as_bytes(self) -> [u8; 4] {
let (key, pressed) = self.split();
let code = key.code().0.to_ne_bytes();
[code[0], code[1], pressed as u8, 0]
// (((pressed as u32) << 16) | (key.code().0 as u32)).to_ne_bytes()
}
/// Obtains a [KeyEvent] from its byte representation
pub const fn from_bytes(bytes: [u8; 4]) -> Self {
let pressed = bytes[2] != 0;
let code = KeyboardKeyCode(u16::from_ne_bytes([bytes[0], bytes[1]]));
let key = KeyboardKey::from_code(code);
if pressed {
Self::Pressed(key)
} else {
Self::Released(key)
}
}
}

81
lib/abi/src/io/mod.rs Normal file
View File

@ -0,0 +1,81 @@
use crate::util::FixedString;
mod channel;
mod device;
mod file;
mod input;
mod terminal;
pub use crate::generated::{FileMode, FileType, GroupId, OpenOptions, PollControl, RawFd, UserId};
pub use channel::{ChannelPublisherId, MessageDestination, ReceivedMessageMetadata, SentMessage};
pub use device::{DeviceRequest, MountOptions, UnmountOptions};
pub use file::{FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, SeekFrom};
pub use input::{KeyboardKey, KeyboardKeyCode, KeyboardKeyEvent};
pub use terminal::{
TerminalControlCharacters, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
TerminalOutputOptions, TerminalSize,
};
impl UserId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(&self) -> bool {
self.0 == 0
}
}
impl GroupId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(&self) -> bool {
self.0 == 0
}
}
impl FileMode {
pub const fn new(value: u32) -> Self {
Self(value)
}
/// Default mode for a regular file
pub const fn default_file() -> Self {
Self(0o666)
}
/// Default mode for a directory
pub const fn default_dir() -> Self {
Self(0o777)
}
}
/// Raw directory entry representation
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct DirectoryEntry {
/// Name of the entry
pub name: FixedString<256>,
/// Type of the entry
pub ty: FileType,
}
impl RawFd {
pub const STDIN: Self = Self(0);
pub const STDOUT: Self = Self(1);
pub const STDERR: Self = Self(2);
}
impl OpenOptions {
pub const fn default() -> Self {
Self(0)
}
}
impl From<u32> for RawFd {
fn from(value: u32) -> Self {
Self(value)
}
}

140
lib/abi/src/io/terminal.rs Normal file
View File

@ -0,0 +1,140 @@
use crate::bitflags;
bitflags! {
#[doc = "Defines how output to the terminal should be processed"]
#[default = (NL_TO_CRNL)]
pub struct TerminalOutputOptions: u32 {
#[doc = "Translate \n to \r\n"]
const NL_TO_CRNL: bit 0;
}
}
bitflags! {
#[doc = "Defines how input should be presented to the reader"]
#[default = (CR_TO_NL)]
pub struct TerminalInputOptions: u32 {
#[doc = "Translate \n to \r on input"]
const NL_TO_CR: bit 0;
#[doc = "Translate \r to \n on input"]
const CR_TO_NL: bit 1;
}
}
bitflags! {
#[doc = "Controls the line discipline of the terminal"]
#[default = (SIGNAL | CANONICAL | ECHO | ECHO_ERASE | ECHO_KILL | ECHO_NL)]
pub struct TerminalLineOptions: u32 {
#[doc = "Enables signal processing for special bytes (INTR, QUIT, etc.)"]
const SIGNAL: bit 0;
#[doc = "Enables canonical mode"]
const CANONICAL: bit 1;
#[doc = "Echo input characters back"]
const ECHO: bit 2;
#[doc = "If CANONICAL is set, ERASE character erases chars, WORD_ERASE erases words"]
const ECHO_ERASE: bit 3;
#[doc = "If CANONICAL is set, KILL erases lines"]
const ECHO_KILL: bit 4;
#[doc = "If CANONICAL is set, echo newline even if ECHO is not set"]
const ECHO_NL: bit 5;
}
}
/// Specifies a set of special control characters
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct TerminalControlCharacters {
/// End-of-file character (0x04, ^D)
pub eof: u8,
/// Erase character (0x7F, ^?)
pub erase: u8,
/// Word erase character (0x17, ^W)
pub werase: u8,
/// Interrupt character (0x03, ^C)
pub interrupt: u8,
/// Kill character (0x15, ^U)
pub kill: u8,
}
/// Terminal I/O transformation and control settings
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct TerminalOptions {
/// Controls output processing
pub output: TerminalOutputOptions,
/// Controls input processing
pub input: TerminalInputOptions,
/// Controls special bytes and line discipline
pub line: TerminalLineOptions,
/// Specifies control characters of the terminal
pub chars: TerminalControlCharacters,
}
/// Describes terminal size in rows and columns
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct TerminalSize {
/// Number of text rows
pub rows: usize,
/// Number of text columns
pub columns: usize,
}
impl TerminalControlCharacters {
/// const-version of [Default] trait impl
pub const fn const_default() -> Self {
Self {
eof: 0x04,
erase: 0x7F,
interrupt: 0x03,
kill: 0x15,
werase: 0x17,
}
}
}
impl TerminalLineOptions {
/// Returns the line options used for raw tty input
pub const fn raw_input() -> Self {
Self::empty()
}
}
impl TerminalOptions {
/// const-version of [Default] trait impl
pub const fn const_default() -> Self {
Self {
output: TerminalOutputOptions::const_default(),
input: TerminalInputOptions::const_default(),
line: TerminalLineOptions::const_default(),
chars: TerminalControlCharacters::const_default(),
}
}
/// Returns terminal options configured for raw tty input
pub const fn raw_input() -> Self {
Self {
line: TerminalLineOptions::raw_input(),
..Self::const_default()
}
}
/// Returns `true` if CANONICAL flag is set for this terminal
pub const fn is_canonical(&self) -> bool {
self.line.contains(TerminalLineOptions::CANONICAL)
}
/// Returns `true` if the terminal echoes the '\n' character
pub fn is_echo_newline(&self) -> bool {
self.line.contains_any(
TerminalLineOptions::CANONICAL
| TerminalLineOptions::ECHO
| TerminalLineOptions::ECHO_NL,
)
}
}
impl Default for TerminalOptions {
fn default() -> Self {
Self::const_default()
}
}

40
lib/abi/src/lib.rs Normal file
View File

@ -0,0 +1,40 @@
//! Yggdrasil OS user-kernel communication ABI
#![no_std]
#![allow(
clippy::new_without_default,
clippy::should_implement_trait,
clippy::module_inception,
incomplete_features,
stable_features
)]
#![feature(trace_macros, const_trait_impl, inline_const_pat, ip_in_core)]
// TODO temporary
#![allow(missing_docs)]
#[cfg(all(feature = "alloc", not(feature = "rustc-dep-of-std")))]
extern crate alloc;
#[cfg(feature = "rustc-dep-of-std")]
extern crate rustc_std_alloc as alloc;
pub(crate) mod macros;
pub mod pass;
pub mod util;
mod generated {
include!(concat!(env!("OUT_DIR"), "/generated_types.rs"));
}
pub mod error {
pub use crate::generated::Error;
// TODO have syscall-generator implement TryFrom<#repr> for #enum
}
pub use generated::SyscallFunction;
pub mod arch;
pub mod io;
pub mod mem;
pub mod net;
pub mod path;
pub mod process;
pub mod system;

336
lib/abi/src/macros.rs Normal file
View File

@ -0,0 +1,336 @@
/// Helper macro to define primitive enums with integer reprs, as well as their conversion methods
#[macro_export]
macro_rules! primitive_enum {
($(#[$struct_meta:meta])* $vis:vis enum $name:ident: $repr:ty {
$( $(#[$variant_meta:meta])* $variant:ident = $discriminant:literal, )+
}) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr($repr)]
$(#[$struct_meta])*
$vis enum $name {
$(
$(#[$variant_meta])?
$variant = $discriminant
),+
}
impl TryFrom<$repr> for $name {
type Error = ();
fn try_from(v: $repr) -> Result<$name, ()> {
match v {
$(
$discriminant => Ok($name::$variant)
,)+
_ => Err(())
}
}
}
impl From<$name> for $repr {
fn from(v: $name) -> $repr {
v as $repr
}
}
};
}
/// Common implementations for bitflag definitions
#[macro_export]
macro_rules! bitflags_impl_common {
($name:ident, $repr:ty) => {
impl $name {
/// Constructs a value from its bit representation
pub const fn new(bits: $repr) -> Self {
Self(bits)
}
/// Constructs a value with all bits set to zero
pub const fn empty() -> Self {
Self(0)
}
/// Returns the bit representation of the value
#[inline]
pub const fn bits(self) -> $repr {
self.0
}
/// Returns `true` if the value contains all bits in `bits`
#[inline]
pub const fn contains(self, bits: $name) -> bool {
self.0 & bits.bits() == bits.bits()
}
/// Returns `true` if the value contains any of the bits in `bits`
#[inline]
pub const fn contains_any(self, bits: $name) -> bool {
self.0 & bits.bits() != 0
}
}
impl From<$repr> for $name {
fn from(value: $repr) -> $name {
$name::new(value)
}
}
impl From<$name> for $repr {
fn from(value: $name) -> $repr {
value.bits()
}
}
impl core::ops::BitOr for $name {
type Output = $name;
fn bitor(self, rhs: $name) -> Self::Output {
Self::new(self.bits() | rhs.bits())
}
}
impl core::ops::BitAnd for $name {
type Output = $name;
fn bitand(self, rhs: $name) -> Self::Output {
Self::new(self.bits() & rhs.bits())
}
}
impl core::ops::BitOrAssign for $name {
fn bitor_assign(&mut self, rhs: $name) {
self.0 |= rhs.bits();
}
}
impl core::ops::BitAndAssign for $name {
fn bitand_assign(&mut self, rhs: $name) {
self.0 &= rhs.bits();
}
}
impl core::ops::Not for $name {
type Output = $name;
fn not(self) -> Self::Output {
Self::new(!self.bits())
}
}
};
}
/// Implements Default trait for a bitflag definition
#[macro_export]
macro_rules! bitflags_impl_default {
($name:ident, $repr:ty) => {};
($name:ident, $repr:ty, ($($flag:ident)|+)) => {
impl Default for $name {
fn default() -> Self {
$name($($name::$flag.bits())|+)
}
}
impl $name {
/// const-version of [Default] trait impl
pub const fn const_default() -> Self {
Self($($name::$flag.bits())|+)
}
}
};
($name:ident, $repr:ty, ()) => {
impl Default for $name {
fn default() -> Self {
$name(0)
}
}
impl $name {
/// const-version of [Default] trait impl
pub const fn const_default() -> Self {
Self(0)
}
}
};
}
/// Helper macro to construct bit flag wrappers
#[macro_export]
macro_rules! bitflags {
(
$(#[doc = $struct_doc:expr])?
$(#[default = $struct_default:tt])?
$vis:vis struct $name:ident: $repr:ty {
$(
$(#[doc = $field_doc:expr])?
const $field_name:ident: bit $field_offset:expr;
)+
}
) => {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)]
$(#[doc = $struct_doc])?
$vis struct $name($repr);
impl $name {
$(
$(#[doc = $field_doc])?
pub const $field_name: $name = $name(1 << $field_offset);
)+
}
$crate::bitflags_impl_common!($name, $repr);
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut s = f.debug_struct(stringify!($name));
$(
s.field(stringify!($field_name), &self.contains($name::$field_name));
)+
s.finish_non_exhaustive()
}
}
$crate::bitflags_impl_default!($name, $repr$(, $struct_default)?);
};
}
/// Generates a #[repr(transparent)] wrapper for a value with Debug, Copy, Eq, Ord, Hash derived +
/// Into<T> implemented
#[macro_export]
macro_rules! transparent_wrapper {
($(#[$struct_meta:meta])* $vis:vis struct $name:ident ( $ty:ty )) => {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
$(#[$struct_meta])* $vis struct $name ($ty);
impl $name {
/// Constructs the value from its raw representation.
///
/// # Safety
///
/// Unsafe: the actual subset of values represented by this type may not include all
/// the raw values.
pub unsafe fn from_raw(value: $ty) -> $name {
$name(value)
}
}
impl From<$name> for $ty {
fn from(value: $name) -> $ty {
value.0
}
}
};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_getter {
($offset:literal => $getter:ident: $ty:ty) => {
pub fn $getter(&self) -> bool {
unsafe { core::ptr::read_volatile(&self.0) & (1 << $offset) != 0 }
}
};
(($start:literal..$end:literal) => $getter:ident: $ty:ty) => {
pub fn $getter(&self) -> $ty {
(unsafe { core::ptr::read_volatile(&self.0) } >> $start) & ((1 << ($end - $start)) - 1)
}
};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_setter {
($offset:literal => $setter:ident: $ty:ty) => {
pub fn $setter(&mut self, value: bool) -> &mut Self {
// TODO volatile
if value {
self.0 |= 1 << $offset;
} else {
self.0 &= !(1 << $offset);
}
self
}
};
(($start:literal..$end:literal) => $setter:ident: $ty:ty) => {
pub fn $setter(&mut self, value: $ty) -> &mut Self {
const MASK: $ty = ((1 << ($end - $start)) - 1) << $start;
self.0 &= !MASK;
self.0 |= (value << $start) & MASK;
self
}
};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_argument {
($offset:literal => $ty:ty) => {
bool
};
(($start:literal..$end:literal) => $ty:ty) => {
$ty
};
}
#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_constructor_field {
($self:ident $offset:literal => $getter:ident) => {
if $getter {
$self.0 |= 1 << $offset;
}
};
($self:ident ($start:literal..$end:literal) => $getter:ident) => {
$self.0 |= ($getter & ((1 << ($end - $start)) - 1)) << $start;
};
}
/// Helper macro to declare more complex bitfields than [bitflags!] does.
/// TODO: deprecate and remove [bitflags!].
#[macro_export]
macro_rules! define_bitfields {
($vis:vis $name:ident : $ty:ty {
$($range:tt => $getter:ident $(+ $setter:ident)?),* $(,)*
}) => {
#[derive(Clone, Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(transparent)]
$vis struct $name($ty);
impl $name {
pub const fn new(
$( $getter: yggdrasil_abi::define_bitfield_argument!($range => $ty), )*
) -> Self {
let mut this = Self(0);
$(
yggdrasil_abi::define_bitfield_constructor_field!(this $range => $getter);
)*
this
}
pub const fn into_raw(self) -> $ty {
self.0
}
$(
yggdrasil_abi::define_bitfield_getter!($range => $getter:$ty);
$(
yggdrasil_abi::define_bitfield_setter!($range => $setter:$ty);
)?
)*
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(stringify!($name))
$(
.field(stringify!($getter), &self.$getter())
)*
.finish()
}
}
};
}

12
lib/abi/src/mem.rs Normal file
View File

@ -0,0 +1,12 @@
//! Memory-related system functions and types
use crate::io::RawFd;
/// Defines the MapMemory function
#[derive(Clone, Copy, Debug)]
pub enum MappingSource {
/// Mapping is backed by regular memory pages
Anonymous,
/// Mapping is backed by a file
File(RawFd, u64),
}

551
lib/abi/src/net/dns.rs Normal file
View File

@ -0,0 +1,551 @@
//! DNS-related protocol structures
use core::{fmt, mem::size_of};
use crate::{net::Ipv4Addr, util};
use alloc::{string::String, vec, vec::Vec};
use super::IpAddr;
// TODO rewrite using NetValue
// "Wire" format
/// Raw DNS message header
#[derive(Clone, Copy)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct DnsHeader {
/// Transaction ID
pub xid: u16,
/// Information about the message
pub flags: u16,
/// Question count
pub qdcount: u16,
/// Answer count
pub ancount: u16,
/// Nameserver record count
pub nscount: u16,
/// Additional record count
pub arcount: u16,
}
/// DNS query/reply type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[non_exhaustive]
#[repr(transparent)]
pub struct DnsType(u16);
/// DNS query/reply class
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[non_exhaustive]
#[repr(transparent)]
pub struct DnsClass(u16);
/// Describes whether a message is a query or a reply
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[non_exhaustive]
#[repr(transparent)]
pub struct DnsMethod(u16);
/// Describes the reply code of the message
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[non_exhaustive]
#[repr(transparent)]
pub struct DnsReplyCode(u16);
impl DnsType {
/// A - Address (when used with IN class: IPv4)
pub const A: Self = Self(0x0001);
/// OPT - Option data
pub const OPT: Self = Self(0x0029);
}
impl DnsClass {
/// IN - Internet
pub const IN: Self = Self(0x0001);
}
impl DnsMethod {
/// The message is a query
pub const QUERY: Self = Self(0);
/// The message is a reply
pub const REPLY: Self = Self(1);
}
impl DnsReplyCode {
/// Successful status
pub const NO_ERROR: Self = Self(0);
/// Server could not recognize the message properly
pub const FORMAT_ERROR: Self = Self(1);
/// Server experienced an error
pub const SERVER_ERROR: Self = Self(2);
/// The name queried was invalid
pub const NAME_ERROR: Self = Self(3);
/// Feature is not implemented
pub const NOT_IMPLEMENTED: Self = Self(4);
/// The server refused to reply
pub const REFUSED: Self = Self(5);
}
impl fmt::Display for DnsReplyCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match *self {
Self::NO_ERROR => "No error",
Self::FORMAT_ERROR => "Format error",
Self::SERVER_ERROR => "Server error",
Self::NAME_ERROR => "Name error",
Self::NOT_IMPLEMENTED => "Not implemented",
Self::REFUSED => "Refused",
_ => "<Unknown error>",
};
f.write_str(text)
}
}
// "Local" format
/// Describes a single name query
#[derive(Debug)]
pub struct DnsQuery {
/// Name being queried
pub name: DnsName,
/// Type
pub ty: DnsType,
/// Class
pub class: DnsClass,
}
/// Describes an additional DNS option
#[derive(Debug)]
pub enum DnsOption {
/// "Cookie" option to identify queries
Cookie(u64),
}
/// Represents a DNS name: a sequence of dot-separated domain levels if Some or NULL if None
#[derive(Debug)]
pub struct DnsName(pub Option<String>);
/// Describes a DNS additional/nameserver/answer record data
#[derive(Debug)]
pub enum DnsRecordData {
/// A-record: address
A(Ipv4Addr),
/// Option-record: additional options
Option(DnsOption),
}
/// Describes a DNS record
#[derive(Debug)]
pub struct DnsRecord {
/// DNS name this record is related to
pub name: DnsName,
/// Type of the record
pub ty: DnsType,
/// Class of the record
pub class: DnsClass,
/// TTL of the record (0 and ignored for Option records)
pub ttl: u32,
/// Data of the record
pub rdata: DnsRecordData,
}
/// Describes a single DNS query/reply
#[derive(Debug)]
pub struct DnsMessage {
/// Transaction ID
pub xid: u16,
/// Query or reply
pub method: DnsMethod,
/// Reply code
pub reply_code: DnsReplyCode,
/// Question section
pub questions: Vec<DnsQuery>,
/// Answer section
pub answers: Vec<DnsRecord>,
/// Additional section
pub additional: Vec<DnsRecord>,
}
/// Helper trait for an abstract "buffer" which can be written to and extended
pub trait ExtensibleBuffer {
/// Writes a slice of bytes to the buffer
fn write_slice(&mut self, slice: &[u8]);
/// Writes a byte to the buffer
fn write_byte(&mut self, byte: u8);
/// Writes value's bytes to the buffer
#[cfg(feature = "bytemuck")]
fn write_value<T: bytemuck::Pod>(&mut self, value: &T) {
self.write_slice(bytemuck::bytes_of(value))
}
/// Writes value's bytes to the buffer
#[cfg(not(feature = "bytemuck"))]
fn write_value<T>(&mut self, value: &T) {
self.write_slice(util::bytes_of(value))
}
}
/// Helper trait for DNS message parsing/serialization
pub trait DnsSerialize {
/// Serializes the message into the `output` buffer
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V);
}
impl DnsName {
/// "NULL" DNS name: an empty name
pub const NULL: Self = Self(None);
}
impl DnsMessage {
/// Constructs a DNS query message
pub fn query<S: Into<String>>(name: S, ty: DnsType, xid: u16, cookie: u64) -> Self {
Self {
xid,
method: DnsMethod::QUERY,
reply_code: DnsReplyCode::NO_ERROR,
questions: vec![DnsQuery {
name: DnsName(Some(name.into())),
ty,
class: DnsClass::IN,
}],
answers: vec![],
additional: vec![DnsRecord {
name: DnsName::NULL,
ty: DnsType::OPT,
class: DnsClass(1232),
ttl: 0,
rdata: DnsRecordData::Option(DnsOption::Cookie(cookie)),
}],
}
}
/// Constructs a DNS reply message
pub fn reply(
reply_code: DnsReplyCode,
questions: Vec<DnsQuery>,
answers: Vec<(String, IpAddr)>,
xid: u16,
) -> Self {
Self {
xid,
reply_code,
method: DnsMethod::REPLY,
questions,
answers: answers
.into_iter()
.map(|(name, addr)| {
let name = DnsName(Some(name));
let (ty, rdata) = match addr {
IpAddr::V4(addr) => (DnsType::A, DnsRecordData::A(addr)),
_ => todo!(),
};
DnsRecord {
name,
ty,
class: DnsClass::IN,
ttl: 100,
rdata,
}
})
.collect(),
additional: vec![],
}
}
}
impl ExtensibleBuffer for Vec<u8> {
fn write_slice(&mut self, slice: &[u8]) {
self.extend_from_slice(slice);
}
fn write_byte(&mut self, byte: u8) {
self.push(byte);
}
}
mod ser {
use super::*;
impl DnsSerialize for DnsName {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
if let Some(name) = &self.0 {
// Write question
for word in name.split('.') {
if word.is_empty() {
continue;
}
output.write_byte(word.len().try_into().unwrap());
output.write_slice(word.as_bytes());
}
}
// Zero-terminate
output.write_byte(0);
}
}
impl DnsSerialize for DnsOption {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
match self {
&Self::Cookie(value) => {
output.write_slice(&12u16.to_be_bytes()); // RDLENGTH
output.write_slice(&10u16.to_be_bytes()); // Option = COOKIE
output.write_slice(&8u16.to_be_bytes()); // Cookie length
output.write_slice(&value.to_be_bytes()); // Cookie data
}
}
}
}
impl DnsSerialize for DnsRecordData {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
match self {
&Self::A(addr) => {
output.write_slice(&4u16.to_be_bytes());
output.write_slice(&u32::from(addr).to_be_bytes());
}
Self::Option(option) => option.serialize(output),
}
}
}
impl DnsSerialize for DnsRecord {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
self.name.serialize(output);
output.write_slice(&self.ty.0.to_be_bytes());
output.write_slice(&self.class.0.to_be_bytes());
output.write_slice(&self.ttl.to_be_bytes());
self.rdata.serialize(output);
}
}
impl DnsSerialize for DnsQuery {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
self.name.serialize(output);
output.write_slice(&self.ty.0.to_be_bytes());
output.write_slice(&self.class.0.to_be_bytes());
}
}
impl DnsSerialize for DnsMessage {
fn serialize<V: ExtensibleBuffer>(&self, output: &mut V) {
let qdcount: u16 = self.questions.len().try_into().unwrap();
let ancount: u16 = self.answers.len().try_into().unwrap();
let arcount: u16 = self.additional.len().try_into().unwrap();
let header = DnsHeader {
xid: self.xid.to_be(),
flags: ((self.method.0 << 15) | (self.reply_code.0 & 0xF) | (1 << 8)).to_be(),
qdcount: qdcount.to_be(),
nscount: 0,
ancount: ancount.to_be(),
arcount: arcount.to_be(),
};
output.write_slice(util::bytes_of(&header));
// Serialize data sections
for question in self.questions.iter() {
question.serialize(output);
}
for answer in self.answers.iter() {
answer.serialize(output);
}
for additional in self.additional.iter() {
additional.serialize(output);
}
}
}
}
#[allow(missing_docs)]
mod de {
use super::*;
impl DnsName {
pub fn parse(message: &[u8], input: &[u8]) -> Option<(Self, usize)> {
let mut pos = 0;
let mut value = String::new();
loop {
if input.len() < pos + 1 {
return None;
}
let len = input[pos];
pos += 1;
if len == 0 {
break;
}
match len & 0b11000000 {
0 => {
let len = len as usize;
if input.len() < pos + len {
return None;
}
let word = core::str::from_utf8(&input[pos..pos + len]).ok()?;
value.push_str(word);
value.push('.');
pos += len;
}
0b11000000 if len == 0b11000000 => {
// TODO pointer
if input.len() < pos + 1 {
return None;
}
let off = input[pos] as usize;
if input.len() < off {
return None;
}
let (rest, _) = Self::parse(message, &message[off..])?;
return Some((rest, pos + 1));
}
_ => return None,
}
}
if value.is_empty() {
Some((Self::NULL, pos))
} else {
Some((Self(Some(value)), pos))
}
}
}
impl DnsRecordData {
pub fn parse(
ty: DnsType,
class: DnsClass,
_message: &[u8],
input: &[u8],
) -> Option<(Option<Self>, usize)> {
if input.len() < size_of::<u16>() {
return None;
}
let rdlength = u16::from_be_bytes([input[0], input[1]]) as usize;
if input.len() < size_of::<u16>() + rdlength {
return None;
}
let value = match (ty, class) {
(DnsType::A, DnsClass::IN) if rdlength == 4 => {
let value = u32::from_be_bytes([input[2], input[3], input[4], input[5]]);
Some(Self::A(value.into()))
}
(DnsType::OPT, _) => {
todo!();
}
(_, _) => None,
};
Some((value, rdlength + size_of::<u16>()))
}
}
impl DnsRecord {
pub fn parse(message: &[u8], input: &[u8]) -> Option<(Option<Self>, usize)> {
if input.len() < 1 + 4 * size_of::<u16>() {
return None;
}
let (name, len) = DnsName::parse(message, input)?;
if input.len() < len + 4 * size_of::<u16>() {
return None;
}
let ty = DnsType(u16::from_be_bytes([input[len], input[len + 1]]));
let class = DnsClass(u16::from_be_bytes([input[len + 2], input[len + 3]]));
let ttl = u32::from_be_bytes([
input[len + 4],
input[len + 5],
input[len + 6],
input[len + 7],
]);
let (rdata, rdata_len) = DnsRecordData::parse(ty, class, message, &input[len + 8..])?;
let value = rdata.map(|v| Self {
name,
ty,
class,
ttl,
rdata: v,
});
Some((value, rdata_len + len + 4 * size_of::<u16>()))
}
}
impl DnsQuery {
pub fn parse(message: &[u8], input: &[u8]) -> Option<(Self, usize)> {
if input.len() < 1 + 2 * size_of::<u16>() {
return None;
}
let (name, len) = DnsName::parse(message, input)?;
if input.len() < len + 2 * size_of::<u16>() {
return None;
}
let ty = DnsType(u16::from_be_bytes([input[len], input[len + 1]]));
let class = DnsClass(u16::from_be_bytes([input[len + 2], input[len + 3]]));
Some((Self { name, ty, class }, len + 2 * size_of::<u16>()))
}
}
impl DnsMessage {
pub fn parse(input: &[u8]) -> Option<Self> {
if input.len() < size_of::<DnsHeader>() {
return None;
}
let header: &DnsHeader = util::from_bytes(&input[..size_of::<DnsHeader>()]);
let method = DnsMethod((u16::from_be(header.flags) >> 15) & 1);
let qdcount = u16::from_be(header.qdcount);
let ancount = u16::from_be(header.ancount);
let reply_code = if method == DnsMethod::REPLY {
let value = u16::from_be(header.flags) & 0xF;
if value > 5 {
return None;
}
DnsReplyCode(value)
} else {
DnsReplyCode::NO_ERROR
};
// TODO don't ignore arcount/nscount
let mut questions = vec![];
let mut answers = vec![];
let mut offset = size_of::<DnsHeader>();
for _ in 0..qdcount {
let (question, len) = DnsQuery::parse(input, &input[offset..])?;
questions.push(question);
offset += len;
}
for _ in 0..ancount {
let (answer, len) = DnsRecord::parse(input, &input[offset..])?;
if let Some(answer) = answer {
answers.push(answer);
}
offset += len;
}
Some(Self {
xid: u16::from_be(header.xid),
method,
reply_code,
questions,
answers,
additional: vec![],
})
}
}
}

60
lib/abi/src/net/mod.rs Normal file
View File

@ -0,0 +1,60 @@
//! Defines data types for network operations
#[cfg(any(feature = "alloc", feature = "rustc_std_alloc"))]
pub mod dns;
#[cfg(feature = "alloc")]
pub mod netconfig;
pub mod protocols;
pub mod types;
pub use crate::generated::SocketType;
pub use types::{
ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr},
socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6},
subnet_addr::{SubnetAddr, SubnetV4Addr},
MacAddress,
};
// primitive_enum! {
// #[doc = "Defines a type of a socket"]
// pub enum SocketType: u32 {
// }
// }
/// Describes a method to query an interface
#[derive(Clone, Debug)]
pub enum SocketInterfaceQuery<'a> {
/// Query by name
ByName(&'a str),
/// Query by id
ById(u32),
}
/// Describes a socket operation parameter
#[derive(Clone, Debug)]
pub enum SocketOption<'a> {
/// Whether the socket is broadcast
Broadcast(bool),
/// Time-to-Live parameter
Ttl(u32),
/// Bind the socket to a specific interface
BindInterface(SocketInterfaceQuery<'a>),
/// Unbind the socket from an interface
UnbindInterface,
/// (Read-only) Hardware address of the bound interface
BoundHardwareAddress(MacAddress),
}
impl<'a> From<&'a str> for SocketInterfaceQuery<'a> {
fn from(value: &'a str) -> Self {
Self::ByName(value)
}
}
impl From<u32> for SocketInterfaceQuery<'_> {
fn from(value: u32) -> Self {
Self::ById(value)
}
}

View File

@ -0,0 +1,126 @@
//! Network configuration service messages
use crate::alloc::boxed::Box;
use super::{IpAddr, MacAddress, SubnetAddr};
/// Describes a single route
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RouteInfo {
/// Interface textual name (unique)
pub interface_name: Box<str>,
/// Interface unique ID number
pub interface_id: u32,
/// Subnetwork this route is related to
pub subnet: SubnetAddr,
/// Gateway this route should be reached through
pub gateway: Option<IpAddr>,
}
/// Describes routing information about some address
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RoutingInfo {
/// Interface textual name (unique)
pub interface_name: Box<str>,
/// Interface unique ID number
pub interface_id: u32,
/// Gateway this route should be reached through
pub gateway: Option<IpAddr>,
/// Real destination which should be used
pub destination: IpAddr,
/// IP address used as a source when sending through this route
pub source: Option<IpAddr>,
/// Interface MAC address
pub source_mac: MacAddress,
}
/// Describes a single interface
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InterfaceInfo {
/// Interface textual name (unique)
pub interface_name: Box<str>,
/// Interface unique ID number
pub interface_id: u32,
/// Interface IP address
pub address: Option<IpAddr>,
/// Interface hardware address
pub mac: MacAddress,
}
/// Describes a query to some interface either by its name or ID
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InterfaceQuery {
/// Query by ID number
ById(u32),
/// Query by name
ByName(Box<str>),
}
/// kernel-netconf interface request
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NetConfigRequest {
/// List all routes
ListRoutes,
/// List all interfaces
ListInterfaces,
/// Describe routes related to a single interface
DescribeRoutes(InterfaceQuery),
/// Describe a single interface
DescribeInterface(InterfaceQuery),
/// Add a route
AddRoute {
/// Interface to which the action applies
interface: InterfaceQuery,
/// Optional gateway to reach the route through
gateway: Option<IpAddr>,
/// Subnetwork of the route
subnet: SubnetAddr,
},
/// Set interface's IP address
SetNetworkAddress {
/// Interface to which the action applies
interface: InterfaceQuery,
/// Address to set
address: IpAddr,
},
/// Clear interface's IP address
ClearNetworkAddress(InterfaceQuery),
/// Query a route and return routing information
QueryRoute(IpAddr),
/// Query the MAC address of the specified IP
QueryArp(u32, IpAddr, bool),
}
/// Wrapper to provide error status in replies
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NetConfigResult<T> {
/// Success
Ok(T),
/// Error
Err(Box<str>),
}
impl<T> NetConfigResult<T> {
/// Constructs an error value
pub fn err<S: Into<Box<str>>>(message: S) -> Self {
Self::Err(message.into())
}
}
impl From<&str> for InterfaceQuery {
fn from(value: &str) -> Self {
Self::ByName(Box::from(value))
}
}
impl From<u32> for InterfaceQuery {
fn from(value: u32) -> Self {
Self::ById(value)
}
}

View File

@ -0,0 +1,236 @@
// TODO don't really want to document this now
#![allow(missing_docs)]
use core::{fmt, mem::size_of};
use crate::bitflags;
use super::{
types::{net_value::WrappedValue, NetValue, NetValueImpl},
MacAddress,
};
/////////// Layer 2 protocols ////////////
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C)]
pub struct EthernetFrame {
pub destination_mac: MacAddress,
pub source_mac: MacAddress,
pub ethertype: NetValue<EtherType>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)]
pub struct EtherType(u16);
impl EtherType {
pub const ARP: Self = Self(0x0806);
pub const IPV4: Self = Self(0x0800);
}
impl WrappedValue for EtherType {
type Inner = u16;
fn into_inner(self) -> u16 {
self.0
}
fn from_inner(value: u16) -> Self {
Self(value)
}
}
/////////// Layer 3 protocols ////////////
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct ArpFrame {
pub hardware_type: NetValue<u16>,
pub protocol: NetValue<EtherType>,
pub hardware_size: u8,
pub protocol_size: u8,
pub opcode: NetValue<u16>,
pub sender_mac: MacAddress,
pub sender_ip: NetValue<u32>,
pub target_mac: MacAddress,
// TODO handle IPv6
pub target_ip: NetValue<u32>,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct Ipv4Frame {
pub version_length: u8,
pub dscp_flags: u8,
pub total_length: NetValue<u16>,
pub id: NetValue<u16>,
pub flags_frag: NetValue<u16>,
pub ttl: u8,
pub protocol: IpProtocol,
pub header_checksum: NetValue<u16>,
pub source_address: NetValue<u32>,
pub destination_address: NetValue<u32>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)]
pub struct IpProtocol(pub u8);
impl IpProtocol {
pub const ICMP: Self = Self(1);
pub const TCP: Self = Self(6);
pub const UDP: Self = Self(17);
}
/////////// Layer 4 protocols ////////////
#[derive(Clone, Copy)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct UdpFrame {
pub source_port: NetValue<u16>,
pub destination_port: NetValue<u16>,
pub length: NetValue<u16>,
pub checksum: NetValue<u16>,
}
bitflags! {
pub struct TcpFlags: u8 {
const FIN: bit 0;
const SYN: bit 1;
const RST: bit 2;
const PSH: bit 3;
const ACK: bit 4;
const URG: bit 5;
const ECE: bit 6;
const CWR: bit 7;
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct TcpFrame {
pub source_port: NetValue<u16>,
pub destination_port: NetValue<u16>,
pub sequence_number: NetValue<u32>,
pub acknowledge_number: NetValue<u32>,
pub data_offset: u8,
pub flags: TcpFlags,
pub window_size: NetValue<u16>,
pub checksum: NetValue<u16>,
pub urgent_pointer: NetValue<u16>,
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct TcpV4PseudoHeader {
pub source_address: NetValue<u32>,
pub destination_address: NetValue<u32>,
pub _zero: u8,
pub protocol: IpProtocol,
pub tcp_length: NetValue<u16>,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(C, packed)]
pub struct IcmpV4Frame {
pub ty: u8,
pub code: u8,
pub checksum: NetValue<u16>,
pub rest: NetValue<u32>,
}
/////////// Helper utilities /////////////
pub struct InetChecksum {
state: u32,
}
///////////// Implementations ////////////
/////////// Layer 3 protocols ////////////
impl fmt::Display for IpProtocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::ICMP => f.write_str("ICMP"),
Self::UDP => f.write_str("UDP"),
Self::TCP => f.write_str("TCP"),
_ => f.write_str("<unknown-l4-proto>"),
}
}
}
impl Ipv4Frame {
pub fn header_length(&self) -> usize {
core::cmp::max(
(self.version_length & 0xF) << 2,
size_of::<Ipv4Frame>() as u8,
) as usize
}
pub fn total_length(&self) -> usize {
u16::from_network_order(self.total_length) as usize
}
}
/////////// Layer 4 protocols ////////////
impl UdpFrame {
pub fn data_length(&self) -> usize {
(u16::from_network_order(self.length) as usize).saturating_sub(size_of::<UdpFrame>())
}
}
impl TcpFrame {
pub fn data_offset(&self) -> usize {
((self.data_offset >> 4) as usize * size_of::<u32>()).max(size_of::<TcpFrame>())
}
}
/////////// Helper utilities /////////////
impl InetChecksum {
pub fn new() -> Self {
Self { state: 0 }
}
#[cfg(feature = "bytemuck")]
pub fn add_value<T: bytemuck::Pod>(&mut self, value: &T, reorder: bool) {
self.add_bytes(bytemuck::bytes_of(value), reorder)
}
pub fn add_bytes(&mut self, bytes: &[u8], reorder: bool) {
let len = bytes.len();
for i in 0..len / 2 {
let word = if reorder {
((bytes[i * 2] as u16) << 8) | (bytes[i * 2 + 1] as u16)
} else {
(bytes[i * 2] as u16) | ((bytes[i * 2 + 1] as u16) << 8)
};
self.state = self.state.wrapping_add(word as u32);
}
if len % 2 != 0 {
let word = if reorder {
(bytes[len - 1] as u16) << 8
} else {
todo!()
};
self.state = self.state.wrapping_add(word as u32);
}
}
pub fn finish(self) -> u16 {
let sum = (self.state >> 16) + (self.state & 0xFFFF);
(!(sum & 0xFFFF)) as u16
}
}

View File

@ -0,0 +1,200 @@
//! IP-address related types, see [core::net::IpAddr]
use core::{fmt, str::FromStr};
use crate::error::Error;
/// Describes an IPv4 address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Ipv4Addr([u8; 4]);
/// Describes an IPv6 address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Ipv6Addr([u8; 16]);
/// Describes an IPv4/IPv6 address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum IpAddr {
/// IPv4 address
V4(Ipv4Addr),
/// IPv6 address
V6(Ipv6Addr),
}
// IPv4
impl Ipv4Addr {
/// An unspecified IPv4 address: 0.0.0.0
pub const UNSPECIFIED: Self = Self([0, 0, 0, 0]);
/// A broadcast IPv4 address: 255.255.255.255
pub const BROADCAST: Self = Self([255, 255, 255, 255]);
/// A loopback IPv4 address: 127.0.0.1
pub const LOOPBACK: Self = Self([127, 0, 0, 1]);
/// Constructs a new IPv4 address from its octets
pub fn new(x: u8, y: u8, z: u8, w: u8) -> Self {
Self([x, y, z, w])
}
}
impl FromStr for Ipv4Addr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let c = core::net::Ipv4Addr::from_str(s).map_err(|_| Error::InvalidArgument)?;
Ok(Self(c.octets()))
}
}
impl From<u32> for Ipv4Addr {
fn from(value: u32) -> Self {
Self(value.to_be_bytes())
}
}
impl From<Ipv4Addr> for u32 {
fn from(value: Ipv4Addr) -> Self {
u32::from_be_bytes(value.0)
}
}
impl fmt::Display for Ipv4Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&core::net::Ipv4Addr::from(self.0), f)
}
}
impl From<core::net::Ipv4Addr> for Ipv4Addr {
fn from(value: core::net::Ipv4Addr) -> Self {
Self(value.octets())
}
}
impl From<Ipv4Addr> for core::net::Ipv4Addr {
fn from(value: Ipv4Addr) -> Self {
core::net::Ipv4Addr::from(value.0)
}
}
// IPv6
impl FromStr for Ipv6Addr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let c = core::net::Ipv6Addr::from_str(s).map_err(|_| Error::InvalidArgument)?;
Ok(Self(c.octets()))
}
}
impl From<[u8; 16]> for Ipv6Addr {
#[inline]
fn from(value: [u8; 16]) -> Self {
Self(value)
}
}
impl From<Ipv6Addr> for [u8; 16] {
#[inline]
fn from(value: Ipv6Addr) -> Self {
value.0
}
}
impl fmt::Display for Ipv6Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&core::net::Ipv6Addr::from(self.0), f)
}
}
impl From<core::net::Ipv6Addr> for Ipv6Addr {
#[inline]
fn from(value: core::net::Ipv6Addr) -> Self {
Self(value.octets())
}
}
impl From<Ipv6Addr> for core::net::Ipv6Addr {
#[inline]
fn from(value: Ipv6Addr) -> Self {
core::net::Ipv6Addr::from(value.0)
}
}
// IP
impl IpAddr {
/// Returns `true` if the address is IPv4
pub fn is_ipv4(&self) -> bool {
matches!(self, Self::V4(_))
}
/// Returns `true` if the address is IPv6
pub fn is_ipv6(&self) -> bool {
matches!(self, Self::V6(_))
}
/// Returns [Some] if this is an IPv4 address, [None] otherwise
pub fn into_ipv4(self) -> Option<Ipv4Addr> {
match self {
Self::V4(address) => Some(address),
_ => None,
}
}
}
impl fmt::Display for IpAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V4(addr) => fmt::Display::fmt(addr, f),
Self::V6(addr) => fmt::Display::fmt(addr, f),
}
}
}
impl FromStr for IpAddr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let c = core::net::IpAddr::from_str(s).map_err(|_| Error::InvalidArgument)?;
Ok(Self::from(c))
}
}
impl From<IpAddr> for core::net::IpAddr {
#[inline]
fn from(value: IpAddr) -> Self {
match value {
IpAddr::V4(address) => core::net::IpAddr::V4(address.into()),
IpAddr::V6(address) => core::net::IpAddr::V6(address.into()),
}
}
}
impl From<core::net::IpAddr> for IpAddr {
#[inline]
fn from(value: core::net::IpAddr) -> Self {
match value {
core::net::IpAddr::V4(address) => Self::V4(address.into()),
core::net::IpAddr::V6(address) => Self::V6(address.into()),
}
}
}
impl From<Ipv4Addr> for IpAddr {
fn from(value: Ipv4Addr) -> Self {
Self::V4(value)
}
}
impl From<Ipv6Addr> for IpAddr {
fn from(value: Ipv6Addr) -> Self {
Self::V6(value)
}
}

View File

@ -0,0 +1,49 @@
//! Various network types
use core::fmt;
pub mod ip_addr;
pub mod net_value;
pub mod socket_addr;
pub mod subnet_addr;
pub use net_value::{NetValue, NetValueImpl};
/// Describes a hardware MAC address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct MacAddress([u8; 6]);
impl MacAddress {
/// A broadcast MAC address
pub const BROADCAST: Self = Self([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
/// An unspecified MAC address
pub const UNSPECIFIED: Self = Self([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
impl From<[u8; 6]> for MacAddress {
fn from(value: [u8; 6]) -> Self {
Self(value)
}
}
impl From<MacAddress> for [u8; 6] {
fn from(value: MacAddress) -> Self {
value.0
}
}
impl fmt::Display for MacAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, octet) in self.0.into_iter().enumerate() {
if i != 0 {
write!(f, ":")?;
}
write!(f, "{:02X}", octet)?;
}
Ok(())
}
}

View File

@ -0,0 +1,110 @@
//! Network value handling utilities
use core::fmt;
macro_rules! wrap_primitives {
($($ty:ty),+) => {
$(impl NetValueImpl for $ty {
#[inline]
fn from_network_order(value: NetValue<Self>) -> Self {
Self::from_be(value.0)
}
#[inline]
fn to_network_order(self) -> NetValue<Self> {
NetValue(self.to_be())
}
})+
};
}
/// Interface for converting from/to network-format values
#[cfg(not(feature = "bytemuck"))]
pub trait NetValueImpl: Copy + Eq {
/// Performs conversion from network to host format
fn from_network_order(value: NetValue<Self>) -> Self;
/// Performs conversion from host format to network
fn to_network_order(self) -> NetValue<Self>;
}
/// Interface for converting from/to network-format values
#[cfg(feature = "bytemuck")]
pub trait NetValueImpl: Copy + Eq + bytemuck::Pod + bytemuck::Zeroable {
/// Performs conversion from network to host format
fn from_network_order(value: NetValue<Self>) -> Self;
/// Performs conversion from host format to network
fn to_network_order(self) -> NetValue<Self>;
}
/// Wrapper type to ensure network values are handled properly
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)]
pub struct NetValue<T: NetValueImpl>(T);
/// Helper type for primitive values wrapped in some 1-struct
#[cfg(not(feature = "bytemuck"))]
pub trait WrappedValue: Copy + Eq {
/// Wrapped type
type Inner: NetValueImpl;
/// Converts from self to wrapped type
fn into_inner(self) -> Self::Inner;
/// Converts from wrapped type to self
fn from_inner(value: Self::Inner) -> Self;
}
/// Helper type for primitive values wrapped in some 1-struct
#[cfg(feature = "bytemuck")]
pub trait WrappedValue: Copy + Eq + bytemuck::Pod + bytemuck::Zeroable {
/// Wrapped type
type Inner: NetValueImpl;
/// Converts from self to wrapped type
fn into_inner(self) -> Self::Inner;
/// Converts from wrapped type to self
fn from_inner(value: Self::Inner) -> Self;
}
impl<T: NetValueImpl> NetValue<T> {
/// Creates a [NetValue] from a value that's already in network order
#[inline]
pub fn from_network_order(value: T) -> Self {
Self(value)
}
}
impl<T: WrappedValue> NetValue<T> {
/// "Extracts" a wrapped value from [NetValue]
#[inline]
pub fn into_inner(self) -> NetValue<T::Inner> {
NetValue(self.0.into_inner())
}
/// Converts a wrapped value into its "shell" [NetValue] type
#[inline]
pub fn from_inner(value: NetValue<T::Inner>) -> NetValue<T> {
NetValue(T::from_inner(value.0))
}
}
impl<T: NetValueImpl + fmt::Debug> fmt::Debug for NetValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = T::from_network_order(*self);
fmt::Debug::fmt(&value, f)
}
}
impl<T: WrappedValue> NetValueImpl for T {
#[inline]
fn from_network_order(value: NetValue<T>) -> T {
let inner = <T::Inner>::from_network_order(value.into_inner());
T::from_inner(inner)
}
#[inline]
fn to_network_order(self) -> NetValue<T> {
NetValue::from_inner(self.into_inner().to_network_order())
}
}
wrap_primitives!(u16, u32, u64);

View File

@ -0,0 +1,177 @@
//! Socket address types
use core::fmt;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
/// Describes an IPv4 socket address: 1.2.3.4:443
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct SocketAddrV4(Ipv4Addr, u16);
/// Describes an IPv6 socket address: [::]:443
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct SocketAddrV6(Ipv6Addr, u16);
/// Describes a socket address
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SocketAddr {
/// IPv4 socket address
V4(SocketAddrV4),
/// IPv6 socket address
V6(SocketAddrV6),
}
// V4
impl SocketAddrV4 {
/// "Null" IPv4 address used when sender/recepient is not known or cannot be represented
pub const NULL: Self = Self::new(Ipv4Addr::UNSPECIFIED, 0);
/// Constructs an IPv4 socket address from its IP and port components
pub const fn new(address: Ipv4Addr, port: u16) -> Self {
Self(address, port)
}
/// Returns the IP address part of the value
pub const fn ip(&self) -> Ipv4Addr {
self.0
}
/// Returns the port part of the value
pub const fn port(&self) -> u16 {
self.1
}
}
impl fmt::Display for SocketAddrV4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.0, self.1)
}
}
impl From<core::net::SocketAddrV4> for SocketAddrV4 {
#[inline]
fn from(value: core::net::SocketAddrV4) -> Self {
SocketAddrV4::new((*value.ip()).into(), value.port())
}
}
impl From<SocketAddrV4> for core::net::SocketAddrV4 {
#[inline]
fn from(value: SocketAddrV4) -> Self {
core::net::SocketAddrV4::new(value.ip().into(), value.port())
}
}
// V6
impl SocketAddrV6 {
/// Constructs an IPv6 socket address from its IP and port components
pub const fn new(address: Ipv6Addr, port: u16) -> Self {
Self(address, port)
}
/// Returns the IP address part of the value
pub const fn ip(&self) -> Ipv6Addr {
self.0
}
/// Returns the port part of the value
pub const fn port(&self) -> u16 {
self.1
}
}
impl fmt::Display for SocketAddrV6 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.0, self.1)
}
}
impl From<core::net::SocketAddrV6> for SocketAddrV6 {
#[inline]
fn from(value: core::net::SocketAddrV6) -> Self {
SocketAddrV6::new((*value.ip()).into(), value.port())
}
}
impl From<SocketAddrV6> for core::net::SocketAddrV6 {
#[inline]
fn from(value: SocketAddrV6) -> Self {
core::net::SocketAddrV6::new(value.ip().into(), value.port(), 0, 0)
}
}
// SocketAddr
impl SocketAddr {
/// "Null" IPv4 address used when sender/recepient is not known or cannot be represented
pub const NULL_V4: Self = Self::V4(SocketAddrV4::NULL);
/// Constructs a socket address from its IP and port components
pub const fn new(address: IpAddr, port: u16) -> Self {
match address {
IpAddr::V4(address) => Self::V4(SocketAddrV4::new(address, port)),
IpAddr::V6(address) => Self::V6(SocketAddrV6::new(address, port)),
}
}
/// Returns the IP address part of the value
pub const fn ip(&self) -> IpAddr {
match self {
Self::V4(address) => IpAddr::V4(address.ip()),
Self::V6(address) => IpAddr::V6(address.ip()),
}
}
/// Returns [Some] if this is an IPv4 address, [None] otherwise
pub fn into_ipv4(self) -> Option<SocketAddrV4> {
match self {
Self::V4(address) => Some(address),
Self::V6(_) => None,
}
}
/// Returns the port part of the value
pub const fn port(&self) -> u16 {
match self {
Self::V4(address) => address.port(),
Self::V6(address) => address.port(),
}
}
}
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V4(address) => fmt::Display::fmt(address, f),
Self::V6(address) => fmt::Display::fmt(address, f),
}
}
}
impl From<SocketAddr> for core::net::SocketAddr {
#[inline]
fn from(value: SocketAddr) -> Self {
match value {
SocketAddr::V4(address) => core::net::SocketAddr::V4(address.into()),
SocketAddr::V6(address) => core::net::SocketAddr::V6(address.into()),
}
}
}
impl From<core::net::SocketAddr> for SocketAddr {
#[inline]
fn from(value: core::net::SocketAddr) -> Self {
match value {
core::net::SocketAddr::V4(address) => SocketAddr::V4(address.into()),
core::net::SocketAddr::V6(address) => SocketAddr::V6(address.into()),
}
}
}

View File

@ -0,0 +1,122 @@
//! Subnetwork address types
use core::{fmt, str::FromStr};
use crate::{
error::Error,
net::{IpAddr, Ipv4Addr},
};
/// Describes an IPv4 subnetwork, e.g. 11.0.0.0/24
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct SubnetV4Addr {
/// "Root" of the subnetwork, e.g. the "11.0.0.0" part in "11.0.0.0/24"
pub root: Ipv4Addr,
/// Number of masked bits, e.g. the "/24" part in "11.0.0.0/24"
pub mask_bits: u32,
mask: u32,
}
/// Describes an IPv4/IPv6 subnetwork
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SubnetAddr {
/// IPv4 subnetwork
V4(SubnetV4Addr),
}
// SubnetV4
impl SubnetV4Addr {
/// "Unspecified" subnetwork address: 0.0.0.0/0
pub const UNSPECIFIED: Self = Self {
root: Ipv4Addr::UNSPECIFIED,
mask_bits: 0,
mask: 0,
};
/// Constructs a subnetwork from its components
pub fn from_address_mask(address: Ipv4Addr, mask_bits: u8) -> Self {
let root = u32::from(address);
let mask = Self::make_mask(mask_bits);
let root = root & Self::make_root_mask(mask_bits);
Self {
root: Ipv4Addr::from(root),
mask_bits: mask_bits as _,
mask,
}
}
/// Returns `true` if the subnet contains given address
pub fn contains(&self, address: &Ipv4Addr) -> bool {
let root = u32::from(self.root);
let address = u32::from(*address);
(address & self.mask) == root
}
fn make_root_mask(bits: u8) -> u32 {
!((1 << (32 - bits)) - 1)
}
fn make_mask(bits: u8) -> u32 {
((1 << bits) - 1) << (32 - bits)
}
}
impl fmt::Display for SubnetV4Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.root, self.mask_bits)
}
}
impl FromStr for SubnetV4Addr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut it = s.split('/');
let network = it.next().ok_or(Error::InvalidArgument)?;
let mask_bits = it.next().ok_or(Error::InvalidArgument)?;
if it.next().is_some() {
return Err(Error::InvalidArgument);
}
let network = Ipv4Addr::from_str(network).map_err(|_| Error::InvalidArgument)?;
let mask_bits = u8::from_str(mask_bits).map_err(|_| Error::InvalidArgument)?;
Ok(Self::from_address_mask(network, mask_bits))
}
}
// Subnet
impl SubnetAddr {
/// Returns `true` if the subnet contains given address
pub fn contains(&self, address: &IpAddr) -> bool {
match (self, address) {
(Self::V4(subnet), IpAddr::V4(addr)) => subnet.contains(addr),
(_, _) => false,
}
}
}
impl fmt::Display for SubnetAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::V4(subnet) => fmt::Display::fmt(subnet, f),
}
}
}
impl FromStr for SubnetAddr {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(v4) = SubnetV4Addr::from_str(s) {
return Ok(Self::V4(v4));
}
Err(Error::InvalidArgument)
}
}

116
lib/abi/src/pass.rs Normal file
View File

@ -0,0 +1,116 @@
//! Interfaces for passing stuff between the kernel and the userspace
use core::ptr::NonNull;
use crate::error::Error;
/// Interface for a "writer", producing objects "owned" by the userspace.
///
/// # Safety
///
/// The trait is marked unsafe as it is the implementor's concern that the produced pointers can
/// actually hold the requested values and are properly aligned for that.
pub unsafe trait Placer {
/// Converts a kernel-space reference into a pointer that can be safely dereferenced in
/// userspace
fn place_ref<T: Place>(&mut self, r: &T) -> Result<NonNull<T::Output>, Error>;
/// Converts a kernel-space slice reference into a slice pointer, elements of which are placed
/// in safely-accessible memory by userspace
fn place_slice<T: Place>(&mut self, r: &[T]) -> Result<NonNull<[T::Output]>, Error>;
}
/// Interface for "transferring ownership" of an object to userspace.
///
/// # Safety
///
/// The trait is marked unsafe because it may produce arbitrary pointer, so it's the implementor's
/// concern that they are actually correct.
pub unsafe trait Place {
/// Conversion result type, must be safely accessible from userspace
type Output: 'static;
/// Creates a userspace-reachable variant of the internal value
fn place<P: Placer>(&self, placer: &mut P) -> Result<Self::Output, Error>;
/// Converts the internal value using [Place::place] and returns a safe userspace reference
fn place_ref<P: Placer>(&self, placer: &mut P) -> Result<&'static Self::Output, Error>
where
Self: Sized,
{
let ptr = placer.place_ref(self)?;
unsafe { Ok(ptr.as_ref()) }
}
}
macro_rules! impl_primitive {
($($ty:ty),+) => {
$(
unsafe impl $crate::pass::Place for $ty {
type Output = $ty;
fn place<P: $crate::pass::Placer>(&self, _placer: &mut P) -> Result<$ty, Error> {
Ok(*self)
}
}
)+
};
}
impl_primitive!(u8, u16, u32, u64, i8, i16, i32, i64, bool, char);
unsafe impl<'s> Place for &'s str {
type Output = &'static str;
fn place<P: Placer>(&self, placer: &mut P) -> Result<Self::Output, Error> {
let data = placer.place_slice(self.as_bytes())?;
// Safety: safe, object passed in was already a proper &str
Ok(unsafe { core::str::from_utf8_unchecked(data.as_ref()) })
}
}
unsafe impl<T: Place> Place for [T] {
type Output = &'static [T::Output];
fn place<P: Placer>(&self, placer: &mut P) -> Result<Self::Output, Error> {
let data = placer.place_slice(self)?;
Ok(unsafe { data.as_ref() })
}
}
/// Automatically implements the [Place] interface for a struct, properly handling any nested
/// references
#[macro_export]
macro_rules! impl_place_lifetime {
(
$( #[ $struct_meta:meta ] )*
$struct_vis:vis struct $struct_name:ident<$struct_lifetime:lifetime> {
$(
$( #[ $field_meta:meta ] )*
$field_vis:vis $field_name:ident: $field_ty:ty,
)*
}
) => {
$( #[$struct_meta] )*
$struct_vis struct $struct_name<$struct_lifetime> {
$(
$( #[$field_meta] )*
$field_vis $field_name: $field_ty
),*
}
unsafe impl<$struct_lifetime> $crate::pass::Place for $struct_name<$struct_lifetime> {
type Output = $struct_name<'static>;
fn place<P: $crate::pass::Placer>(&self, placer: &mut P)
-> Result<Self::Output, $crate::error::Error> {
$(
let $field_name = self.$field_name.place(placer)?;
)*
Ok($struct_name {
$($field_name),*
})
}
}
};
}

11
lib/abi/src/path/mod.rs Normal file
View File

@ -0,0 +1,11 @@
//! Filesystem path definitions and manipulation functions
#![allow(missing_docs)]
mod path;
#[cfg(feature = "alloc")]
mod path_buf;
pub use path::Path;
#[cfg(feature = "alloc")]
pub use path_buf::PathBuf;

119
lib/abi/src/path/path.rs Normal file
View File

@ -0,0 +1,119 @@
use core::ffi::CStr;
use crate::error::Error;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Path(str);
impl Path {
pub const SEPARATOR: char = '/';
pub const SEPARATOR_STR: &'static str = "/";
/// Parent directory path element
pub const PARENT_NAME: &'static str = "..";
/// Path element that refers to itself
pub const SELF_NAME: &'static str = ".";
pub fn from_str(s: &str) -> &Self {
unsafe { core::mem::transmute(s) }
}
pub fn from_c_str(s: &CStr) -> Result<&Self, Error> {
s.to_str()
.map_err(|_| Error::InvalidArgument)
.map(Path::from_str)
}
pub fn empty() -> &'static Self {
Self::from_str("")
}
pub fn trim_start_separators(&self) -> &Self {
Self::from_str(self.0.trim_start_matches(Self::SEPARATOR))
}
pub fn trim_end_separators(&self) -> &Self {
Self::from_str(self.0.trim_end_matches(Self::SEPARATOR))
}
pub fn display(&self) -> &str {
&self.0
}
pub fn split_right(&self) -> (&Path, &str) {
if let Some((left, right)) = self
.0
.trim_end_matches(Self::SEPARATOR)
.rsplit_once(Self::SEPARATOR)
{
(
Path::from_str(left.trim_end_matches(Self::SEPARATOR)),
right,
)
} else {
(Path::empty(), &self.0)
}
}
pub fn split_left(&self) -> (&str, &Path) {
if let Some((left, right)) = self.0.split_once(Self::SEPARATOR) {
(
left,
Path::from_str(right.trim_start_matches(Self::SEPARATOR)),
)
} else {
(&self.0, Path::empty())
}
}
#[inline]
pub fn is_absolute(&self) -> bool {
self.0.starts_with(Self::SEPARATOR)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.0.as_ptr()
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl AsRef<str> for Path {
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<Path> for Path {
fn as_ref(&self) -> &Path {
self
}
}
impl AsRef<Path> for str {
fn as_ref(&self) -> &Path {
Path::from_str(self)
}
}
#[cfg(feature = "alloc")]
impl AsRef<Path> for alloc::string::String {
fn as_ref(&self) -> &Path {
self.as_str().as_ref()
}
}

View File

@ -0,0 +1,71 @@
use crate::path::Path;
use alloc::{
borrow::{Borrow, ToOwned},
string::String,
};
use core::ops::Deref;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PathBuf(String);
impl PathBuf {
pub fn new() -> Self {
Self(String::new())
}
pub fn from_str(s: &str) -> Self {
Self(String::from(s))
}
pub fn push<P: AsRef<Path>>(&mut self, _path: P) {
todo!()
}
pub fn join<P: AsRef<Path>>(&mut self, _path: P) -> Self {
todo!()
}
pub fn display(&self) -> &str {
self.0.as_str()
}
}
impl Deref for PathBuf {
type Target = Path;
fn deref(&self) -> &Self::Target {
Path::from_str(self.0.as_str())
}
}
impl AsRef<Path> for PathBuf {
fn as_ref(&self) -> &Path {
Path::from_str(self.0.as_str())
}
}
impl From<&Path> for PathBuf {
fn from(value: &Path) -> Self {
Self::from_str(value.as_ref())
}
}
impl From<&str> for PathBuf {
fn from(value: &str) -> Self {
Self::from_str(value)
}
}
impl ToOwned for Path {
type Owned = PathBuf;
fn to_owned(&self) -> Self::Owned {
PathBuf::from(self)
}
}
impl Borrow<Path> for PathBuf {
fn borrow(&self) -> &Path {
Path::from_str(self.0.as_str())
}
}

View File

@ -0,0 +1,63 @@
use core::num::NonZeroI32;
use abi_lib::SyscallRegister;
use super::signal::Signal;
/// Code signalled by a process when it finishes or is terminated by a signal
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum ExitCode {
/// Process exited normally, either with an error or with a 0
Exited(i32),
/// Process was killed by a signal
BySignal(Signal),
}
impl ExitCode {
/// Returned when a process has exited successfully
pub const SUCCESS: Self = Self::Exited(0);
/// Returns `true` if this ExitCode signals a successful exit
pub const fn is_success(self) -> bool {
matches!(self, Self::Exited(0))
}
/// Returns [Some] if the process exited with an error or was killed by a signal
pub fn as_failure(self) -> Option<NonZeroI32> {
match self {
Self::Exited(0) => None,
Self::Exited(n) => Some(unsafe { NonZeroI32::new_unchecked(n) }),
Self::BySignal(sig) => {
Some(unsafe { NonZeroI32::new_unchecked(u32::from(sig) as i32) })
}
}
}
}
impl From<i32> for ExitCode {
fn from(value: i32) -> Self {
match value {
10000.. => Self::BySignal(Signal::try_from((value - 10000) as u32).unwrap()),
0..=512 => Self::Exited(value - 255),
_ => unreachable!(),
}
}
}
impl SyscallRegister for ExitCode {
fn into_syscall_register(self) -> usize {
match self {
Self::Exited(value) => (value + 255) as usize,
Self::BySignal(value) => value.into_syscall_register() + 10000,
}
}
fn from_syscall_register(value: usize) -> Self {
match value {
10000.. => Self::BySignal(Signal::from_syscall_register(value - 10000)),
0..=512 => Self::Exited(value as i32 - 255),
_ => unreachable!(),
}
}
}

View File

@ -0,0 +1,55 @@
//! Data structures for process management
use core::{fmt, time::Duration};
use crate::{impl_place_lifetime, io::FileMode};
mod exit;
mod signal;
mod spawn;
pub use crate::generated::{ProcessId, ThreadId};
pub use exit::ExitCode;
pub use signal::{Signal, SignalEntryData};
pub use spawn::{ExecveOptions, SpawnOption, SpawnOptions, ThreadSpawnOptions};
/// Describes a single mutex operation
#[derive(Clone, Debug)]
#[repr(C)]
pub enum MutexOperation {
/// Waits on the mutex object until it is different from "compare value"
Wait(u32, Option<Duration>),
/// Wakes a single mutex-waiting thread
Wake,
/// Wakes all threads waiting on the mutex
WakeAll,
}
/// Provides some amount of process-related information
#[derive(Clone, Debug)]
pub enum ProcessInfoElement {
/// Mask applied to file modes when creating new files/directories
Umask(FileMode),
}
// TODO not sure if I really need #[repr(C)] ABI here
impl_place_lifetime! {
#[doc = "Argument struct passed from the kernel to a spawned process"]
#[derive(Debug)]
pub struct ProgramArgumentInner<'a> {
#[doc = "Argument list"]
pub args: &'a [&'a str],
#[doc = "List of KEY=VALUE environment variable pairs"]
pub env: &'a [&'a str],
}
}
impl ProcessId {
pub const ZERO: Self = Self(0);
}
impl fmt::Display for ProcessId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<Process {}>", self.0)
}
}

View File

@ -0,0 +1,13 @@
use crate::arch::SavedFrame;
pub use crate::generated::Signal;
/// Data provided by the kernel to signal entry function
#[derive(Clone, Debug)]
#[repr(C)]
pub struct SignalEntryData {
/// Which signal was issued
pub signal: Signal,
/// Saved frame of the context that was interrupted
pub frame: SavedFrame,
}

View File

@ -0,0 +1,56 @@
use crate::io::RawFd;
use super::ProcessId;
/// Defines an optional argument for controlling process creation
#[derive(Debug)]
pub enum SpawnOption {
/// Indicates a new process should inherit a file descriptor from its creator
InheritFile {
/// FD on the creator side
source: RawFd,
/// What FD number should be used in the child
child: RawFd,
},
/// The new process should be placed in the specified group
SetProcessGroup(ProcessId),
/// Gain terminal control for the given FD
GainTerminal(RawFd),
}
/// Controls how processes are created
#[derive(Clone, Debug)]
#[repr(C)]
pub struct SpawnOptions<'a> {
/// Creator-relative path to the binary to execute
pub program: &'a str,
/// Arguments to the child process
pub arguments: &'a [&'a str],
/// Environment for the child process
pub environment: &'a [&'a str],
/// Optional arguments to specify details of the creation
pub optional: &'a [SpawnOption],
}
/// Legacy execve(2) arguments
#[allow(missing_docs)]
#[derive(Clone, Debug)]
#[repr(C)]
pub struct ExecveOptions<'a> {
pub program: &'a str,
pub arguments: &'a [&'a str],
pub environment: &'a [&'a str],
}
/// Controls how threads are spawned within a process
#[derive(Clone, Debug)]
#[repr(C)]
pub struct ThreadSpawnOptions {
/// Thread entry function
pub entry: extern "C" fn(u64) -> !,
/// Thread argument value
pub argument: u64,
/// Thread stack pointer. NOTE: it's the caller's responsibility to allocate a proper stack for
/// the thread.
pub stack_top: usize,
}

136
lib/abi/src/syscall.rs Normal file
View File

@ -0,0 +1,136 @@
//! System call function definitions
use crate::primitive_enum;
primitive_enum!(
#[doc = "Describes a system call issued to the kernel"]
pub enum SyscallFunction: usize {
#[doc = "Suspend the caller task for some time"]
Nanosleep = 1,
#[doc = "Map a virtual memory region"]
MapMemory = 2,
#[doc = "Release a virtual memory region"]
UnmapMemory = 3,
#[doc = "Write data to a file descriptor"]
Write = 4,
#[doc = "Read data from a file descriptor"]
Read = 5,
#[doc = "Open a file"]
Open = 6,
#[doc = "Close a file descriptor"]
Close = 7,
#[doc = "Open a directory for reading"]
OpenDirectory = 8,
#[doc = "Read entries from a directory descriptor"]
ReadDirectory = 9,
#[doc = "Create a directory"]
CreateDirectory = 10,
#[doc = "Remove a file"]
Remove = 11,
#[doc = "Remove a directory"]
RemoveDirectory = 12,
#[doc = "Retrieves metadata for a filesystem entry"]
GetMetadata = 13,
#[doc = "Seek a file descriptor to the specified position"]
Seek = 14,
#[doc = "Perform a device-specific request"]
DeviceRequest = 15,
#[doc = "Create an anonymous pipe"]
CreatePipe = 16,
#[doc = "Create a file descriptor poll channel"]
CreatePoll = 17,
#[doc = "Add/remove/modify poll channel entries of interest"]
PollControl = 18,
#[doc = "Poll a channel for descriptor readiness"]
PollWait = 19,
#[doc = "Create or get an existing messaging channel"]
OpenChannel = 20,
#[doc = "Receive a message from channel subscription"]
ReceiveMessage = 21,
#[doc = "Send a message to a channel"]
SendMessage = 22,
#[doc = "Creates a buffer of shared memory and returns a FD to it"]
CreateSharedMemory = 23,
#[doc = "Creates a pair of pseudo-terminal master/slave"]
CreatePty = 24,
#[doc = "Clones a file descriptor"]
CloneFd = 25,
#[doc = "Changes file metadata"]
UpdateMetadata = 26,
#[doc = "Creates a event-generator file descriptor"]
CreateTimer = 27,
#[doc = "Create and start a new process"]
SpawnProcess = 40,
#[doc = "Wait for a process to exit"]
WaitProcess = 41,
#[doc = "Get the caller process ID"]
GetPid = 42,
#[doc = "Get the caller process session ID"]
GetSessionId = 43,
#[doc = "Get the caller process group ID"]
GetProcessGroupId = 44,
#[doc = "Put the caller process into given group ID"]
SetProcessGroupId = 45,
#[doc = "Start a new session and set the caller process as its leader"]
StartSession = 46,
#[doc = "Create and start a new thread within the caller's process"]
SpawnThread = 47,
#[doc = "Mutex operation: wait, wake, etc."]
Mutex = 48,
#[doc = "Send a signal to a process"]
SendSignal = 50,
#[doc = "Setup a signal entry for the caller process"]
SetSignalEntry = 51,
#[doc = "Exit from a syscall handler"]
ExitSignal = 52,
#[doc = "Terminate the caller thread"]
ExitThread = 53,
#[doc = "Terminate the caller process"]
ExitProcess = 54,
// C compat
#[doc = "Fork a process, creating an identical copy of its address space (C compat)"]
Fork = 55,
#[doc = "Replace the process address space with a new loaded program"]
Exec = 56,
#[doc = "Get information about the calling process"]
GetProcessInfo = 57,
#[doc = "Update information about the calling process"]
SetProcessInfo = 58,
#[doc = "Mount a filesystem"]
Mount = 101,
#[doc = "Unmount a filesystem"]
Unmount = 102,
#[doc = "Print a message to the kernel's debug trace output"]
DebugTrace = 128,
#[doc = "Fetches bytes of random data"]
GetRandom = 129,
#[doc = "Retrieves system information"]
GetSystemInfo = 130,
#[doc = "Bind a socket for listening"]
BindSocket = 256,
#[doc = "Connect a socket to a remote host"]
ConnectSocket = 257,
#[doc = "Send a message to a socket"]
SendTo = 258,
#[doc = "Receive a message from a socket"]
ReceiveFrom = 259,
#[doc = "Get socket option value"]
GetSocketOption = 260,
#[doc = "Update socket option value"]
SetSocketOption = 261,
#[doc = "Accept an incoming stream connection"]
Accept = 262,
}
);

21
lib/abi/src/system.rs Normal file
View File

@ -0,0 +1,21 @@
//! System-related data structures
/// Describes system memory usage information
#[derive(Clone, Debug, Default)]
pub struct SystemMemoryStats {
/// Total number of pages in the system
pub total_usable_pages: usize,
/// Amount of used/allocated memory
pub allocated_pages: usize,
/// Amount of usable/free memory
pub free_pages: usize,
/// Single page size
pub page_size: usize,
}
/// Describes a piece of system information
#[derive(Clone, Debug)]
pub enum SystemInfo {
/// Memory usage stats
MemoryStats(SystemMemoryStats),
}

95
lib/abi/src/util.rs Normal file
View File

@ -0,0 +1,95 @@
//! Misc utilities for the ABI crate
use crate::error::Error;
use core::{fmt, str::FromStr};
/// Helper struct to hold a fixed-size string of UTF-8 characters
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct FixedString<const N: usize> {
data: [u8; N],
len: usize,
}
impl<const N: usize> FixedString<N> {
/// Returns an empty [FixedString]
pub const fn empty() -> Self {
Self {
data: [0; N],
len: 0,
}
}
/// Returns the length of the string
pub const fn len(&self) -> usize {
self.len
}
/// Returns `true` if the string is empty
pub const fn is_empty(&self) -> bool {
self.len == 0
}
/// Returns the underlying byte data of the string
pub fn as_bytes(&self) -> &[u8] {
&self.data[..self.len]
}
}
impl<const N: usize> fmt::Debug for FixedString<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_ref(), f)
}
}
impl<const N: usize> AsRef<str> for FixedString<N> {
fn as_ref(&self) -> &str {
// Safety: totally safe, the FixedString can only be constructed from a &str or be empty
unsafe { core::str::from_utf8_unchecked(&self.data[..self.len]) }
}
}
impl<const N: usize> FromStr for FixedString<N> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() >= N {
return Err(Error::InvalidArgument);
}
let len = s.len();
let mut data = [0; N];
data[..len].copy_from_slice(s.as_bytes());
Ok(Self { data, len })
}
}
impl<const N: usize> PartialEq<&str> for FixedString<N> {
fn eq(&self, other: &&str) -> bool {
&self.data[..self.len] == other.as_bytes()
}
}
/// Reinterprets a `T` structure as a slice of its bytes
#[cfg(feature = "bytemuck")]
pub fn bytes_of<T: bytemuck::Pod>(data: &T) -> &[u8] {
bytemuck::bytes_of(data)
}
/// Reinterprets a slice of bytes as a `T` structure reference
#[cfg(feature = "bytemuck")]
pub fn from_bytes<T: bytemuck::Pod>(bytes: &[u8]) -> &T {
bytemuck::from_bytes(bytes)
}
/// Reinterprets a `T` structure as a slice of its bytes
#[cfg(not(feature = "bytemuck"))]
pub fn bytes_of<T>(data: &T) -> &[u8] {
unsafe { core::slice::from_raw_parts(data as *const T as *const u8, core::mem::size_of::<T>()) }
}
/// Reinterprets a slice of bytes as a `T` structure reference
#[cfg(not(feature = "bytemuck"))]
pub fn from_bytes<T>(bytes: &[u8]) -> &T {
// TODO align check
assert_eq!(bytes.len(), core::mem::size_of::<T>());
unsafe { &*(&bytes[0] as *const u8 as *const T) }
}