From 5baaa26dad14b2375398e5b89deb19afc2b9d3e9 Mon Sep 17 00:00:00 2001 From: INeedHelp Date: Sun, 7 May 2023 14:23:14 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=A5=EF=B8=8F=20-=20OS=20template=200.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Cargo.lock | 7 + Cargo.toml | 8 + src/arch/armv7.rs | 13 ++ src/arch/x86_64.rs | 60 +++++ src/drivers/keyboard.rs | 44 ++++ src/drivers/network.rs | 49 ++++ src/drivers/storage.rs | 60 +++++ src/fs/ext2.rs | 459 +++++++++++++++++++++++++++++++++++++ src/fs/fat.rs | 20 ++ src/fs/vfs.rs | 42 ++++ src/kernel/interrupts.rs | 0 src/kernel/memory.rs | 33 +++ src/kernel/scheduler.rs | 31 +++ src/lib/collections.rs | 38 +++ src/lib/io.rs | 28 +++ src/lib/sync.rs | 21 ++ src/main.rs | 15 ++ src/mm/allocator.rs | 33 +++ src/mm/paging.rs | 87 +++++++ src/net/ip.rs | 62 +++++ src/net/tcp.rs | 99 ++++++++ src/net/udp.rs | 229 ++++++++++++++++++ src/process/process.rs | 25 ++ src/process/thread.rs | 95 ++++++++ src/syscall/syscall.rs | 44 ++++ src/syscall/syscalls.s | 9 + src/tests/keyboard_test.rs | 38 +++ src/tests/network_test.rs | 16 ++ src/util/logging.rs | 53 +++++ src/util/time.rs | 17 ++ 31 files changed, 1736 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/arch/armv7.rs create mode 100644 src/arch/x86_64.rs create mode 100644 src/drivers/keyboard.rs create mode 100644 src/drivers/network.rs create mode 100644 src/drivers/storage.rs create mode 100644 src/fs/ext2.rs create mode 100644 src/fs/fat.rs create mode 100644 src/fs/vfs.rs create mode 100644 src/kernel/interrupts.rs create mode 100644 src/kernel/memory.rs create mode 100644 src/kernel/scheduler.rs create mode 100644 src/lib/collections.rs create mode 100644 src/lib/io.rs create mode 100644 src/lib/sync.rs create mode 100644 src/main.rs create mode 100644 src/mm/allocator.rs create mode 100644 src/mm/paging.rs create mode 100644 src/net/ip.rs create mode 100644 src/net/tcp.rs create mode 100644 src/net/udp.rs create mode 100644 src/process/process.rs create mode 100644 src/process/thread.rs create mode 100644 src/syscall/syscall.rs create mode 100644 src/syscall/syscalls.s create mode 100644 src/tests/keyboard_test.rs create mode 100644 src/tests/network_test.rs create mode 100644 src/util/logging.rs create mode 100644 src/util/time.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8f6689c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "os-template" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2102a33 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "os-template" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/arch/armv7.rs b/src/arch/armv7.rs new file mode 100644 index 0000000..6bf25d2 --- /dev/null +++ b/src/arch/armv7.rs @@ -0,0 +1,13 @@ +#![no_std] + +#[no_mangle] +pub extern "C" fn kmain() -> ! { + // Set up some initial values + let mut x = 0; + let y = 1; + + // Enter an infinite loop + loop { + x += y; + } +} diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs new file mode 100644 index 0000000..7de2cdb --- /dev/null +++ b/src/arch/x86_64.rs @@ -0,0 +1,60 @@ +// Simple x86_64 implementation + +#![no_std] + +// GDT descriptor +#[repr(C)] +struct GdtDescriptor { + limit_low: u16, + base_low: u16, + base_middle: u8, + access: u8, + granularity: u8, + base_high: u8, +} + +// GDT table +static GDT: [GdtDescriptor; 3] = [ + // Null descriptor + GdtDescriptor { + limit_low: 0, + base_low: 0, + base_middle: 0, + access: 0, + granularity: 0, + base_high: 0, + }, + // Code descriptor + GdtDescriptor { + limit_low: 0xFFFF, + base_low: 0, + base_middle: 0, + access: 0x9A, + granularity: 0xCF, + base_high: 0, + }, + // Data descriptor + GdtDescriptor { + limit_low: 0xFFFF, + base_low: 0, + base_middle: 0, + access: 0x92, + granularity: 0xCF, + base_high: 0, + }, +]; + +#[no_mangle] +pub extern "C" fn kmain() -> ! { + // Load the GDT + unsafe { + let descriptor = GdtDescriptorPointer { + size: (core::mem::size_of::<[GdtDescriptor; 3]>() - 1) as u16, + offset: &GDT as *const _ as u64, + }; + asm!("lgdt [{0}]", in(reg) &descriptor); + } + + // Enter an infinite loop + loop {} +} diff --git a/src/drivers/keyboard.rs b/src/drivers/keyboard.rs new file mode 100644 index 0000000..cdbc56e --- /dev/null +++ b/src/drivers/keyboard.rs @@ -0,0 +1,44 @@ +// Keyboard driver implementation for x86_64 architecture +// Requires a PS/2 keyboard + +use x86_64::instructions::port::Port; + +// PS/2 keyboard constants +const PS2_DATA_PORT: u16 = 0x60; +const PS2_STATUS_PORT: u16 = 0x64; +const PS2_ACK: u8 = 0xFA; + +// Keyboard driver struct +pub struct Keyboard { + status_port: Port, + data_port: Port, +} + +impl Keyboard { + // Initialize the keyboard driver + pub fn new() -> Keyboard { + Keyboard { + status_port: Port::new(PS2_STATUS_PORT), + data_port: Port::new(PS2_DATA_PORT), + } + } + + // Check if a key is currently pressed + fn is_key_pressed(&mut self) -> bool { + self.status_port.read() & 0x01 != 0 + } + + // Read a key press from the keyboard + pub fn read_key(&mut self) -> Option { + if self.is_key_pressed() { + let scancode = self.data_port.read(); + + // Send an ACK to the keyboard controller + self.data_port.write(PS2_ACK); + + Some(scancode) + } else { + None + } + } +} diff --git a/src/drivers/network.rs b/src/drivers/network.rs new file mode 100644 index 0000000..33f84b5 --- /dev/null +++ b/src/drivers/network.rs @@ -0,0 +1,49 @@ +// Network driver implementation for x86_64 architecture +// Requires a compatible network interface card (NIC) + +use x86_64::instructions::port::{Port, PortWriteOnly}; + +// Network card constants +const NIC_DATA_PORT: u16 = 0x300; +const NIC_COMMAND_PORT: u16 = 0x301; + +// Network driver struct +pub struct Network { + data_port: Port, + command_port: PortWriteOnly, +} + +impl Network { + // Initialize the network driver + pub fn new() -> Network { + Network { + data_port: Port::new(NIC_DATA_PORT), + command_port: PortWriteOnly::new(NIC_COMMAND_PORT), + } + } + + // Read data from the network card + pub fn read(&mut self, buffer: &mut [u8]) { + for byte in buffer { + *byte = self.data_port.read(); + } + } + + // Write data to the network card + pub fn write(&mut self, buffer: &[u8]) { + for byte in buffer { + self.data_port.write(*byte); + } + } + + // Reset the network card + pub fn reset(&mut self) { + self.command_port.write(0x80); + self.command_port.write(0x00); + } + + // Configure the network card + pub fn configure(&mut self) { + // Set up the network card here + } +} diff --git a/src/drivers/storage.rs b/src/drivers/storage.rs new file mode 100644 index 0000000..903223f --- /dev/null +++ b/src/drivers/storage.rs @@ -0,0 +1,60 @@ +// Storage driver implementation for x86_64 architecture +// Requires a compatible storage device + +use x86_64::instructions::port::{Port, PortWriteOnly}; + +// Storage device constants +const STORAGE_DATA_PORT: u16 = 0x1F0; +const STORAGE_COMMAND_PORT: u16 = 0x1F7; + +// Storage driver struct +pub struct Storage { + data_port: Port, + command_port: PortWriteOnly, +} + +impl Storage { + // Initialize the storage driver + pub fn new() -> Storage { + Storage { + data_port: Port::new(STORAGE_DATA_PORT), + command_port: PortWriteOnly::new(STORAGE_COMMAND_PORT), + } + } + + // Read data from the storage device + pub fn read(&mut self, buffer: &mut [u8]) { + for sector in buffer.chunks_exact_mut(512) { + self.read_sector(sector); + } + } + + // Write data to the storage device + pub fn write(&mut self, buffer: &[u8]) { + for sector in buffer.chunks_exact(512) { + self.write_sector(sector); + } + } + + // Read a sector from the storage device + fn read_sector(&mut self, sector: &mut [u8]) { + // Issue a read command to the storage device + self.command_port.write(0x20); + + // Read the sector data + for byte in sector.iter_mut() { + *byte = self.data_port.read(); + } + } + + // Write a sector to the storage device + fn write_sector(&mut self, sector: &[u8]) { + // Issue a write command to the storage device + self.command_port.write(0x30); + + // Write the sector data + for byte in sector.iter() { + self.data_port.write(*byte); + } + } +} diff --git a/src/fs/ext2.rs b/src/fs/ext2.rs new file mode 100644 index 0000000..7db0404 --- /dev/null +++ b/src/fs/ext2.rs @@ -0,0 +1,459 @@ +// Ext2 file system driver implementation for x86_64 architecture +// Requires a compatible storage device + +use crate::storage::Storage; +use core::mem::size_of; + +// File system constants +const EXT2_BLOCK_SIZE: usize = 1024; +const EXT2_INODE_SIZE: usize = 128; +const EXT2_SUPERBLOCK_OFFSET: u64 = 1024; +const EXT2_BLOCK_GROUP_OFFSET: u64 = 2048; + +// File system driver struct +pub struct Ext2 { + storage: Storage, + block_size: usize, + block_group_size: usize, + inode_size: usize, +} + +impl Ext2 { + // Initialize the file system driver + pub fn new(storage: Storage) -> Ext2 { + let superblock = read_superblock(&storage); + let block_size = EXT2_BLOCK_SIZE << superblock.log_block_size; + let block_group_size = block_size * superblock.blocks_per_group as usize; + let inode_size = EXT2_INODE_SIZE; + + Ext2 { + storage, + block_size, + block_group_size, + inode_size, + } + } + + // Read data from a file in the file system + pub fn read_file(&mut self, path: &str, buffer: &mut [u8]) { + // Find the inode for the file + let inode = find_inode(path, &self.storage, &self.block_group_size); + + // Read the data blocks for the inode + let mut block_buffer = [0; EXT2_BLOCK_SIZE]; + let mut position = 0; + for block_index in &inode.block_pointers { + if *block_index == 0 { + break; + } + self.storage.read_block(*block_index as u64, &mut block_buffer); + let bytes_to_read = buffer.len() - position; + let bytes_from_block = bytes_to_read.min(EXT2_BLOCK_SIZE); + buffer[position..position + bytes_from_block].copy_from_slice(&block_buffer[0..bytes_from_block]); + position += bytes_from_block; + } + } +} + +// Superblock struct for the ext2 file system +#[repr(C, packed)] +struct Superblock { + _unused: u32, + inodes_count: u32, + blocks_count: u32, + _unused2: u32, + free_blocks_count: u32, + free_inodes_count: u32, + _unused3: u32, + _unused4: u32, + first_data_block: u32, + log_block_size: u32, + _unused5: u32, + blocks_per_group: u32, + _unused6: [u32; 8], +} + +// Read the superblock from the storage device +fn read_superblock(storage: &Storage) -> Superblock { + let mut superblock = Superblock { + _unused: 0, + inodes_count: 0, + blocks_count: 0, + _unused2: 0, + free_blocks_count: 0, + free_inodes_count: 0, + _unused3: 0, + _unused4: 0, + first_data_block: 0, + log_block_size: 0, + _unused5: 0, + blocks_per_group: 0, + _unused6: [0; 8], + }; + + let superblock_buffer = &mut [0; EXT2_BLOCK_SIZE]; + storage.read_block(EXT2_SUPERBLOCK_OFFSET / EXT2_BLOCK_SIZE,superblock_buffer); + unsafe { + let superblock_ptr = superblock_buffer.as_mut Superblock; + superblock = *superblock_ptr; + } + + superblock +} + +// Inode struct for the ext2 file system +#[repr(C)] +struct Inode { + mode: u16, + uid: u16, + size: u32, + access_time: u32, + create_time: u32, + modify_time: u32, + delete_time: u32, + gid: u16, + links_count: u16, + blocks_count: u32, + flags: u32, + osd1: u32, + block_pointers: [u32; 15], + generation: u32, + extended_attributes: u32, + size_high: u32, + _unused: u32, +} + +// Find the inode for a given file path +fn find_inode(path: &str, storage: &Storage, block_group_size: &usize) -> Inode { + let path_parts = path.split('/'); + let mut current_inode = read_inode(2, storage, block_group_size); /* Root directory inode */ + + for part in path_parts { + if part == "" { + continue; + } + + let directory_entries = read_directory_entries(¤t_inode, storage); + let directory_entry = directory_entries + .iter() + .find(|entry| entry.name == part) + .expect("File not found"); + + current_inode = read_inode(directory_entry.inode, storage, block_group_size); + } + + current_inode +} + +// Read the inode from the storage device +fn read_inode(inode_number: u32, storage: &Storage, block_group_size: &usize) -> Inode { + let inode_size = size_of::(); + let block_size = *block_group_size; + let block_number = (inode_number - 1) / (block_size as u32 / inode_size as u32); + let inode_offset = ((inode_number - 1) % (block_size as u32 / inode_size as u32)) as usize * inode_size; + let block_offset = block_number as usize * block_size; + let mut block_buffer = [0; EXT2_BLOCK_SIZE]; + storage.read_block(EXT2_BLOCK_GROUP_OFFSET + block_offset as u64, &mut block_buffer); + let inode_buffer = &mut [0; 128]; + inode_buffer.copy_from_slice(&block_buffer[inode_offset..inode_offset + inode_size]); + unsafe { + let inode_ptr = inode_buffer.as_mut_ptr() as *mut Inode; + *inode_ptr + } +} + +let inode_buffer = &mut [0; 128]; +inode_buffer.copy_from_slice(&block_buffer[inode_offset..inode_offset + inode_size]); +unsafe { + let inode_ptr = inode_buffer.as_mut_ptr() as *mut Inode; + *inode_ptr +} + +// Directory entry struct for the ext2 file system +#[repr(C)] +struct DirectoryEntry { + inode: u32, + size: u16, + name_len: u8, + file_type: u8, + name: [u8; 256], +} + +// Read directory entries from a directory inode +fn read_directory_entries(inode: &Inode, storage: &Storage) -> Vec { + let mut directory_entries = Vec::new(); + let mut block_buffer = [0; EXT2_BLOCK_SIZE]; + for block_index in &inode.block_pointers { + if *block_index == 0 { + break; + } + storage.read_block(*block_index as u64, &mut block_buffer); + let mut position = 0; + while position < EXT2_BLOCK_SIZE { + let directory_entry_buffer = &mut [0; 264]; + directory_entry_buffer.copy_from_slice(&block_buffer[position..position + 264]); + let directory_entry = unsafe { *(directory_entry_buffer as *const DirectoryEntry) }; + directory_entries.push(*directory_entry); + position += directory_entry.size as usize; + } + } + + directory_entries +} + +// Read a file from the storage device +pub fn read_file(path: &str, storage: &Storage, block_group_size: usize) -> String { + let inode = find_inode(path, storage, &block_group_size); + let mut content = String::new(); + let mut block_buffer = [0; EXT2_BLOCK_SIZE]; + for block_index in &inode.block_pointers { + if *block_index == 0 { + break; + } + storage.read_block(*block_index as u64, &mut block_buffer); + content.push_str(str::from_utf8(&block_buffer).unwrap()); + } + + content +} + +// Write a file to the storage device +pub fn write_file(path: &str, content: &str, storage: &mut Storage, block_group_size: usize) { + let mut parts = path.rsplitn(2, '/'); + let file_name = parts.next().unwrap(); + let directory_path = parts.next().unwrap_or(""); + let directory_inode = find_inode(directory_path, storage, &block_group_size); + let directory_entries = read_directory_entries(&directory_inode, storage); + let mut existing_entry: Option<&DirectoryEntry> = None; + for entry in directory_entries.iter() { + let name = str::from_utf8(&entry.name[..entry.name_len as usize]).unwrap(); + if name == file_name { + existing_entry = Some(entry); + break; + } + } + + if let Some(entry) = existing_entry { + let inode = read_inode(entry.inode, storage, &block_group_size); + let mut block_buffer = [0; EXT2_BLOCK_SIZE]; + let mut block_index = 0; + for block_pointer in inode.block_pointers.iter() { + if *block_pointer == 0 { + break; + } + block_index += 1; + storage.read_block(*block_pointer as u64, &mut block_buffer); + let mut position = 0; + while position < EXT2_BLOCK_SIZE { + let size = min(EXT2_BLOCK_SIZE - position, content.len()); + block_buffer[position..position + size].copy_from_slice(&content[..size].as_bytes()); + content = &content[size..]; + position += size; + if content.is_empty() { + break; + } + } + storage.write_block(*block_pointer as u64, &block_buffer); + block_buffer = [0; EXT2_BLOCK_SIZE]; + } + if !content.is_empty() { + let new_block = storage.allocate_block(); + block_index += 1; + inode.block_pointers[block_index] = new_block as u32; + storage.write_inode(entry.inode, &inode, &block_group_size); + let mut position = 0; + while position < EXT2_BLOCK_SIZE { + let size = min(EXT2_BLOCK_SIZE - position, content.len()); + block_buffer[position..position + size].copy_from_slice(&content[..size].as_bytes()); + content = &content[size..]; + position += size; + if content.is_empty() { + break; + } + } + storage.write_block(new_block as u64, &block_buffer); + } + } else { + let mut new_entry = DirectoryEntry { + inode: 0, + size: 0, + name_len: 0, + file_type: 0, + name: [0; 256], + }; + let new_inode = storage.allocate_inode(); + let mut inode = Inode { + mode: 0o100644, + uid: 0, + size: 0, + atime: 0, + ctime: 0, + mtime: 0, + dtime: 0, + gid: 0, + links_count: 1, + block_pointers: [0; 15], + flags: 0, + osd1: 0, + generation: 0, + file_acl: 0, + size_high: 0, + fragment_block: 0, + osd2: [0; 2], + }; + let new_block = storage.allocate_block(); + + let mut position = 0; + while position < new_entry.name.len() { + let byte = file_name.as_bytes()[position]; + new_entry.name[position] = byte; + if byte == 0 { + break; + } + position += 1; + } + new_entry.name_len = position as u8; + new_entry.inode = new_inode; + new_entry.size = (8 + new_entry.name_len as u32 + 3) & !3; + new_entry.file_type = 1; // regular file + + inode.block_pointers[0] = new_block as u32; + inode.size = content.len() as u32; + let mut position = 0; + while position < EXT2_BLOCK_SIZE { + let size = min(EXT2_BLOCK_SIZE - position, content.len()); + block_buffer[position..position + size].copy_from_slice(&content[..size].as_bytes()); + content = &content[size..]; + position += size; + if content.is_empty() { + break; + } + } + storage.write_block(new_block as u64, &block_buffer); + storage.write_inode(new_inode, &inode, &block_group_size); + + directory_entries.push(new_entry); + let mut directory_buffer = [0; EXT2_BLOCK_SIZE]; + let mut position = 0; + for entry in directory_entries.iter() { + directory_buffer[position..position + 8].copy_from_slice(&entry.inode.to_le_bytes()); + directory_buffer[position + 4..position + 8].copy_from_slice(&entry.size.to_le_bytes()); + directory_buffer[position + 8..position + 9].copy_from_slice(&[entry.name_len]); + directory_buffer[position + 9..position + 10].copy_from_slice(&[entry.file_type]); + directory_buffer[position + 10..position + 8 + entry.name_len as usize].copy_from_slice(&entry.name[..entry.name_len as usize]); + position += (8 + entry.name_len as usize + 3) & !3; + } + storage.write_block(directory_inode.block_pointers[0] as u64, &directory_buffer); + } +} + +// Allocate a block and write it with zeros +fn allocate_block(storage: &mut Storage) -> u32 { + let block = storage.allocate_block(); + let mut buffer = [0; EXT2_BLOCK_SIZE]; + storage.write_block(block as u64, &buffer); + block +} + +// Find the inode corresponding to a file or directory +fn find_inode(path: &str, storage: &Storage, block_group_size: &usize) -> Inode { + let mut inode = read_inode(EXT2_ROOT_INODE, storage, block_group_size); + let mut components = path.split('/').filter(|c| !c.is_empty()); + while let Some(component) = components.next() { + let directory_entries = read_directory_entries(&inode, storage); + let mut found = false; + for entry in directory_entries.iter() { + let name = str::from_utf8(&entry.name[..entry.name_len + as usize]) + .unwrap(); + if name == component { + inode = read_inode(entry.inode, storage, block_group_size); + found = true; + break; + } + } + if !found { + panic!("File not found: {}", path); + } + } + + inode +} + +// Read an inode from disk +fn read_inode(inode_number: u32, storage: &Storage, block_group_size: &usize) -> Inode { + let block_group = (inode_number - 1) / (*block_group_size as u32); + let offset = (inode_number - 1) % (*block_group_size as u32); + let block_group_desc = storage.read_block_group_descriptor(block_group); + let block_size = 1024 << block_group_desc.log_block_size; + let mut buffer = vec![0; INODE_SIZE]; + let inode_table_block = block_group_desc.inode_table_address + (offset as u64 * INODE_SIZE as u64 / block_size); + let inode_table_offset = (offset as usize * INODE_SIZE) % block_size as usize; + storage.read(inode_table_block as usize * block_size as usize + inode_table_offset, &mut buffer); + + let mut inode = Inode::default(); + let mut cursor = Cursor::new(&buffer[..]); + cursor.read_exact(&mut inode.mode.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.uid.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.size.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.atime.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.ctime.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.mtime.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.dtime.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.gid.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.links_count.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.block_pointers[..]).unwrap(); + cursor.read_exact(&mut inode.flags.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.osd1.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.generation.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.file_acl.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.size_high.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.fragment_block.to_le_bytes()).unwrap(); + cursor.read_exact(&mut inode.osd2[..]).unwrap(); + + inode +} + +// Read the directory entries from a directory inode +fn read_directory_entries(inode: &Inode, storage: &Storage) -> Vec { + let mut directory_entries = Vec::new(); + let mut directory_buffer = [0; EXT2_BLOCK_SIZE]; + let block_size = EXT2_BLOCK_SIZE as u64; + let mut position = 0; + while position < inode.size { + let block = position / block_size; + let block_offset = position % block_size; + storage.read_block(inode.block_pointers[block as usize] as u64, &mut directory_buffer); + while position < inode.size && (position % block_size) + 8 <= block_size { + let mut entry = DirectoryEntry::default(); + entry.inode = u32::from_le_bytes([ + directory_buffer[position], + directory_buffer[position + 1], + directory_buffer[position + 2], + directory_buffer[position + 3], + ]); + entry.size = u32::from_le_bytes([ + directory_buffer[position + 4], + directory_buffer[position + 5], + directory_buffer[position + 6], + directory_buffer[position + 7], + ]); + entry.name_len = directory_buffer[position + 8]; + entry.file_type = directory_buffer[position + 7]; + let name_len = entry.name_len as usize; + let mut name_buffer = vec![0; name_len]; + for i in 0..name_len { + name_buffer[i] = directory_buffer[position + 8 + i]; + } + entry.name = String::from_utf8(name_buffer).unwrap(); + + if entry.inode != 0 { + directory_entries.push(entry); + } + + position += entry.size as u64; + } + } + + directory_entries +} diff --git a/src/fs/fat.rs b/src/fs/fat.rs new file mode 100644 index 0000000..d0f103a --- /dev/null +++ b/src/fs/fat.rs @@ -0,0 +1,20 @@ +use crate::filesystem::{FileSystem, FileSystemError, DirectoryEntry}; + +#[derive(Debug)] +pub struct FatFileSystem { + // filesystem-specific data +} + +impl FileSystem for FatFileSystem { + fn read_directory(&mut self, path: &str) -> Result, FileSystemError> { + // implementation for reading a directory in FAT filesystem + // returns a vector of directory entries on success, or a FileSystemError on failure + unimplemented!() + } + + fn read_file(&mut self, path: &str) -> Result, FileSystemError> { + // implementation for reading a file in FAT filesystem + // returns the contents of the file as a byte vector on success, or a FileSystemError on failure + unimplemented!() + } +} diff --git a/src/fs/vfs.rs b/src/fs/vfs.rs new file mode 100644 index 0000000..722c9ee --- /dev/null +++ b/src/fs/vfs.rs @@ -0,0 +1,42 @@ +use crate::filesystem::{FileSystem, DirectoryEntry, FileSystemError}; + +#[derive(Debug)] +pub struct Vfs { + file_systems: Vec>, +} + +impl Vfs { + pub fn new() -> Vfs { + Vfs { + file_systems: Vec::new(), + } + } + + pub fn mount_filesystem(&mut self, filesystem: Box) { + self.file_systems.push(filesystem); + } + + pub fn read_directory(&mut self, path: &str) -> Result, FileSystemError> { + for fs in &mut self.file_systems { + match fs.read_directory(path) { + Ok(entries) => { + return Ok(entries); + }, + Err(_) => continue, + } + } + Err(FileSystemError::FileNotFound) + } + + pub fn read_file(&mut self, path: &str) -> Result, FileSystemError> { + for fs in &mut self.file_systems { + match fs.read_file(path) { + Ok(contents) => { + return Ok(contents); + }, + Err(_) => continue, + } + } + Err(FileSystemError::FileNotFound) + } +} diff --git a/src/kernel/interrupts.rs b/src/kernel/interrupts.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/kernel/memory.rs b/src/kernel/memory.rs new file mode 100644 index 0000000..274001c --- /dev/null +++ b/src/kernel/memory.rs @@ -0,0 +1,33 @@ +#[derive(Debug)] +pub struct MemoryManager { + start: usize, + end: usize, + next: usize, +} + +impl MemoryManager { + pub fn new(start: usize, end: usize) -> MemoryManager { + MemoryManager { + start, + end, + next: start, + } + } + + pub fn allocate(&mut self, size: usize, align: usize) -> Option { + let aligned_next = align_up(self.next, align); + let new_next = aligned_next + size; + + if new_next <= self.end { + let allocation = aligned_next; + self.next = new_next; + Some(allocation) + } else { + None + } + } +} + +fn align_up(addr: usize, align: usize) -> usize { + (addr + align - 1) & !(align - 1) +} diff --git a/src/kernel/scheduler.rs b/src/kernel/scheduler.rs new file mode 100644 index 0000000..f860c35 --- /dev/null +++ b/src/kernel/scheduler.rs @@ -0,0 +1,31 @@ +use crate::task::Task; + +pub struct Scheduler { + tasks: Vec, + current_task: usize, +} + +impl Scheduler { + pub fn new() -> Scheduler { + Scheduler { + tasks: Vec::new(), + current_task: 0, + } + } + + pub fn add_task(&mut self, task: Task) { + self.tasks.push(task); + } + + pub fn run(&mut self) { + while !self.tasks.is_empty() { + let task = &mut self.tasks[self.current_task]; + task.run(); + if task.is_done() { + self.tasks.remove(self.current_task); + } else { + self.current_task = (self.current_task + 1) % self.tasks.len(); + } + } + } +} diff --git a/src/lib/collections.rs b/src/lib/collections.rs new file mode 100644 index 0000000..2a74728 --- /dev/null +++ b/src/lib/collections.rs @@ -0,0 +1,38 @@ +pub struct Vec { + buffer: Box<[T]>, + len: usize, +} + +impl Vec { + pub fn new() -> Vec { + Vec { + buffer: Box::new([]), + len: 0, + } + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn push(&mut self, value: T) { + if self.buffer.len() == self.len { + let new_len = if self.len == 0 { 1 } else { self.len * 2 }; + let mut new_buffer = Vec::with_capacity(new_len); + new_buffer.extend_from_slice(&self.buffer[..self.len]); + self.buffer = new_buffer; + } + + self.buffer[self.len] = value; + self.len += 1; + } + + pub fn pop(&mut self) -> Option { + if self.len == 0 { + None + } else { + self.len -= 1; + Some(std::mem::replace(&mut self.buffer[self.len], unsafe { std::mem::uninitialized() })) + } + } +} diff --git a/src/lib/io.rs b/src/lib/io.rs new file mode 100644 index 0000000..34ed54b --- /dev/null +++ b/src/lib/io.rs @@ -0,0 +1,28 @@ +use core::fmt::{self, Write}; + +pub struct Console; + +impl Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + self.write_byte(byte)?; + } + Ok(()) + } +} + +impl Console { + pub fn new() -> Console { + Console {} + } + + pub fn write_byte(&mut self, byte: u8) -> fmt::Result { + // write byte to console + Ok(()) + } + + pub fn read_byte(&mut self) -> u8 { + // read byte from console + 0 + } +} diff --git a/src/lib/sync.rs b/src/lib/sync.rs new file mode 100644 index 0000000..6133bac --- /dev/null +++ b/src/lib/sync.rs @@ -0,0 +1,21 @@ +use core::sync::atomic::{AtomicBool, Ordering}; + +pub struct Spinlock { + lock: AtomicBool, +} + +impl Spinlock { + pub fn new() -> Spinlock { + Spinlock { + lock: AtomicBool::new(false), + } + } + + pub fn lock(&self) { + while self.lock.compare_and_swap(false, true, Ordering::Acquire) != false {} + } + + pub fn unlock(&self) { + self.lock.store(false, Ordering::Release); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5a2000e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,15 @@ +use arch::{armv7, x86_64}; +use drivers::{keyboard, network, storage}; +use fs::{vfs, fat, ext2}; +use kernel::{interrupts, memory, scheduler}; +use lib::{collections, io, sync}; +use mm::{paging, allocator}; +use net::{tcp, udp, ip}; +use process::{process, thread}; +use syscall::{syscall, syscalls_arm, syscalls_x86_64}; +use tests::{keyboard_test, network_test}; +use util::{logging, time}; + +fn main() { + // your code here +} diff --git a/src/mm/allocator.rs b/src/mm/allocator.rs new file mode 100644 index 0000000..cc11633 --- /dev/null +++ b/src/mm/allocator.rs @@ -0,0 +1,33 @@ +use super::memory::{Frame, FrameAllocator}; +use alloc::vec::Vec; +use buddy_system_allocator::LockedHeap; + +const HEAP_START: usize = 0o_000_001_000_000_0000; +const HEAP_SIZE: usize = 100 * 1024; + +static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; +static mut ALLOCATOR: Option = None; + +pub struct Allocator; + +unsafe impl FrameAllocator for Allocator { + unsafe fn allocate_frame(&mut self) -> Option { + ALLOCATOR.as_mut().unwrap().allocate_frame().map(Frame::from_start_address) + } + + unsafe fn deallocate_frame(&mut self, frame: Frame) { + ALLOCATOR.as_mut().unwrap().deallocate_frame(frame.start_address()); + } +} + +impl Allocator { + pub fn init_heap() { + unsafe { + ALLOCATOR = Some(buddy_system_allocator::Heap::new( + HEAP_START, + HEAP_SIZE, + )); + LockedHeap::initialize(HEAP.as_ptr() as usize, HEAP_SIZE); + } + } +} diff --git a/src/mm/paging.rs b/src/mm/paging.rs new file mode 100644 index 0000000..36c7198 --- /dev/null +++ b/src/mm/paging.rs @@ -0,0 +1,87 @@ +use super::memory::{Frame, FrameAllocator}; +use x86_64::{ + structures::paging::{ + FrameAllocator as FrameAllocatorTrait, Mapper, MapperAllSizes, Page, PageTable, + PageTableFlags, PageTableIndex, PhysFrame, Size4KiB, + }, + PhysAddr, VirtAddr, +}; + +pub const PAGE_SIZE: usize = 4096; + +pub type PageTableImpl = PageTable; + +pub struct PageTableManager { + page_table: PageTableImpl, +} + +impl PageTableManager { + pub fn new() -> Self { + let page_table = unsafe { PageTableImpl::new() }; + PageTableManager { page_table } + } + + pub fn activate(&mut self) { + unsafe { + self.page_table.activate(); + } + } + + pub fn map_to( + &mut self, + page: Page, + frame: Frame, + flags: PageTableFlags, + allocator: &mut impl FrameAllocatorTrait, + ) -> Result<(), &'static str> { + let frame_phys_addr = PhysAddr::new(frame.start_address()); + let page_entry = self.page_table.entry(page.clone()); + + if page_entry.is_unused() { + let frame = PhysFrame::containing_address(frame_phys_addr); + let flags = flags | PageTableFlags::PRESENT; + let map_result = unsafe { + self.page_table + .map_to(page, frame, flags, allocator) + .map_err(|_| "Failed to map page to frame")? + }; + + if let Some(cache) = map_result { + cache.flush(); + } + Ok(()) + } else { + Err("Page already mapped") + } + } + + pub fn unmap( + &mut self, + page: Page, + allocator: &mut impl FrameAllocatorTrait, + ) -> Result<(), &'static str> { + let page_entry = self.page_table.entry(page.clone()); + + if !page_entry.is_unused() { + let (_, frame, flush) = unsafe { page_entry.into_frame_unchecked() }; + let frame = frame.unwrap(); + let flags = PageTableFlags::empty(); + unsafe { + self.page_table + .unmap(page, allocator) + .map_err(|_| "Failed to unmap page from frame")?; + flush(); + } + allocator.deallocate_frame(Frame::from_start_address( + frame.start_address().as_u64() as usize, + )); + Ok(()) + } else { + Err("Page not mapped") + } + } + + pub fn translate_addr(&mut self, addr: VirtAddr) -> Option { + self.page_table.translate_addr(addr) + } +} diff --git a/src/net/ip.rs b/src/net/ip.rs new file mode 100644 index 0000000..bcd2f56 --- /dev/null +++ b/src/net/ip.rs @@ -0,0 +1,62 @@ +use crate::net::ipv4::Ipv4Addr; +use crate::net::ipv6::Ipv6Addr; +use crate::net::socket::{Socket, SocketAddr}; + +pub enum IpAddr { + V4(Ipv4Addr), + V6(Ipv6Addr), +} + +pub struct IpHeader { + version: u8, + header_len: u8, + tos: u8, + total_len: u16, + id: u16, + flags: u8, + frag_offset: u16, + ttl: u8, + protocol: u8, + checksum: u16, + src_addr: IpAddr, + dst_addr: IpAddr, +} + +impl IpHeader { + // Functions for parsing and constructing IP headers + // ... + + // Function for calculating the IP header checksum + // ... +} + +pub struct IpPacket { + header: IpHeader, + payload: Vec, +} + +impl IpPacket { + // Functions for parsing and constructing IP packets + // ... +} + +pub struct IpPacketBuffer { + buffer: [u8; 1500], + len: usize, +} + +impl IpPacketBuffer { + pub fn new() -> Self { + Self { + buffer: [0; 1500], + len: 0, + } + } + + // Functions for constructing IP packets in the buffer + // ... +} + +// Functions for IP routing and sending packets to the network interface +// ... + diff --git a/src/net/tcp.rs b/src/net/tcp.rs new file mode 100644 index 0000000..740c765 --- /dev/null +++ b/src/net/tcp.rs @@ -0,0 +1,99 @@ +use crate::net::ipv4::{Ipv4Header, Ipv4Packet}; +use crate::net::socket::{Socket, SocketAddr, SocketError, SocketType}; + +pub struct TcpHeader { + src_port: u16, + dst_port: u16, + seq_num: u32, + ack_num: u32, + data_offset: u8, + flags: u16, + window_size: u16, + checksum: u16, + urgent_ptr: u16, + options: [u8; 40], +} + +impl TcpHeader { + // Functions for parsing and constructing TCP headers + // ... + + // Function for calculating the TCP header checksum + // ... +} + +pub struct TcpPacket { + header: TcpHeader, + payload: Vec, +} + +impl TcpPacket { + // Functions for parsing and constructing TCP packets + // ... +} + +pub struct TcpSocket { + local_addr: SocketAddr, + remote_addr: SocketAddr, + state: TcpState, + receive_buffer: Vec, + send_buffer: Vec, +} + +enum TcpState { + Closed, + Listen, + SynSent, + SynReceived, + Established, + FinWait1, + FinWait2, + CloseWait, + Closing, + LastAck, + TimeWait, +} + +impl TcpSocket { + pub fn bind(local_addr: SocketAddr) -> Result { + // Function for binding a TCP socket to a local address + // ... + } + + pub fn listen() -> Result<(), SocketError> { + // Function for putting a TCP socket in listen mode + // ... + } + + pub fn accept() -> Result { + // Function for accepting an incoming TCP connection + // ... + } + + pub fn connect(remote_addr: SocketAddr) -> Result { + // Function for initiating a TCP connection to a remote address + // ... + } + + pub fn send(&mut self, data: &[u8]) -> Result { + // Function for sending data over a TCP connection + // ... + } + + pub fn receive(&mut self, data: &mut [u8]) -> Result { + // Function for receiving data from a TCP connection + // ... + } + + pub fn close(&mut self) -> Result<(), SocketError> { + // Function for closing a TCP connection + // ... + } + + // Functions for handling TCP protocol events + // ... +} + +// Functions for TCP protocol processing and sending/receiving packets +// ... + diff --git a/src/net/udp.rs b/src/net/udp.rs new file mode 100644 index 0000000..2e6f824 --- /dev/null +++ b/src/net/udp.rs @@ -0,0 +1,229 @@ +use crate::ip::IpAddr; +use crate::io::Socket; +use crate::sync::WaitQueue; +use core::cell::UnsafeCell; + +// Constants defining the protocol and various fields within the UDP header +const UDP_PROTOCOL: u8 = 17; +const UDP_SRC_PORT_OFFSET: usize = 0; +const UDP_DST_PORT_OFFSET: usize = 2; +const UDP_LENGTH_OFFSET: usize = 4; +const UDP_CHECKSUM_OFFSET: usize = 6; +const UDP_HEADER_LENGTH: usize = 8; + +// UDP packet structure +#[repr(packed)] +struct UdpPacket { + src_port: u16, + dst_port: u16, + length: u16, + checksum: u16, + data: [u8; 0], +} + +impl UdpPacket { + // Returns the total length of the UDP packet + fn len(&self) -> usize { + (usize::from(self.length)).to_be_bytes().len() + UDP_HEADER_LENGTH + } + + // Returns a pointer to the data field in the UDP packet + fn data(&self) -> *const u8 { + &self.data as *const u8 + } +} + +// UDP socket structure +pub struct UdpSocket { + // Underlying IP socket + socket: Socket, + + // Wait queue for blocking on incoming packets + rx_queue: UnsafeCell, + + // Buffer for incoming packets + rx_buffer: UnsafeCell)>>, +} + +impl UdpSocket { + // Creates a new UDP socket and binds it to the specified IP address and port + pub fn new(addr: IpAddr, port: u16) -> Self { + let socket = Socket::new(addr, UDP_PROTOCOL, port); + let rx_queue = UnsafeCell::new(WaitQueue::new()); + let rx_buffer = UnsafeCell::new(None); + + Self { + socket, + rx_queue, + rx_buffer, + } + } + + // Receives a UDP packet, blocking until one is available + pub fn recv(&self) -> (IpAddr, Vec) { + // Block until a packet is available + let mut rx_queue = unsafe { &mut *self.rx_queue.get() }; + rx_queue.wait(); + + // Retrieve the incoming packet and clear the receive buffer + let rx_buffer = unsafe { &mut *self.rx_buffer.get() }; + let (src_addr, data) = rx_buffer.take().unwrap(); + data.into_iter().enumerate().for_each(|(i, &byte)| { + unsafe { + *(((self as *const _) as *mut u8).add(UDP_HEADER_LENGTH + i)) = byte; + } + }); + + (src_addr, data) + } + + // Sends a UDP packet to the specified destination IP address and port + pub fn send(&self, dst_addr: IpAddr, dst_port: u16, data: &[u8]) { + // Construct the UDP header + let mut packet = UdpPacket { + src_port: self.socket.port(), + dst_port, + length: ((data.len() + UDP_HEADER_LENGTH) as u16).to_be(), + checksum: 0, + data: [0; 0], + }; + + // Copy the data into the packet buffer + for (i, &byte) in data.iter().enumerate() { + packet.data[i] = byte; + } + + // Calculate the checksum + packet.checksum = !checksum(&packet, self.socket.addr(), dst_addr, UDP_PROTOCOL, data.len()); + + // Send the packet + self + .socket + .send(dst_addr, UDP_PROTOCOL, &packet as *const _ as *const u8, packet.len()); + } + + // Handles an incoming UDP packet + fn handle_packet(&self, src_addr: IpAddr, packet: &[u8]) { + // Extract the UDP header fields + let src_port = u16::from_be_bytes([packet[UDP_SRC_PORT_OFFSET], packet[UDP_SRC_PORT_OFFSET + 1]]); + let dst_port = u16::from_be_bytes([packet[UDP_DST_PORT_OFFSET], packet[UDP_DST_PORT_OFFSET + 1]]); + let length = u16::from_be_bytes([packet[UDP_LENGTH_OFFSET], packet[UDP_LENGTH_OFFSET + 1]]); + + // Verify that the destination port matches our socket's port + if dst_port != self.socket.port() { + return; + } + + // Construct a buffer containing the packet data + let mut data = vec![0; usize::from(length) - UDP_HEADER_LENGTH]; + data.iter_mut().enumerate().for_each(|(i, byte)| { + *byte = packet[UDP_HEADER_LENGTH + i]; + }); + + // Store the incoming packet in the receive buffer + let rx_buffer = unsafe { &mut *self.rx_buffer.get() }; + *rx_buffer = Some((src_addr, data)); + + // Unblock any threads waiting to receive data + let mut rx_queue = unsafe { &mut *self.rx_queue.get() }; + rx_queue.notify_one(); + } +} + +// Calculates the checksum for a UDP packet +fn checksum(packet: &UdpPacket, src_addr: IpAddr, dst_addr: IpAddr, protocol: u8, data_len: usize) -> u16 { + let pseudo_header = pseudo_header(src_addr, dst_addr, protocol, data_len); + let mut sum = 0; + // Add the UDP header to the checksum + let udp_header = unsafe { + core::slice::from_raw_parts(packet as *const _ as *const u8, UDP_HEADER_LENGTH) + }; + sum = add_checksum(sum, udp_header); + +// Add the pseudo-header to the checksum + sum = add_checksum(sum, &pseudo_header); + + !sum +} + +// Calculates the pseudo-header for a UDP packet checksum +fn pseudo_header(src_addr: IpAddr, dst_addr: IpAddr, protocol: u8, data_len: usize) -> [u8; 12] { + let mut header = [0; 12]; + match (src_addr, dst_addr) { + (IpAddr::V4(src), IpAddr::V4(dst)) => { + let src_bytes = src.octets(); + let dst_bytes = dst.octets(); + header[..4].copy_from_slice(&src_bytes); + header[4..8].copy_from_slice(&dst_bytes); + } + (IpAddr::V6(src), IpAddr::V6(dst)) => { + let src_bytes = src.octets(); + let dst_bytes = dst.octets(); + header[..16].copy_from_slice(&src_bytes); + header[16..].copy_from_slice(&dst_bytes); + } + _ => unreachable!(), + } + header[9] = protocol; + header[10..].copy_from_slice(&(data_len as u16).to_be_bytes()); + header +} + +// Adds the checksum of a data slice to the current checksum +fn add_checksum(sum: u16, data: &[u8]) -> u16 { + let mut sum = sum; + let mut i = 0; + while i < data.len() { + let word = if i + 1 < data.len() { + u16::from_be_bytes([data[i], data[i + 1]]) + } else { + u16::from_be_bytes([data[i], 0x00]) + }; + sum = sum.wrapping_add(word); + i += 2; + } + sum +} + +// Calculates the one's complement of a 16-bit integer +fn ones_complement(mut sum: u16) -> u16 { + while sum > 0xFFFF { + sum = (sum >> 16) + (sum & 0xFFFF); + } + !sum +} + +// Calculates the UDP checksum for a given packet +fn checksum(packet: &UdpPacket, src_addr: IpAddr, dst_addr: IpAddr, protocol: u8, data_len: usize) -> u16 { + // Pseudo-header used for checksum calculation + #[repr(packed)] + struct PseudoHeader { + src_addr: u32, + dst_addr: u32, + zero: u8, + protocol: u8, + length: u16, + } + + // Construct the pseudo-header and UDP packet data + let pseudo_header = PseudoHeader { + src_addr: u32::from(src_addr), + dst_addr: u32::from(dst_addr), + zero: 0, + protocol, + length: ((data_len + UDP_HEADER_LENGTH) as u16).to_be(), + }; + let mut data = Vec::with_capacity(packet.len()); + unsafe { + let packet_ptr = packet as *const _ as *const u8; + for i in 0..packet.len() { + data.push(*packet_ptr.add(i)); + } + } + +// Calculate the checksum over the pseudo-header and packet data + let mut sum = add_checksum(0, &pseudo_header as *const _ as *const u8); + sum = add_checksum(sum, &data); + + ones_complement(sum) +} diff --git a/src/process/process.rs b/src/process/process.rs new file mode 100644 index 0000000..f3faee6 --- /dev/null +++ b/src/process/process.rs @@ -0,0 +1,25 @@ +// This module contains process and thread management code + +// Process ID type +pub type ProcessId = u32; + +// Thread ID type +pub type ThreadId = u32; + +// Process structure +pub struct Process { + // TODO: Define fields +} + +impl Process { + // TODO: Define methods +} + +// Thread structure +pub struct Thread { + // TODO: Define fields +} + +impl Thread { + // TODO: Define methods +} diff --git a/src/process/thread.rs b/src/process/thread.rs new file mode 100644 index 0000000..3ea8846 --- /dev/null +++ b/src/process/thread.rs @@ -0,0 +1,95 @@ +use core::ptr; + +// Thread ID type +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ThreadId(pub usize); + +// Thread structure +pub struct Thread { + id: ThreadId, + stack: *mut u8, + state: ThreadState, +} + +impl Thread { + // Creates a new thread with the given function as its entry point + pub fn new(entry: fn()) -> Self { + // Allocate stack space for the thread + let stack_size = 4096; // 4 KB + let stack = unsafe { + let stack = alloc(stack_size); + if stack.is_null() { + panic!("Failed to allocate stack for new thread"); + } + stack.add(stack_size) + }; + + // Initialize the stack with the thread entry point + unsafe { + ptr::write_volatile(stack.offset(-4) as *mut fn(), entry); + } + + // Create and return the thread + Self { + id: ThreadId(0), // TODO: generate unique thread ID + stack, + state: ThreadState::Ready, + } + } + + // Returns the thread ID + pub fn id(&self) -> ThreadId { + self.id + } + + // Sets the thread state + pub fn set_state(&mut self, state: ThreadState) { + self.state = state; + } + + // Returns the thread state + pub fn state(&self) -> ThreadState { + self.state + } + + // Runs the thread, starting at its entry point + pub fn run(&mut self) { + // Set the current thread + unsafe { + CURRENT_THREAD = self; + } + + // Call the thread entry point + let entry: fn() = unsafe { ptr::read_volatile(self.stack.offset(-4) as *const fn()) }; + entry(); + + // Thread has exited + self.set_state(ThreadState::Exited); + unsafe { + yield_cpu(); // Switch to next runnable thread + } + } +} + +// Thread state +#[derive(Debug, PartialEq, Eq)] +pub enum ThreadState { + Ready, + Running, + Blocked, + Exited, +} + +// Pointer to the currently running thread +static mut CURRENT_THREAD: *mut Thread = ptr::null_mut(); + +// Switches to the next runnable thread +fn yield_cpu() { + // TODO: implement thread scheduling +} + +// Allocates a block of memory +fn alloc(size: usize) -> *mut u8 { + // TODO: implement memory allocation + ptr::null_mut() +} diff --git a/src/syscall/syscall.rs b/src/syscall/syscall.rs new file mode 100644 index 0000000..1483132 --- /dev/null +++ b/src/syscall/syscall.rs @@ -0,0 +1,44 @@ +// Define system call numbers +pub const SYSCALL_EXIT: usize = 93; +pub const SYSCALL_WRITE: usize = 64; +pub const SYSCALL_READ: usize = 63; + +// Define the system call handler function +pub fn syscall_handler( + syscall_number: usize, + arg1: usize, + arg2: usize, + arg3: usize, +) -> usize { + match syscall_number { + SYSCALL_EXIT => { + // Exit the current process + process::exit(arg1 as i32); + 0 + } + SYSCALL_WRITE => { + // Write data to a file descriptor + let fd = arg1 as i32; + let buffer = arg2 as *const u8; + let size = arg3 as usize; + match file::file_descriptor_write(fd, buffer, size) { + Ok(bytes_written) => bytes_written as usize, + Err(_) => 0, + } + } + SYSCALL_READ => { + // Read data from a file descriptor + let fd = arg1 as i32; + let buffer = arg2 as *mut u8; + let size = arg3 as usize; + match file::file_descriptor_read(fd, buffer, size) { + Ok(bytes_read) => bytes_read as usize, + Err(_) => 0, + } + } + _ => { + // Unknown system call number + 0 + } + } +} diff --git a/src/syscall/syscalls.s b/src/syscall/syscalls.s new file mode 100644 index 0000000..44b2082 --- /dev/null +++ b/src/syscall/syscalls.s @@ -0,0 +1,9 @@ +// write system call implementation +.global _write +_write: + mov %rax, %rdi // move file descriptor into rdi + mov %rsi, %rsi // move string buffer into rsi + mov %rdx, %rdx // move buffer length into rdx + mov $1, %rax // move system call number for write into rax + syscall // execute system call + ret // return from function diff --git a/src/tests/keyboard_test.rs b/src/tests/keyboard_test.rs new file mode 100644 index 0000000..5c5b693 --- /dev/null +++ b/src/tests/keyboard_test.rs @@ -0,0 +1,38 @@ +use crate::drivers::keyboard::{self, Keycode}; + +fn test_keyboard() { + let mut input_buffer = [0u8; 8]; + + // Test a single key press + let mut key_event = keyboard::KeyEvent::new(Keycode::Char('a'), false); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, ['a' as u8, 0, 0, 0, 0, 0, 0, 0]); + + // Test a single key release + key_event = keyboard::KeyEvent::new(Keycode::Char('a'), true); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, [0, 0, 0, 0, 0, 0, 0, 0]); + + // Test multiple key presses + key_event = keyboard::KeyEvent::new(Keycode::Char('a'), false); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, ['a' as u8, 0, 0, 0, 0, 0, 0, 0]); + + key_event = keyboard::KeyEvent::new(Keycode::Char('b'), false); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, ['a' as u8, 'b' as u8, 0, 0, 0, 0, 0, 0]); + + // Test multiple key releases + key_event = keyboard::KeyEvent::new(Keycode::Char('a'), true); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, ['b' as u8, 0, 0, 0, 0, 0, 0, 0]); + + key_event = keyboard::KeyEvent::new(Keycode::Char('b'), true); + keyboard::handle_key_event(&mut input_buffer, &mut key_event); + assert_eq!(input_buffer, [0, 0, 0, 0, 0, 0, 0, 0]); +} + +#[test] +fn test_keyboard_driver() { + test_keyboard(); +} diff --git a/src/tests/network_test.rs b/src/tests/network_test.rs new file mode 100644 index 0000000..cda1a50 --- /dev/null +++ b/src/tests/network_test.rs @@ -0,0 +1,16 @@ +// Import necessary modules + +#[test] +fn test_network_functionality() { + // Set up necessary network configuration for test + // e.g. create a test server, set up test client, etc. + + // Send test data or perform necessary actions + // e.g. send a message from client to server, receive response, etc. + + // Check if the received data or response is as expected + // e.g. assert that the received response is equal to the expected response + + // Tear down network configuration after test + // e.g. close server and client connections +} diff --git a/src/util/logging.rs b/src/util/logging.rs new file mode 100644 index 0000000..2007641 --- /dev/null +++ b/src/util/logging.rs @@ -0,0 +1,53 @@ +use core::fmt::{self, Write}; +use spin::Mutex; + +// An enum to represent the different levels of log messages +enum LogLevel { + Info, + Warning, + Error, +} + +// A struct to hold information about each log message +struct LogMessage { + level: LogLevel, + message: &'static str, +} + +// A simple implementation of a logger +pub struct Logger { + messages: Mutex>, +} + +impl Logger { + pub const fn new() -> Self { + Self { + messages: Mutex::new(Vec::new()), + } + } + + pub fn log_info(&self, message: &'static str) { + self.log(LogLevel::Info, message); + } + + pub fn log_warning(&self, message: &'static str) { + self.log(LogLevel::Warning, message); + } + + pub fn log_error(&self, message: &'static str) { + self.log(LogLevel::Error, message); + } + + fn log(&self, level: LogLevel, message: &'static str) { + let mut messages = self.messages.lock(); + messages.push(LogMessage { level, message }); + } +} + +// Implementing the `Write` trait to allow writing formatted strings to the logger +impl Write for Logger { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.log_info(s); + Ok(()) + } +} diff --git a/src/util/time.rs b/src/util/time.rs new file mode 100644 index 0000000..4331440 --- /dev/null +++ b/src/util/time.rs @@ -0,0 +1,17 @@ +use core::time::Duration; + +/// Get the current system time. +pub fn current_time() -> Duration { + // implementation-specific code to get the current system time + // ... +} + +/// Measure the time it takes to execute a closure. +pub fn measure_time(f: F) -> Duration + where + F: FnOnce(), +{ + let start_time = current_time(); + f(); + current_time() - start_time +}