Chapter 6-2 Introduction We gonna load our easy-fs
to kernel. First, we need to know our device interface. Second, our Inode should be wrapped in OS as OSInode for extended functionality. Then we implement sys_read/write
for it.
MMIO-Memory-Mapped I/O The device registers of peripherals can be accessed through specific physical memory addresses, VirtIO MMIO physical address range for the peripheral bus is 4KiB starting at 0X10001000.
1 2 3 4 5 6 #[cfg(feature = "board_qemu" )] pub const MMIO: &[(usize , usize )] = &[ (0x10001000 , 0x1000 ), ];
OS Inode We only take restriction on our operations with readable
and writable
by OpenFlags
. offset
and Arc
is a way to tackle simple situation of multi processes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 bitflags! { pub struct OpenFlags : u32 { const RDONLY = 0 ; const WRONLY = 1 << 0 ; const RDWR = 1 << 1 ; const CREATE = 1 << 9 ; const TRUNC = 1 << 10 ; } } impl OpenFlags { pub fn read_write (&self ) -> (bool , bool ) { if self .is_empty() { (true , false ) } else if self .contains(Self::WRONLY) { (false , true ) } else { (true , true ) } } } pub struct OSInode { readable: bool , writable: bool , inner: Mutex<OSInodeInner>, } pub struct OSInodeInner { offset: usize , inode: Arc<Inode>, } impl OSInode { pub fn new ( readable: bool , writable: bool , inode: Arc<Inode>, ) -> Self { Self { readable, writable, inner: Mutex::new(OSInodeInner { offset: 0 , inode, }), } } }
File Descriptor Table Now we need to connect file operations with process, each process need a descriptors table(which manage many files!) to indicate file record infos.
1 2 3 4 5 6 pub struct TaskControlBlockInner { pub fd_table: Vec <Option <Arc<dyn File + Send + Sync >>>, }
Remember our previous root inode? We load it directly for easy manipulation. So our workflow would be for current process, push a descriptor of allocation, and return the ptr of this allocation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 lazy_static! { pub static ref ROOT_INODE: Arc<Inode> = { let efs = EasyFileSystem::open(BLOCK_DEVICE.clone()); Arc::new(EasyFileSystem::root_inode(&efs)) }; } pub fn open_file (name: &str , flags: OpenFlags) -> Option <Arc<OSInode>> { let (readable, writable) = flags.read_write(); if flags.contains(OpenFlags::CREATE) { if let Some (inode) = ROOT_INODE.find(name) { inode.clear(); Some (Arc::new(OSInode::new( readable, writable, inode, ))) } else { ROOT_INODE.create(name) .map(|inode| { Arc::new(OSInode::new( readable, writable, inode, )) }) } } else { ROOT_INODE.find(name) .map(|inode| { if flags.contains(OpenFlags::TRUNC) { inode.clear(); } Arc::new(OSInode::new( readable, writable, inode )) }) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 pub fn sys_open (path: *const u8 , flags: u32 ) -> isize { let task = current_task().unwrap(); let token = current_user_token(); let path = translated_str(token, path); if let Some (inode) = open_file( path.as_str(), OpenFlags::from_bits(flags).unwrap() ) { let mut inner = task.acquire_inner_lock(); let fd = inner.alloc_fd(); inner.fd_table[fd] = Some (inode); fd as isize } else { -1 } } pub fn sys_close (fd: usize ) -> isize { let task = current_task().unwrap(); let mut inner = task.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1 ; } if inner.fd_table[fd].is_none() { return -1 ; } inner.fd_table[fd].take(); 0 }
The implementation is same for sys_read/write
.