diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 7497933c..2f242404 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -317,6 +317,26 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "pc-keyboard" version = "0.8.0" @@ -536,6 +556,8 @@ dependencies = [ "lazy_static", "limine", "log", + "num-derive", + "num-traits", "pc-keyboard", "ps2", "rand", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 52d9527d..70ccf171 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -7,34 +7,42 @@ edition = "2021" # Treat warnings as a build error. strict = [] - [dependencies] +arrayvec = { version = "0.7.6", default-features = false } +async-trait = "0.1.86" bitflags = { version = "2.8", default-features = false } bytes = { version = "1.10.0", default-features = false } -lazy_static = { version = "1.5.0", features = ["spin_no_std"] } -limine = "0.3.1" -raw-cpuid = "11.3.0" -spin = "0.9.8" -uart_16550 = "0.3.2" -x86_64 = "0.15.2" -talc = "4.4.2" -goblin = { version = "0.9.3", default-features = false, features = [ +crossbeam-queue = { version = "0.3.12", default-features = false, features = [ "alloc", - "elf64", - "elf32", - "endian_fd", ] } -rand = { version = "0.8.3", features = ["small_rng"], default-features = false } +fontdue = { version = "0.9.3", default-features = false, features = [ + "hashbrown", +] } futures = { version = "0.3", default-features = false, features = [ "alloc", "async-await", ] } -crossbeam-queue = { version = "0.3.12", default-features = false, features = [ +futures-util = { version = "0.3.31", default-features = false, features = [ "alloc", + "async-await", + "async-await-macro", + "futures-macro", ] } -arrayvec = { version = "0.7.6", default-features = false } +goblin = { version = "0.9.3", default-features = false, features = [ + "alloc", + "elf64", + "elf32", + "endian_fd", +] } +lazy_static = { version = "1.5.0", features = ["spin_no_std"] } +limine = "0.3.1" log = { version = "0.4.25", default-features = false } -async-trait = "0.1.86" +num-derive = { version = "0.4", default-features = false } +num-traits = { version = "0.2", default-features = false } +pc-keyboard = "0.8.0" +ps2 = "0.2.0" +rand = { version = "0.8.3", features = ["small_rng"], default-features = false } +raw-cpuid = "11.3.0" smoltcp = { version = "0.10.0", default-features = false, features = [ "alloc", "proto-ipv4", @@ -44,16 +52,10 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ "socket-icmp", "socket-dns", "socket-udp", + "socket-tcp", ] } -fontdue = { version = "0.9.3", default-features = false, features = [ - "hashbrown", -] } +spin = "0.9.8" +talc = "4.4.2" +uart_16550 = "0.3.2" +x86_64 = "0.15.2" zerocopy = { version = "0.8", features = ["derive"] } -pc-keyboard = "0.8.0" -futures-util = { version = "0.3.31", default-features = false, features = [ - "alloc", - "async-await", - "async-await-macro", - "futures-macro", -] } -ps2 = "0.2.0" diff --git a/kernel/limage_config.toml b/kernel/limage_config.toml index c971a2d8..491413bc 100644 --- a/kernel/limage_config.toml +++ b/kernel/limage_config.toml @@ -49,7 +49,7 @@ gdb-terminal = { args = ["-nographic", "-s", "-S"] } gdb-gui = { args = ["-s", "-S"] } [test] -timeout_secs = 30 +timeout_secs = 60 success_exit_code = 33 no_reboot = true extra_args = [ diff --git a/kernel/src/constants/syscalls.rs b/kernel/src/constants/syscalls.rs index a73c032c..cd0d317d 100644 --- a/kernel/src/constants/syscalls.rs +++ b/kernel/src/constants/syscalls.rs @@ -8,6 +8,9 @@ pub const SYSCALL_MPROTECT: u32 = 10; pub const SYSCALL_MUNMAP: u32 = 11; pub const SYSCALL_FORK: u32 = 5; pub const SYSCALL_WAIT: u32 = 6; +pub const SYSCALL_SOCKET: u32 = 41; +pub const SYSCALL_BIND: u32 = 49; +pub const SYSCALL_CONNECT: u32 = 42; // Mmap pub const START_MMAP_ADDRESS: u64 = 0x0900_0000_0000; diff --git a/kernel/src/filesys/ext2/filesystem.rs b/kernel/src/filesys/ext2/filesystem.rs index 6ee15517..50ed464f 100644 --- a/kernel/src/filesys/ext2/filesystem.rs +++ b/kernel/src/filesys/ext2/filesystem.rs @@ -29,6 +29,8 @@ pub enum FilesystemError { InvalidPath, CacheError, InvalidFd, + /// The process has used all of its file descriptors + NoFd, } pub type FilesystemResult = Result; diff --git a/kernel/src/filesys/mod.rs b/kernel/src/filesys/mod.rs index ee081996..80b49426 100644 --- a/kernel/src/filesys/mod.rs +++ b/kernel/src/filesys/mod.rs @@ -12,7 +12,7 @@ use crate::{ paging::map_kernel_frame, KERNEL_MAPPER, }, - processes::process::with_current_pcb, + processes::process::{with_current_pcb, FakeFile}, }; use alloc::{ boxed::Box, @@ -21,7 +21,10 @@ use alloc::{ sync::Arc, vec::Vec, }; -use core::sync::atomic::{AtomicBool, Ordering}; +use core::{ + cmp, + sync::atomic::{AtomicBool, Ordering}, +}; use ext2::{ filesystem::{Ext2, FilesystemError, FilesystemResult}, node::{DirEntry, NodeError}, @@ -221,7 +224,17 @@ pub fn get_file(fd: usize) -> FilesystemResult>> { if file.is_none() { return Err(FilesystemError::InvalidFd); } - Ok(file.unwrap()) + let file = file.unwrap().clone(); + + // let file_guard = file.lock(); + if let FakeFile::File(f) = file { + return Ok(f); + } + // match file_guard { + // FakeFile::File(f) => {return Ok(Arc::new(Mutex::new(f)))} + // _ => Err(FilesystemError::InvalidFd) + // } + Err(FilesystemError::InvalidFd) } /// Gets the file descriptor from the file path @@ -231,9 +244,11 @@ pub fn get_fd(filepath: &str) -> FilesystemResult { with_current_pcb(|pcb| { for (fd, file_opt) in pcb.fd_table.iter().enumerate() { if let Some(file_arc) = file_opt { - let file = file_arc.lock(); - if file.pathname == filepath { - return Ok(fd); + if let FakeFile::File(f) = file_arc.clone() { + let file_guard = f.lock(); + if file_guard.pathname == filepath { + return Ok(fd); + } } } } @@ -320,22 +335,24 @@ impl FileSystem for Ext2Wrapper { self.filesystem.lock().get_node(path).await?.number() }; - let fd = with_current_pcb(|pcb| { - let mut next_fd_guard = pcb.next_fd.lock(); - let fd = *next_fd_guard; - *next_fd_guard += 1; - + let fd: Option = with_current_pcb(|pcb| { + let fd = pcb.find_next_fd()?; let file = File::new(path.to_string(), fd, 0, flags, inode_number); - pcb.fd_table[fd] = Some(Arc::new(Mutex::new(file))); + pcb.fd_table[fd] = Some(FakeFile::File(Arc::new(Mutex::new(file)))); - fd + Option::Some(fd) }); - self.refcount - .lock() - .entry(inode_number) - .and_modify(|v| *v += 1) - .or_insert(1); - Ok(fd) + match fd { + Option::None => Err(FilesystemError::NoFd), + Option::Some(file) => { + self.refcount + .lock() + .entry(inode_number) + .and_modify(|v| *v += 1) + .or_insert(1); + Ok(file) + } + } } async fn remove(&mut self, fd: usize) -> FilesystemResult<()> { @@ -498,7 +515,6 @@ impl FileSystem for Ext2Wrapper { let mut remaining = buf.len(); let mut total_read = 0; let mut file_pos = locked_file.position; - while remaining > 0 { let page_offset = file_pos & !(PAGE_SIZE - 1); let page_offset_in_buf = file_pos % PAGE_SIZE; @@ -596,7 +612,11 @@ impl FileSystem for Ext2Wrapper { // Do raw pointer write *after* .await to avoid Send violation unsafe { let buf_ptr = kernel_va.as_mut_ptr(); - core::ptr::copy_nonoverlapping(file_buf.as_ptr(), buf_ptr, file_buf.len()); + core::ptr::copy_nonoverlapping( + file_buf.as_ptr(), + buf_ptr, + cmp::min(PAGE_SIZE, file_buf.len()), + ); } file_mappings.insert(offset, Page::containing_address(kernel_va)); @@ -704,7 +724,7 @@ mod tests { .open_file("./temp/test.txt", OpenFlags::O_WRONLY | OpenFlags::O_CREAT) .await .unwrap(); - assert!(active_fd_count() > 0); + assert!(active_fd_count() == 4); user_fs.write_file(fd, b"Test 123").await.unwrap(); user_fs.seek_file(fd, 0).await.unwrap(); @@ -715,7 +735,7 @@ mod tests { assert_eq!(&buf[..8], b"Test 123"); user_fs.close_file(fd).await.unwrap(); - assert_eq!(active_fd_count(), 0); + assert_eq!(active_fd_count(), 3); } #[test_case] @@ -780,7 +800,7 @@ mod tests { { let meta = user_fs.metadata(fd).await.unwrap(); assert_eq!(meta.pathname, "./temp/meta.txt"); - assert_eq!(meta.fd, 0); + assert_eq!(meta.fd, 3); assert_eq!(meta.flags, OpenFlags::O_WRONLY | OpenFlags::O_CREAT); } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index ed3ebd75..d6561e8b 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,5 +1,6 @@ #![feature(naked_functions_rustic_abi)] #![feature(abi_x86_interrupt)] +#![feature(btree_cursors)] #![cfg_attr(feature = "strict", deny(warnings))] #![no_std] #![cfg_attr(test, no_main)] diff --git a/kernel/src/memory/page_fault.rs b/kernel/src/memory/page_fault.rs index a1ffb04c..c9124f8a 100644 --- a/kernel/src/memory/page_fault.rs +++ b/kernel/src/memory/page_fault.rs @@ -21,7 +21,7 @@ use crate::{ paging::{create_mapping, create_mapping_to_frame, update_mapping, update_permissions}, HHDM_OFFSET, KERNEL_MAPPER, }, - processes::process::with_current_pcb, + processes::process::{with_current_pcb, FakeFile}, }; use super::mm::{VmArea, VmAreaBackings, VmaChain}; @@ -282,26 +282,31 @@ pub async fn handle_shared_file_mapping( .expect("could not get fd from fd table") }); - let file_guard = { file.lock() }; - let absent_in_page_cache = fs - .page_cache_get_mapping(file_guard.clone(), offset as usize) - .await - .is_err(); - if absent_in_page_cache { - fs.add_entry_to_page_cache(file_guard.clone(), offset as usize) + if let FakeFile::File(file_arc) = file { + let file_guard = file_arc.lock(); + let absent_in_page_cache = fs + .page_cache_get_mapping(file_guard.clone(), offset as usize) .await - .expect("failed to add entry to page cache"); - } + .is_err(); + if absent_in_page_cache { + fs.add_entry_to_page_cache(file_guard.clone(), offset as usize) + .await + .expect("failed to add entry to page cache"); + } - let kernel_va = fs - .page_cache_get_mapping(file_guard.clone(), offset as usize) - .await - .unwrap(); - let kernel_mapper = { KERNEL_MAPPER.lock() }; - let frame: PhysFrame = kernel_mapper - .translate_page(Page::containing_address(kernel_va)) - .expect("Could not translate kernel VA."); - create_mapping_to_frame(page, mapper, Some(flags), frame); + let kernel_va = fs + .page_cache_get_mapping(file_guard.clone(), offset as usize) + .await + .unwrap(); + let kernel_mapper = { KERNEL_MAPPER.lock() }; + let frame: PhysFrame = kernel_mapper + .translate_page(Page::containing_address(kernel_va)) + .expect("Could not translate kernel VA."); + create_mapping_to_frame(page, mapper, Some(flags), frame); + } else { + // TODO: fix this to return a error to the user (probally just kill it) + panic!("Tried to mmap a socket!") + } } /// Handles a page fault for a file that should not be shared by adding an entry into the page @@ -340,26 +345,28 @@ pub async fn handle_private_file_mapping( .expect("could not get fd from fd table") }); - let file_guard = { file.lock() }; - let absent_in_page_cache = fs - .page_cache_get_mapping(file_guard.clone(), offset as usize) - .await - .is_err(); - if absent_in_page_cache { - fs.add_entry_to_page_cache(file_guard.clone(), offset as usize) + if let FakeFile::File(file_arc) = file { + let file_guard = file_arc.lock(); + let absent_in_page_cache = fs + .page_cache_get_mapping(file_guard.clone(), offset as usize) .await - .expect("failed to add entry to page cache"); - } + .is_err(); + if absent_in_page_cache { + fs.add_entry_to_page_cache(file_guard.clone(), offset as usize) + .await + .expect("failed to add entry to page cache"); + } - let kernel_va = fs - .page_cache_get_mapping(file_guard.clone(), offset as usize) - .await - .unwrap(); - let kernel_mapper = { KERNEL_MAPPER.lock() }; - let frame: PhysFrame = kernel_mapper - .translate_page(Page::containing_address(kernel_va)) - .expect("Could not translate kernel VA."); - create_mapping_to_frame(page, mapper, Some(flags), frame); + let kernel_va = fs + .page_cache_get_mapping(file_guard.clone(), offset as usize) + .await + .unwrap(); + let kernel_mapper = { KERNEL_MAPPER.lock() }; + let frame: PhysFrame = kernel_mapper + .translate_page(Page::containing_address(kernel_va)) + .expect("Could not translate kernel VA."); + create_mapping_to_frame(page, mapper, Some(flags), frame); + } } /// Handles a fault by creating a new mapping and inserting it into the backing. diff --git a/kernel/src/net/mod.rs b/kernel/src/net/mod.rs index d64bda4f..16dea177 100644 --- a/kernel/src/net/mod.rs +++ b/kernel/src/net/mod.rs @@ -1,16 +1,25 @@ -use alloc::vec; +use core::sync::atomic::{AtomicBool, AtomicU16}; + +use alloc::{collections::btree_set::BTreeSet, sync::Arc, vec}; +use lazy_static::lazy_static; use smoltcp::{ iface::{Interface, SocketHandle, SocketSet}, socket::dhcpv4, time::Instant, wire::{EthernetAddress, IpCidr, Ipv4Cidr}, }; -use spin::Mutex; +use spin::{Mutex, RwLock}; use crate::{debug, debug_println, devices::xhci::ecm::ECMDevice}; - +const EPHEMERAL_PORTS_START: u16 = 49152; +static NEXT_EPHEMERAL_PORT: AtomicU16 = AtomicU16::new(EPHEMERAL_PORTS_START); +static EPHEMERAL_PORTS_OVERFLOW: AtomicBool = AtomicBool::new(false); pub static INTERFACE: Mutex> = Mutex::new(Option::None); +lazy_static! { + pub static ref PORTS_SET: Arc>> = Arc::new(RwLock::new(BTreeSet::new())); +} + #[derive(Debug)] pub enum NetError { NoInterface, @@ -20,10 +29,10 @@ pub enum NetError { Deconfigured, } -pub struct DeviceInterface<'a> { +pub struct DeviceInterface { device: ECMDevice, - interface: Interface, - sockets: SocketSet<'a>, + pub interface: Interface, + pub sockets: SocketSet<'static>, dhcp_handle: Option, } @@ -31,10 +40,13 @@ pub fn set_interface(mut device: ECMDevice, hardware_address: EthernetAddress) { let config = smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ethernet(hardware_address)); let interface = smoltcp::iface::Interface::new(config, &mut device, Instant::ZERO); + // Can use 'static because we are using owned buffers for sockets + // see https://docs.rs/smoltcp/latest/smoltcp/iface/struct.SocketSet.html + let sockets: SocketSet<'static> = SocketSet::new(vec![]); let new_interface = DeviceInterface { device, interface, - sockets: SocketSet::new(vec![]), + sockets, dhcp_handle: Option::None, }; @@ -42,6 +54,25 @@ pub fn set_interface(mut device: ECMDevice, hardware_address: EthernetAddress) { *old_interface = Option::Some(new_interface); } +/// Get a reference to the global network interface +pub fn get() -> Option>> { + let guard = INTERFACE.lock(); + if guard.is_some() { + Some(guard) + } else { + None + } +} + +pub fn with_interface(f: F) -> Option +where + F: FnOnce(&mut DeviceInterface) -> R, +{ + let mut guard = get()?; + let interface = guard.as_mut()?; + Some(f(interface)) +} + /// Sends a dhcp request for an ip address. pub fn get_ip_addr() -> Result<(), NetError> { let mut interface = INTERFACE.lock(); @@ -115,3 +146,15 @@ fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) { addrs.push(IpCidr::Ipv4(cidr)).unwrap(); }); } + +pub fn get_eph_port() -> Option { + if EPHEMERAL_PORTS_OVERFLOW.load(core::sync::atomic::Ordering::SeqCst) { + return Option::None; + } + let potential_result = NEXT_EPHEMERAL_PORT.fetch_add(1, core::sync::atomic::Ordering::SeqCst); + if potential_result < EPHEMERAL_PORTS_START { + EPHEMERAL_PORTS_OVERFLOW.store(true, core::sync::atomic::Ordering::SeqCst); + return Option::None; + } + Option::Some(potential_result) +} diff --git a/kernel/src/processes/mod.rs b/kernel/src/processes/mod.rs index 83ab3be9..70b03ac3 100644 --- a/kernel/src/processes/mod.rs +++ b/kernel/src/processes/mod.rs @@ -12,6 +12,7 @@ pub fn init(cpu_id: u32) { #[cfg(test)] mod tests { + use crate::{ constants::processes::TEST_SIMPLE_PROCESS, events::{ @@ -33,4 +34,47 @@ mod tests { .await; assert!(waiter.is_ok()); } + + // #[test_case] + // async fn test_simple_c_ret() { + // let fs = FILESYSTEM.get().unwrap(); + // let fd = { + // fs.lock() + // .open_file( + // "/executables/ret", + // OpenFlags::O_RDONLY | OpenFlags::O_WRONLY, + // ) + // .await + // .expect("Could not open file") + // }; + // sys_mmap( + // 0x9000, + // 0x1000, + // ProtFlags::PROT_EXEC.bits(), + // MmapFlags::MAP_FILE.bits(), + // fd as i64, + // 0, + // ); + + // let mut buffer = vec![0u8; 4096]; + // let bytes_read = { + // fs.lock() + // .read_file(fd, &mut buffer) + // .await + // .expect("Failed to read file") + // }; + // debug!("bytes_read = {bytes_read}"); + + // let buf = &buffer[..bytes_read]; + + // let pid = create_process(buf); + // schedule_process(pid); + // let waiter = AwaitProcess::new( + // pid, + // get_runner_time(3_000_000_000), + // current_running_event().unwrap(), + // ) + // .await; + // assert!(waiter.is_ok()); + // } } diff --git a/kernel/src/processes/process.rs b/kernel/src/processes/process.rs index 8875ba15..ca35911c 100644 --- a/kernel/src/processes/process.rs +++ b/kernel/src/processes/process.rs @@ -23,6 +23,7 @@ use crate::{ }, processes::{loader::load_elf, registers::Registers}, serial_println, + syscalls::sockets::Socket, }; use alloc::{collections::BTreeMap, sync::Arc}; use core::{ @@ -51,6 +52,20 @@ pub enum ProcessState { Kernel, } +/// Represents everything that is given a file descriptor +#[derive(Debug, Clone)] +pub enum FakeFile { + /// Represents a actual file + File(Arc>), + /// Represents a socket + Socket(Arc>), + /// Reserved for standard in and out + Console(), + /// Used as a sentinel to say that someone has claimed the file + /// descriptor, but has not chosen what to place in it yet. + Claimed(), +} + #[derive(Debug, Clone)] /// TODO:Put locks around all of this for supporting multithreadings pub struct PCB { @@ -61,8 +76,7 @@ pub struct PCB { pub next_preemption_time: u64, pub registers: Registers, pub mmap_address: u64, - pub fd_table: [Option>>; MAX_FILES], - pub next_fd: Arc>, + pub fd_table: [Option; MAX_FILES], pub mm: Mm, pub namespace: Namespace, } @@ -96,6 +110,17 @@ impl PCB { let ptr = virt.as_mut_ptr::(); OffsetPageTable::new(unsafe { &mut *ptr }, *HHDM_OFFSET) } + + /// Finds the next open file descriptor in the files + pub fn find_next_fd(&mut self) -> Option { + for (idx, file) in self.fd_table.iter().enumerate() { + if file.is_none() { + self.fd_table[idx] = Option::Some(FakeFile::Claimed()); + return Option::Some(idx); + } + } + Option::None + } } pub fn get_current_pid() -> u32 { @@ -155,6 +180,10 @@ pub fn create_placeholder_process() -> u32 { let pid = 0; let process_pml4_frame = unsafe { create_process_page_table() }; let mm = Mm::new(process_pml4_frame); + let mut fd_table = [const { None }; MAX_FILES]; + fd_table[0] = Option::Some(FakeFile::Console()); + fd_table[1] = Option::Some(FakeFile::Console()); + fd_table[2] = Option::Some(FakeFile::Console()); let process = Arc::new(UnsafePCB::new(PCB { pid, state: ProcessState::New, @@ -181,8 +210,7 @@ pub fn create_placeholder_process() -> u32 { rflags: 0x0, }, mmap_address: START_MMAP_ADDRESS, - fd_table: [const { None }; MAX_FILES], - next_fd: Arc::new(Mutex::new(0)), + fd_table, next_preemption_time: 0, mm, namespace: Namespace::new(), @@ -211,7 +239,10 @@ pub fn create_process(elf_bytes: &[u8]) -> u32 { &mut KERNEL_MAPPER.lock(), mm.borrow_mut(), ); - + let mut fd_table = [const { None }; MAX_FILES]; + fd_table[0] = Option::Some(FakeFile::Console()); + fd_table[1] = Option::Some(FakeFile::Console()); + fd_table[2] = Option::Some(FakeFile::Console()); let process = Arc::new(UnsafePCB::new(PCB { pid, state: ProcessState::New, @@ -239,8 +270,7 @@ pub fn create_process(elf_bytes: &[u8]) -> u32 { rflags: 0x202, }, mmap_address: START_MMAP_ADDRESS, - fd_table: [const { None }; MAX_FILES], - next_fd: Arc::new(Mutex::new(0)), + fd_table, mm, namespace: Namespace::new(), })); diff --git a/kernel/src/syscalls/mod.rs b/kernel/src/syscalls/mod.rs index c22f6dcb..092d99e8 100644 --- a/kernel/src/syscalls/mod.rs +++ b/kernel/src/syscalls/mod.rs @@ -1,5 +1,6 @@ pub mod fork; pub mod memorymap; +pub mod sockets; pub mod syscall_handlers; #[cfg(test)] diff --git a/kernel/src/syscalls/sockets.rs b/kernel/src/syscalls/sockets.rs new file mode 100644 index 00000000..0a88b3e5 --- /dev/null +++ b/kernel/src/syscalls/sockets.rs @@ -0,0 +1,315 @@ +use alloc::{boxed::Box, sync::Arc, vec, vec::Vec}; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use smoltcp::{ + iface::SocketHandle, + socket::{tcp, udp}, + wire::{IpAddress, IpEndpoint, Ipv4Address}, +}; +use spin::Mutex; + +use crate::{ + constants::processes::MAX_FILES, + net::{get_eph_port, with_interface, DeviceInterface}, + processes::process::{with_current_pcb, FakeFile}, +}; + +#[repr(u32)] +#[derive(FromPrimitive)] +enum SocketDomain { + Unspecified = 0, + // Unix domain sockets for local communication + Unix = 1, + // IP protocol sockets + Inet = 2, +} + +#[repr(u32)] +#[derive(FromPrimitive)] +enum InetSocketType { + /// TCP + Stream = 1, + /// UDP + Datagram = 2, + /// Raw + Raw = 3, +} + +#[derive(Debug, Clone)] +pub enum Socket { + Unix(UnixSocket), + Internet(InternetSocket), +} + +/// A socket File Descriptor for IPC +#[derive(Debug, Clone)] +pub struct UnixSocket {} + +#[derive(Debug, Clone)] +/// A socket File Descriptor for connecting out to the wider internet +pub enum InternetSocket { + UDP(UDPSocket), + TCP(TCPSocket), + Raw(RawSocket), +} + +#[derive(Debug, Clone)] +pub struct UDPSocket { + handle: SocketHandle, +} + +#[derive(Debug, Clone)] +pub struct TCPSocket { + handle: SocketHandle, + queue: Option]>>, +} + +#[derive(Debug, Clone)] +pub struct RawSocket {} + +pub enum SocketError { + // Should set errno = EINVAL + UnsupportedProtocol, + // Should set errno = EINVAL + UnsupportedFlags, + // Should set errno = EMFILE + NoFreeFileDescriptor, + // No interface is set up, set errno = ENODEV (but ENXIO also works) + NoInterface, + // The file descriptor given was not a valid file = EBADF + NotAnOpenFile, + // The file descriptor given was not a socket = ENOTSOC + NotASocket, + // The address was already used, or all ephenepheral ports were used = EADDRINUSE + AddressInUse, + // Tried to bind to an already bound value = EINVAL + SocketAlreadyBound, + // Tried to preform an operation on a socket type that was not supported = EOPNOTSUPP + OperationNotSuppoorted, +} + +/// Implementation of the socket system call. +/// Wraps everything up into a u64 (but keep in mind that failures will return +/// usize::MAX ) which should have a equvilent equivilent to -1i64. +/// If kernel code wants to use sockets, they should use socket_impl +/// which has a much more sane error reporting syntax +pub fn sys_socket(domain: u64, socket_type: u64, protocol: u64) -> u64 { + let stuff = socket_impl(domain, socket_type, protocol).unwrap_or(usize::MAX); + let return_64: u64 = stuff.try_into().unwrap(); + return_64 +} + +/// Implementation of the socket system call. Returns the sockets file descriptor +/// or SocketError if the call failed. Invalid arguments will NOT raise +/// assertion errors as this is designed to be called by users +pub fn socket_impl(domain: u64, socket_type: u64, protocol: u64) -> Result { + let checked_domain = SocketDomain::from_u64(domain).ok_or(SocketError::UnsupportedProtocol)?; + + // Claim a file descriptor + let fd = with_current_pcb(|pcb| pcb.find_next_fd()).ok_or(SocketError::NoFreeFileDescriptor)?; + + // Send off to the apppropate domain + match checked_domain { + SocketDomain::Inet => create_internet_socket(socket_type, protocol, fd), + SocketDomain::Unix => { + todo!() + } + SocketDomain::Unspecified => Err(SocketError::UnsupportedProtocol), + } +} + +fn create_internet_socket( + socket_type: u64, + _protocol: u64, + fd: usize, +) -> Result { + let checked_type = + InetSocketType::from_u64(socket_type).ok_or(SocketError::UnsupportedProtocol)?; + let internet_socket = match checked_type { + InetSocketType::Datagram => { + let rx_buffer = udp::PacketBuffer::new(vec![], vec![0; 4096]); + let tx_buffer = udp::PacketBuffer::new(vec![], vec![0; 4096]); + + let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); + let socket_handle = with_interface(|interface| interface.sockets.add(udp_socket)) + .ok_or(SocketError::NoInterface)?; + let udp_socket = UDPSocket { + handle: socket_handle, + }; + InternetSocket::UDP(udp_socket) + } + InetSocketType::Raw => { + todo!(); + } + InetSocketType::Stream => { + let rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp_socket = tcp::Socket::new(rx_buffer, tx_buffer); + let socket_handle = with_interface(|interface| interface.sockets.add(tcp_socket)) + .ok_or(SocketError::NoInterface)?; + let tcp_socket = TCPSocket { + handle: socket_handle, + queue: Option::None, + }; + InternetSocket::TCP(tcp_socket) + } + }; + with_current_pcb(|pcb| { + pcb.fd_table[fd] = Option::Some(FakeFile::Socket(Arc::new(Mutex::new(Socket::Internet( + internet_socket, + ))))) + }); + Result::Ok(fd) +} + +pub fn sys_bind(socket_fd: u64, sock_addr_ptr: u64, addrlen: u64) -> u64 { + if bind_impl(socket_fd, sock_addr_ptr, addrlen).is_err() { + return u64::MAX; + } + 0 +} + +pub fn bind_impl(socket_fd: u64, sock_addr_ptr: u64, _addrlen: u64) -> Result<(), SocketError> { + let socket_size: usize = socket_fd.try_into().unwrap(); + if socket_size > MAX_FILES { + return Result::Err(SocketError::NotAnOpenFile); + } + let file = with_current_pcb(|pcb| pcb.fd_table[socket_size].clone()); + let file = file.ok_or(SocketError::NotAnOpenFile)?; + if let FakeFile::Socket(socket) = file { + let socket_guard = socket.lock(); + match socket_guard.clone() { + Socket::Internet(inet_socket) => { + bind_internet_socket(inet_socket, sock_addr_ptr)?; + Ok(()) + } + Socket::Unix(_domain_socket) => { + todo!() + } + } + } else { + Result::Err(SocketError::NotASocket) + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +struct SockAddr { + sa_family: u32, + // In NETWORK byte order (big endian) + port: u16, + // In NETWORK byte order (big endian) + sin_addr: u32, +} + +fn bind_internet_socket( + inet_socket: InternetSocket, + sock_addr_ptr: u64, +) -> Result<(), SocketError> { + let sock_addr_size: usize = sock_addr_ptr.try_into().unwrap(); + let sock_addr_ptr = sock_addr_size as *const SockAddr; + let sock_addr = unsafe { *sock_addr_ptr }; + let network_ports = sock_addr.port.to_be(); + let network_addr = sock_addr.sin_addr.to_be_bytes(); + let ipv4_addr = Ipv4Address::new( + network_addr[0], + network_addr[1], + network_addr[2], + network_addr[3], + ); + let ip_ep = IpEndpoint { + addr: IpAddress::Ipv4(ipv4_addr), + port: network_ports, + }; + match inet_socket { + InternetSocket::UDP(udp_socket) => { + with_interface(|interface| bind_udp_socket(interface, ip_ep, udp_socket)) + .ok_or(SocketError::NoInterface)? + } + InternetSocket::TCP(tcp_socket) => { + with_interface(|interface| bind_tcp_socket(interface, ip_ep, tcp_socket)) + .ok_or(SocketError::NoInterface)? + } + InternetSocket::Raw(_raw_socket) => { + todo!() + } + } +} + +fn bind_udp_socket( + interface: &mut DeviceInterface, + ip_ep: IpEndpoint, + udp_socket: UDPSocket, +) -> Result<(), SocketError> { + let udp_socket = interface.sockets.get_mut::(udp_socket.handle); + udp_socket + .bind(ip_ep) + .map_err(|_| SocketError::SocketAlreadyBound)?; + Result::Ok(()) +} + +fn bind_tcp_socket( + interface: &mut DeviceInterface, + ip_ep: IpEndpoint, + tcp_socket: TCPSocket, +) -> Result<(), SocketError> { + let tcp_socket = interface.sockets.get_mut::(tcp_socket.handle); + let local_endpoint = get_eph_port().ok_or(SocketError::AddressInUse)?; + tcp_socket + .connect(interface.interface.context(), ip_ep, local_endpoint) + .map_err(|_| SocketError::SocketAlreadyBound)?; + + Result::Ok(()) +} + +pub fn sys_connect(socket_fd: u64, sock_addr_ptr: u64, addrlen: u64) -> u64 { + if bind_impl(socket_fd, sock_addr_ptr, addrlen).is_err() { + return u64::MAX; + } + 0 +} + +pub fn connect_impl(socket_fd: u64, sock_addr_ptr: u64, addrlen: u64) -> Result<(), SocketError> { + // Probally wrong, but who cares + bind_impl(socket_fd, sock_addr_ptr, addrlen) +} + +pub fn listen_impl(socket_fd: u64, backlog: u64) -> Result<(), SocketError> { + let socket_size: usize = socket_fd.try_into().unwrap(); + if socket_size > MAX_FILES { + return Result::Err(SocketError::NotAnOpenFile); + } + let file = with_current_pcb(|pcb| pcb.fd_table[socket_size].clone()); + let file = file.ok_or(SocketError::NotAnOpenFile)?; + if let FakeFile::Socket(socket) = file { + let mut socket_guard = socket.lock(); + match socket_guard.clone() { + Socket::Internet(mut inet_socket) => { + listen_internet_socket(&mut inet_socket, backlog)?; + *socket_guard = Socket::Internet(inet_socket); + Result::Ok(()) + } + Socket::Unix(_domain_socket) => { + todo!() + } + } + } else { + Result::Err(SocketError::NotASocket) + } +} + +fn listen_internet_socket(socket: &mut InternetSocket, backlog: u64) -> Result<(), SocketError> { + if let InternetSocket::TCP(ref mut tcp_sock) = socket { + let backlog_size: usize = backlog.try_into().unwrap(); + let mut conn_buff: Vec> = Vec::with_capacity(backlog_size); + for _ in 0..backlog_size { + conn_buff.push(Option::None); + } + let stuff = conn_buff.into_boxed_slice(); + + tcp_sock.queue = Option::Some(stuff); + return Result::Ok(()); + } + + Result::Err(SocketError::OperationNotSuppoorted) +} diff --git a/kernel/src/syscalls/syscall_handlers.rs b/kernel/src/syscalls/syscall_handlers.rs index 221121ba..7d36a5ad 100644 --- a/kernel/src/syscalls/syscall_handlers.rs +++ b/kernel/src/syscalls/syscall_handlers.rs @@ -29,7 +29,10 @@ use crate::{ use core::arch::naked_asm; -use super::memorymap::{sys_mprotect, sys_munmap}; +use super::{ + memorymap::{sys_mprotect, sys_munmap}, + sockets::{sys_bind, sys_connect, sys_socket}, +}; lazy_static! { pub static ref EXIT_CODES: Mutex> = Mutex::new(BTreeMap::new()); @@ -178,6 +181,9 @@ pub unsafe extern "C" fn syscall_handler_impl( SYSCALL_WAIT => block_on(sys_wait(syscall.arg1 as u32)), SYSCALL_MUNMAP => sys_munmap(syscall.arg1, syscall.arg2), SYSCALL_MPROTECT => sys_mprotect(syscall.arg1, syscall.arg2, syscall.arg3), + SYSCALL_SOCKET => sys_socket(syscall.arg1, syscall.arg2, syscall.arg3), + SYSCALL_BIND => sys_bind(syscall.arg1, syscall.arg2, syscall.arg3), + SYSCALL_CONNECT => sys_connect(syscall.arg1, syscall.arg2, syscall.arg3), _ => { panic!("Unknown syscall, {}", syscall.number); } diff --git a/resources/executables/hello b/resources/executables/hello new file mode 100755 index 00000000..27b9f7b3 Binary files /dev/null and b/resources/executables/hello differ diff --git a/resources/executables/hello.c b/resources/executables/hello.c new file mode 100644 index 00000000..dafadc78 --- /dev/null +++ b/resources/executables/hello.c @@ -0,0 +1,6 @@ +#include + +int main() { + printf("Hello World!\n"); + return 0; +} diff --git a/resources/executables/ret b/resources/executables/ret new file mode 100755 index 00000000..dee274c0 Binary files /dev/null and b/resources/executables/ret differ diff --git a/resources/executables/ret.c b/resources/executables/ret.c new file mode 100644 index 00000000..4cce7f66 --- /dev/null +++ b/resources/executables/ret.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/setup_sysroot.sh b/setup_sysroot.sh new file mode 100755 index 00000000..994c918b --- /dev/null +++ b/setup_sysroot.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -euo pipefail + +# Ensure system architecture is x86_64 +ARCH="$(uname -m)" +if [[ "${ARCH}" != "x86_64" ]]; then + echo "This script only supports x86_64 architecture" + exit 1 +fi + +SYSROOT="$HOME/linux_sysroot" +MUSL_VERSION="1.2.5" +MUSL_TAR="musl-${MUSL_VERSION}.tar.gz" +MUSL_URL="https://musl.libc.org/releases/${MUSL_TAR}" + +# Create sysroot and build dirs +echo "Creating sysroot directory at ${SYSROOT}..." +mkdir -p "${SYSROOT}" +BUILD_DIR="$(mktemp -d)" +echo "Using build directory: ${BUILD_DIR}" + +# Download musl tar +echo "Downloading and extracting musl ${MUSL_VERSION}..." +curl -LO "${MUSL_URL}" +tar -xf "${MUSL_TAR}" -C "${BUILD_DIR}" + +pushd "${BUILD_DIR}/musl-${MUSL_VERSION}" >/dev/null + +# configure for static linking +echo "Configuring musl..." +./configure --prefix=/usr --sysroot=~/linux_sysroot --disable-shared +make && make install DESTDIR=~/linux_sysroot + +popd >/dev/null + +# Update musl-gcc wrapper to point to the correct specs file +echo "Updating musl-gcc wrapper..." +MUSL_GCC_PATH="${SYSROOT}/usr/bin/musl-gcc" +if [ -f "${MUSL_GCC_PATH}" ]; then + "${SYSROOT}/usr/bin/musl-gcc" -dumpspecs >"${SYSROOT}/usr/lib/musl-gcc.specs" + sed -i 's|exec "${REALGCC:-gcc}" "$@" -specs "/usr/lib/musl-gcc.specs"|"${REALGCC:-gcc}" "$@" -specs "'${SYSROOT}'/usr/lib/musl-gcc.specs"|g' "${MUSL_GCC_PATH}" +else + echo "musl-gcc wrapper not found at ${MUSL_GCC_PATH}" +fi + +# Clean up build directory and tarball +echo "Cleaning up..." +rm -rf "${BUILD_DIR}" +rm -f "${MUSL_TAR}" + +echo "Sysroot setup complete at ${SYSROOT}." +echo "You can now statically compile C programs using musl by invoking:" +echo " ${SYSROOT}/usr/bin/musl-gcc -static -o your_program your_program.c"