diff --git a/easy-fs-fuse/.gitignore b/easy-fs-fuse/.gitignore new file mode 100644 index 00000000..79f5db68 --- /dev/null +++ b/easy-fs-fuse/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +Cargo.lock diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml new file mode 100644 index 00000000..ee0ef971 --- /dev/null +++ b/easy-fs-fuse/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "easy-fs-fuse" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = "2.33.3" +easy-fs = { path = "../easy-fs" } +rand = "0.8.0" \ No newline at end of file diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs new file mode 100644 index 00000000..cf07b618 --- /dev/null +++ b/easy-fs-fuse/src/main.rs @@ -0,0 +1,168 @@ +use easy_fs::{ + BlockDevice, + EasyFileSystem, +}; +use std::fs::{File, OpenOptions, read_dir}; +use std::io::{Read, Write, Seek, SeekFrom}; +use std::sync::Mutex; +use std::sync::Arc; +use clap::{Arg, App}; + +const BLOCK_SZ: usize = 512; + +struct BlockFile(Mutex); + +impl BlockDevice for BlockFile { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + let mut file = self.0.lock().unwrap(); + file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) + .expect("Error when seeking!"); + assert_eq!(file.read(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); + } + + fn write_block(&self, block_id: usize, buf: &[u8]) { + let mut file = self.0.lock().unwrap(); + file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64)) + .expect("Error when seeking!"); + assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); + } +} + +fn main() { + easy_fs_pack().expect("Error when packing easy-fs!"); +} + +fn easy_fs_pack() -> std::io::Result<()> { + let matches = App::new("EasyFileSystem packer") + .arg(Arg::with_name("source") + .short("s") + .long("source") + .takes_value(true) + .help("Executable source dir(with backslash)") + ) + .arg(Arg::with_name("target") + .short("t") + .long("target") + .takes_value(true) + .help("Executable target dir(with backslash)") + ) + .get_matches(); + let src_path = matches.value_of("source").unwrap(); + let target_path = matches.value_of("target").unwrap(); + println!("src_path = {}\ntarget_path = {}", src_path, target_path); + let block_file = Arc::new(BlockFile(Mutex::new({ + let f = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(format!("{}{}", target_path, "fs.img"))?; + f.set_len(16 * 2048 * 512).unwrap(); + f + }))); + // 16MiB, at most 4095 files + let efs = EasyFileSystem::create( + block_file.clone(), + 16 * 2048, + 1, + ); + let root_inode = Arc::new(EasyFileSystem::root_inode(&efs)); + let apps: Vec<_> = read_dir(src_path) + .unwrap() + .into_iter() + .map(|dir_entry| { + let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap(); + name_with_ext.drain(name_with_ext.find('.').unwrap()..name_with_ext.len()); + name_with_ext + }) + .collect(); + for app in apps { + // load app data from host file system + let mut host_file = File::open(format!("{}{}", target_path, app)).unwrap(); + let mut all_data: Vec = Vec::new(); + host_file.read_to_end(&mut all_data).unwrap(); + // create a file in easy-fs + let inode = root_inode.create(app.as_str()).unwrap(); + // write data to easy-fs + inode.write_at(0, all_data.as_slice()); + } + // list apps + for app in root_inode.ls() { + println!("{}", app); + } + Ok(()) +} + +#[test] +fn efs_test() -> std::io::Result<()> { + let block_file = Arc::new(BlockFile(Mutex::new({ + let f = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open("target/fs.img")?; + f.set_len(8192 * 512).unwrap(); + f + }))); + EasyFileSystem::create( + block_file.clone(), + 4096, + 1, + ); + let efs = EasyFileSystem::open(block_file.clone()); + let root_inode = EasyFileSystem::root_inode(&efs); + root_inode.create("filea"); + root_inode.create("fileb"); + for name in root_inode.ls() { + println!("{}", name); + } + let filea = root_inode.find("filea").unwrap(); + let greet_str = "Hello, world!"; + filea.write_at(0, greet_str.as_bytes()); + //let mut buffer = [0u8; 512]; + let mut buffer = [0u8; 233]; + let len = filea.read_at(0, &mut buffer); + assert_eq!( + greet_str, + core::str::from_utf8(&buffer[..len]).unwrap(), + ); + + let mut random_str_test = |len: usize| { + filea.clear(); + assert_eq!( + filea.read_at(0, &mut buffer), + 0, + ); + let mut str = String::new(); + use rand; + // random digit + for _ in 0..len { + str.push(char::from('0' as u8 + rand::random::() % 10)); + } + filea.write_at(0, str.as_bytes()); + let mut read_buffer = [0u8; 127]; + let mut offset = 0usize; + let mut read_str = String::new(); + loop { + let len = filea.read_at(offset, &mut read_buffer); + if len == 0 { + break; + } + offset += len; + read_str.push_str( + core::str::from_utf8(&read_buffer[..len]).unwrap() + ); + } + assert_eq!(str, read_str); + }; + + random_str_test(4 * BLOCK_SZ); + random_str_test(8 * BLOCK_SZ + BLOCK_SZ / 2); + random_str_test(100 * BLOCK_SZ); + random_str_test(70 * BLOCK_SZ + BLOCK_SZ / 7); + random_str_test((12 + 128) * BLOCK_SZ); + random_str_test(400 * BLOCK_SZ); + random_str_test(1000 * BLOCK_SZ); + random_str_test(2000 * BLOCK_SZ); + + Ok(()) +} diff --git a/easy-fs/.gitignore b/easy-fs/.gitignore new file mode 100644 index 00000000..79f5db68 --- /dev/null +++ b/easy-fs/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +Cargo.lock diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml new file mode 100644 index 00000000..6e7cd925 --- /dev/null +++ b/easy-fs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "easy-fs" +version = "0.1.0" +authors = ["Yifan Wu "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +spin = "0.7.0" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } \ No newline at end of file diff --git a/easy-fs/src/bitmap.rs b/easy-fs/src/bitmap.rs new file mode 100644 index 00000000..4feaa9cf --- /dev/null +++ b/easy-fs/src/bitmap.rs @@ -0,0 +1,73 @@ +use alloc::sync::Arc; +use super::{ + BlockDevice, + BLOCK_SZ, + get_block_cache, +}; + +type BitmapBlock = [u64; 64]; + +const BLOCK_BITS: usize = BLOCK_SZ * 8; + +pub struct Bitmap { + start_block_id: usize, + blocks: usize, +} + +/// Return (block_pos, bits64_pos, inner_pos) +fn decomposition(mut bit: usize) -> (usize, usize, usize) { + let block_pos = bit / BLOCK_BITS; + bit = bit % BLOCK_BITS; + (block_pos, bit / 64, bit % 64) +} + +impl Bitmap { + pub fn new(start_block_id: usize, blocks: usize) -> Self { + Self { + start_block_id, + blocks, + } + } + + pub fn alloc(&self, block_device: &Arc) -> Option { + for block_id in 0..self.blocks { + let pos = get_block_cache( + block_id + self.start_block_id as usize, + Arc::clone(block_device), + ).lock().modify(0, |bitmap_block: &mut BitmapBlock| { + if let Some((bits64_pos, inner_pos)) = bitmap_block + .iter() + .enumerate() + .find(|(_, bits64)| **bits64 != u64::MAX) + .map(|(bits64_pos, bits64)| { + (bits64_pos, bits64.trailing_ones() as usize) + }) { + // modify cache + bitmap_block[bits64_pos] |= 1u64 << inner_pos; + Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize) + } else { + None + } + }); + if pos.is_some() { + return pos; + } + } + None + } + + pub fn dealloc(&self, block_device: &Arc, bit: usize) { + let (block_pos, bits64_pos, inner_pos) = decomposition(bit); + get_block_cache( + block_pos + self.start_block_id, + Arc::clone(block_device) + ).lock().modify(0, |bitmap_block: &mut BitmapBlock| { + assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0); + bitmap_block[bits64_pos] -= 1u64 << inner_pos; + }); + } + + pub fn maximum(&self) -> usize { + self.blocks * BLOCK_BITS + } +} \ No newline at end of file diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs new file mode 100644 index 00000000..ba945b1d --- /dev/null +++ b/easy-fs/src/block_cache.rs @@ -0,0 +1,135 @@ +use super::{ + BLOCK_SZ, + BlockDevice, +}; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use lazy_static::*; +use spin::Mutex; + +pub struct BlockCache { + cache: [u8; BLOCK_SZ], + block_id: usize, + block_device: Arc, + modified: bool, +} + +impl BlockCache { + /// Load a new BlockCache from disk. + pub fn new( + block_id: usize, + block_device: Arc + ) -> Self { + let mut cache = [0u8; BLOCK_SZ]; + block_device.read_block(block_id, &mut cache); + Self { + cache, + block_id, + block_device, + modified: false, + } + } + + fn addr_of_offset(&self, offset: usize) -> usize { + &self.cache[offset] as *const _ as usize + } + + pub fn get_ref(&self, offset: usize) -> &T where T: Sized { + let type_size = core::mem::size_of::(); + assert!(offset + type_size <= BLOCK_SZ); + let addr = self.addr_of_offset(offset); + unsafe { &*(addr as *const T) } + } + + pub fn get_mut(&mut self, offset: usize) -> &mut T where T: Sized { + let type_size = core::mem::size_of::(); + assert!(offset + type_size <= BLOCK_SZ); + self.modified = true; + let addr = self.addr_of_offset(offset); + unsafe { &mut *(addr as *mut T) } + } + + pub fn read(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V { + f(self.get_ref(offset)) + } + + pub fn modify(&mut self, offset:usize, f: impl FnOnce(&mut T) -> V) -> V { + f(self.get_mut(offset)) + } + + pub fn sync(&mut self) { + if self.modified { + self.modified = false; + self.block_device.write_block(self.block_id, &self.cache); + } + } +} + +impl Drop for BlockCache { + fn drop(&mut self) { + self.sync() + } +} + +const BLOCK_CACHE_SIZE: usize = 16; + +pub struct BlockCacheManager { + queue: VecDeque<(usize, Arc>)>, +} + +impl BlockCacheManager { + pub fn new() -> Self { + Self { queue: VecDeque::new() } + } + + pub fn get_block_cache( + &mut self, + block_id: usize, + block_device: Arc, + ) -> Arc> { + if let Some(pair) = self.queue + .iter() + .find(|pair| pair.0 == block_id) { + Arc::clone(&pair.1) + } else { + // substitute + if self.queue.len() == BLOCK_CACHE_SIZE { + // from front to tail + if let Some((idx, _)) = self.queue + .iter() + .enumerate() + .find(|(_, pair)| Arc::strong_count(&pair.1) == 1) { + self.queue.drain(idx..=idx); + } else { + panic!("Run out of BlockCache!"); + } + } + // load block into mem and push back + let block_cache = Arc::new(Mutex::new( + BlockCache::new(block_id, Arc::clone(&block_device)) + )); + self.queue.push_back((block_id, Arc::clone(&block_cache))); + block_cache + } + } +} + +lazy_static! { + pub static ref BLOCK_CACHE_MANAGER: Mutex = Mutex::new( + BlockCacheManager::new() + ); +} + +pub fn get_block_cache( + block_id: usize, + block_device: Arc +) -> Arc> { + BLOCK_CACHE_MANAGER.lock().get_block_cache(block_id, block_device) +} + +pub fn block_cache_sync_all() { + let manager = BLOCK_CACHE_MANAGER.lock(); + for (_, cache) in manager.queue.iter() { + cache.lock().sync(); + } +} diff --git a/easy-fs/src/block_dev.rs b/easy-fs/src/block_dev.rs new file mode 100644 index 00000000..7a282751 --- /dev/null +++ b/easy-fs/src/block_dev.rs @@ -0,0 +1,6 @@ +use core::any::Any; + +pub trait BlockDevice : Send + Sync + Any { + fn read_block(&self, block_id: usize, buf: &mut [u8]); + fn write_block(&self, block_id: usize, buf: &[u8]); +} diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs new file mode 100644 index 00000000..8b7adf26 --- /dev/null +++ b/easy-fs/src/efs.rs @@ -0,0 +1,163 @@ +use alloc::sync::Arc; +use spin::Mutex; +use super::{ + BlockDevice, + Bitmap, + SuperBlock, + DiskInode, + DiskInodeType, + Inode, + get_block_cache, + block_cache_sync_all, +}; +use crate::BLOCK_SZ; + +pub struct EasyFileSystem { + pub block_device: Arc, + pub inode_bitmap: Bitmap, + pub data_bitmap: Bitmap, + inode_area_start_block: u32, + data_area_start_block: u32, +} + +type DataBlock = [u8; BLOCK_SZ]; + +impl EasyFileSystem { + pub fn create( + block_device: Arc, + total_blocks: u32, + inode_bitmap_blocks: u32, + ) -> Arc> { + // calculate block size of areas & create bitmaps + let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize); + let inode_num = inode_bitmap.maximum(); + let inode_area_blocks = + ((inode_num * core::mem::size_of::() + BLOCK_SZ - 1) / BLOCK_SZ) as u32; + let inode_total_blocks = inode_bitmap_blocks + inode_area_blocks; + let data_total_blocks = total_blocks - 1 - inode_total_blocks; + let data_bitmap_blocks = (data_total_blocks + 4096) / 4097; + let data_area_blocks = data_total_blocks - data_bitmap_blocks; + let data_bitmap = Bitmap::new( + (1 + inode_bitmap_blocks + inode_area_blocks) as usize, + data_bitmap_blocks as usize, + ); + let mut efs = Self { + block_device: Arc::clone(&block_device), + inode_bitmap, + data_bitmap, + inode_area_start_block: 1 + inode_bitmap_blocks, + data_area_start_block: 1 + inode_total_blocks + data_bitmap_blocks, + }; + // clear all blocks + for i in 0..total_blocks { + get_block_cache( + i as usize, + Arc::clone(&block_device) + ) + .lock() + .modify(0, |data_block: &mut DataBlock| { + for byte in data_block.iter_mut() { *byte = 0; } + }); + } + // initialize SuperBlock + get_block_cache(0, Arc::clone(&block_device)) + .lock() + .modify(0, |super_block: &mut SuperBlock| { + super_block.initialize( + total_blocks, + inode_bitmap_blocks, + inode_area_blocks, + data_bitmap_blocks, + data_area_blocks, + ); + }); + // write back immediately + // create a inode for root node "/" + assert_eq!(efs.alloc_inode(), 0); + let (root_inode_block_id, root_inode_offset) = efs.get_disk_inode_pos(0); + get_block_cache( + root_inode_block_id as usize, + Arc::clone(&block_device) + ) + .lock() + .modify(root_inode_offset, |disk_inode: &mut DiskInode| { + disk_inode.initialize(DiskInodeType::Directory); + }); + block_cache_sync_all(); + Arc::new(Mutex::new(efs)) + } + + pub fn open(block_device: Arc) -> Arc> { + // read SuperBlock + get_block_cache(0, Arc::clone(&block_device)) + .lock() + .read(0, |super_block: &SuperBlock| { + assert!(super_block.is_valid(), "Error loading EFS!"); + let inode_total_blocks = + super_block.inode_bitmap_blocks + super_block.inode_area_blocks; + let efs = Self { + block_device, + inode_bitmap: Bitmap::new( + 1, + super_block.inode_bitmap_blocks as usize + ), + data_bitmap: Bitmap::new( + (1 + inode_total_blocks) as usize, + super_block.data_bitmap_blocks as usize, + ), + inode_area_start_block: 1 + super_block.inode_bitmap_blocks, + data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks, + }; + Arc::new(Mutex::new(efs)) + }) + } + + pub fn root_inode(efs: &Arc>) -> Inode { + let block_device = Arc::clone(&efs.lock().block_device); + // acquire efs lock temporarily + let (block_id, block_offset) = efs.lock().get_disk_inode_pos(0); + // release efs lock + Inode::new( + block_id, + block_offset, + Arc::clone(efs), + block_device, + ) + } + + pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) { + let inode_size = core::mem::size_of::(); + let inodes_per_block = (BLOCK_SZ / inode_size) as u32; + let block_id = self.inode_area_start_block + inode_id / inodes_per_block; + (block_id, (inode_id % inodes_per_block) as usize * inode_size) + } + + pub fn get_data_block_id(&self, data_block_id: u32) -> u32 { + self.data_area_start_block + data_block_id + } + + pub fn alloc_inode(&mut self) -> u32 { + self.inode_bitmap.alloc(&self.block_device).unwrap() as u32 + } + + /// Return a block ID not ID in the data area. + pub fn alloc_data(&mut self) -> u32 { + self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block + } + + pub fn dealloc_data(&mut self, block_id: u32) { + get_block_cache( + block_id as usize, + Arc::clone(&self.block_device) + ) + .lock() + .modify(0, |data_block: &mut DataBlock| { + data_block.iter_mut().for_each(|p| { *p = 0; }) + }); + self.data_bitmap.dealloc( + &self.block_device, + (block_id - self.data_area_start_block) as usize + ) + } + +} \ No newline at end of file diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs new file mode 100644 index 00000000..c6f7b315 --- /dev/null +++ b/easy-fs/src/layout.rs @@ -0,0 +1,448 @@ +use core::fmt::{Debug, Formatter, Result}; +use super::{ + BLOCK_SZ, + BlockDevice, + get_block_cache, +}; +use alloc::sync::Arc; +use alloc::vec::Vec; + +const EFS_MAGIC: u32 = 0x3b800001; +const INODE_DIRECT_COUNT: usize = 28; +const NAME_LENGTH_LIMIT: usize = 27; +const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4; +const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT; +const DIRECT_BOUND: usize = INODE_DIRECT_COUNT; +const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT; +#[allow(unused)] +const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT; + +#[repr(C)] +pub struct SuperBlock { + magic: u32, + pub total_blocks: u32, + pub inode_bitmap_blocks: u32, + pub inode_area_blocks: u32, + pub data_bitmap_blocks: u32, + pub data_area_blocks: u32, +} + +impl Debug for SuperBlock { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("SuperBlock") + .field("total_blocks", &self.total_blocks) + .field("inode_bitmap_blocks", &self.inode_bitmap_blocks) + .field("inode_area_blocks", &self.inode_area_blocks) + .field("data_bitmap_blocks", &self.data_bitmap_blocks) + .field("data_area_blocks", &self.data_area_blocks) + .finish() + } +} + +impl SuperBlock { + pub fn initialize( + &mut self, + total_blocks: u32, + inode_bitmap_blocks: u32, + inode_area_blocks: u32, + data_bitmap_blocks: u32, + data_area_blocks: u32, + ) { + *self = Self { + magic: EFS_MAGIC, + total_blocks, + inode_bitmap_blocks, + inode_area_blocks, + data_bitmap_blocks, + data_area_blocks, + } + } + pub fn is_valid(&self) -> bool { + self.magic == EFS_MAGIC + } +} + +#[derive(PartialEq)] +pub enum DiskInodeType { + File, + Directory, +} + +type IndirectBlock = [u32; BLOCK_SZ / 4]; +type DataBlock = [u8; BLOCK_SZ]; + +#[repr(C)] +pub struct DiskInode { + pub size: u32, + pub direct: [u32; INODE_DIRECT_COUNT], + pub indirect1: u32, + pub indirect2: u32, + type_: DiskInodeType, +} + +impl DiskInode { + /// indirect1 and indirect2 block are allocated only when they are needed. + pub fn initialize(&mut self, type_: DiskInodeType) { + self.size = 0; + self.direct.iter_mut().for_each(|v| *v = 0); + self.indirect1 = 0; + self.indirect2 = 0; + self.type_ = type_; + } + pub fn is_dir(&self) -> bool { + self.type_ == DiskInodeType::Directory + } + #[allow(unused)] + pub fn is_file(&self) -> bool { + self.type_ == DiskInodeType::File + } + /// Return block number correspond to size. + pub fn data_blocks(&self) -> u32 { + Self::_data_blocks(self.size) + } + fn _data_blocks(size: u32) -> u32 { + (size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32 + } + /// Return number of blocks needed include indirect1/2. + pub fn total_blocks(size: u32) -> u32 { + let data_blocks = Self::_data_blocks(size) as usize; + let mut total = data_blocks as usize; + // indirect1 + if data_blocks > INODE_DIRECT_COUNT { + total += 1; + } + // indirect2 + if data_blocks > INDIRECT1_BOUND { + total += 1; + // sub indirect1 + total += (data_blocks - INDIRECT1_BOUND + INODE_INDIRECT1_COUNT - 1) / INODE_INDIRECT1_COUNT; + } + total as u32 + } + pub fn blocks_num_needed(&self, new_size: u32) -> u32 { + assert!(new_size >= self.size); + Self::total_blocks(new_size) - Self::total_blocks(self.size) + } + pub fn get_block_id(&self, inner_id: u32, block_device: &Arc) -> u32 { + let inner_id = inner_id as usize; + if inner_id < INODE_DIRECT_COUNT { + self.direct[inner_id] + } else if inner_id < INDIRECT1_BOUND { + get_block_cache(self.indirect1 as usize, Arc::clone(block_device)) + .lock() + .read(0, |indirect_block: &IndirectBlock| { + indirect_block[inner_id - INODE_DIRECT_COUNT] + }) + } else { + let last = inner_id - INDIRECT1_BOUND; + let indirect1 = get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device) + ) + .lock() + .read(0, |indirect2: &IndirectBlock| { + indirect2[last / INODE_INDIRECT1_COUNT] + }); + get_block_cache( + indirect1 as usize, + Arc::clone(block_device) + ) + .lock() + .read(0, |indirect1: &IndirectBlock| { + indirect1[last % INODE_INDIRECT1_COUNT] + }) + } + } + pub fn increase_size( + &mut self, + new_size: u32, + new_blocks: Vec, + block_device: &Arc, + ) { + let mut current_blocks = self.data_blocks(); + self.size = new_size; + let mut total_blocks = self.data_blocks(); + let mut new_blocks = new_blocks.into_iter(); + // fill direct + while current_blocks < total_blocks.min(INODE_DIRECT_COUNT as u32) { + self.direct[current_blocks as usize] = new_blocks.next().unwrap(); + current_blocks += 1; + } + // alloc indirect1 + if total_blocks > INODE_DIRECT_COUNT as u32{ + if current_blocks == INODE_DIRECT_COUNT as u32 { + self.indirect1 = new_blocks.next().unwrap(); + } + current_blocks -= INODE_DIRECT_COUNT as u32; + total_blocks -= INODE_DIRECT_COUNT as u32; + } else { + return; + } + // fill indirect1 + get_block_cache( + self.indirect1 as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + while current_blocks < total_blocks.min(INODE_INDIRECT1_COUNT as u32) { + indirect1[current_blocks as usize] = new_blocks.next().unwrap(); + current_blocks += 1; + } + }); + // alloc indirect2 + if total_blocks > INODE_INDIRECT1_COUNT as u32 { + if current_blocks == INODE_INDIRECT1_COUNT as u32 { + self.indirect2 = new_blocks.next().unwrap(); + } + current_blocks -= INODE_INDIRECT1_COUNT as u32; + total_blocks -= INODE_INDIRECT1_COUNT as u32; + } else { + return; + } + // fill indirect2 from (a0, b0) -> (a1, b1) + let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT; + let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT; + let a1 = total_blocks as usize / INODE_INDIRECT1_COUNT; + let b1 = total_blocks as usize % INODE_INDIRECT1_COUNT; + // alloc low-level indirect1 + get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |indirect2: &mut IndirectBlock| { + while (a0 < a1) || (a0 == a1 && b0 < b1) { + if b0 == 0 { + indirect2[a0] = new_blocks.next().unwrap(); + } + // fill current + get_block_cache( + indirect2[a0] as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + indirect1[b0] = new_blocks.next().unwrap(); + }); + // move to next + b0 += 1; + if b0 == INODE_INDIRECT1_COUNT { + b0 = 0; + a0 += 1; + } + } + }); + } + + /// Clear size to zero and return blocks that should be deallocated. + /// + /// We will clear the block contents to zero later. + pub fn clear_size(&mut self, block_device: &Arc) -> Vec { + let mut v: Vec = Vec::new(); + let mut data_blocks = self.data_blocks() as usize; + self.size = 0; + let mut current_blocks = 0usize; + // direct + while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) { + v.push(self.direct[current_blocks]); + self.direct[current_blocks] = 0; + current_blocks += 1; + } + // indirect1 block + if data_blocks > INODE_DIRECT_COUNT { + v.push(self.indirect1); + data_blocks -= INODE_DIRECT_COUNT; + current_blocks = 0; + } else { + return v; + } + // indirect1 + get_block_cache( + self.indirect1 as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) { + v.push(indirect1[current_blocks]); + //indirect1[current_blocks] = 0; + current_blocks += 1; + } + }); + self.indirect1 = 0; + // indirect2 block + if data_blocks > INODE_INDIRECT1_COUNT { + v.push(self.indirect2); + data_blocks -= INODE_INDIRECT1_COUNT; + } else { + return v; + } + // indirect2 + assert!(data_blocks <= INODE_INDIRECT2_COUNT); + let a1 = data_blocks / INODE_INDIRECT1_COUNT; + let b1 = data_blocks % INODE_INDIRECT1_COUNT; + get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect2: &mut IndirectBlock| { + // full indirect1 blocks + for i in 0..a1 { + v.push(indirect2[i]); + get_block_cache( + indirect2[i] as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + for j in 0..INODE_INDIRECT1_COUNT { + v.push(indirect1[j]); + //indirect1[j] = 0; + } + }); + //indirect2[i] = 0; + } + // last indirect1 block + if b1 > 0 { + v.push(indirect2[a1]); + get_block_cache( + indirect2[a1] as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + for j in 0..b1 { + v.push(indirect1[j]); + //indirect1[j] = 0; + } + }); + //indirect2[a1] = 0; + } + }); + self.indirect2 = 0; + v + } + pub fn read_at( + &self, + offset: usize, + buf: &mut [u8], + block_device: &Arc, + ) -> usize { + let mut start = offset; + let end = (offset + buf.len()).min(self.size as usize); + if start >= end { + return 0; + } + let mut start_block = start / BLOCK_SZ; + let mut read_size = 0usize; + loop { + // calculate end of current block + let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; + end_current_block = end_current_block.min(end); + // read and update read size + let block_read_size = end_current_block - start; + let dst = &mut buf[read_size..read_size + block_read_size]; + get_block_cache( + self.get_block_id(start_block as u32, block_device) as usize, + Arc::clone(block_device), + ) + .lock() + .read(0, |data_block: &DataBlock| { + let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size]; + dst.copy_from_slice(src); + }); + read_size += block_read_size; + // move to next block + if end_current_block == end { break; } + start_block += 1; + start = end_current_block; + } + read_size + } + /// File size must be adjusted before. + pub fn write_at( + &mut self, + offset: usize, + buf: &[u8], + block_device: &Arc, + ) -> usize { + let mut start = offset; + let end = (offset + buf.len()).min(self.size as usize); + assert!(start <= end); + let mut start_block = start / BLOCK_SZ; + let mut write_size = 0usize; + loop { + // calculate end of current block + let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ; + end_current_block = end_current_block.min(end); + // write and update write size + let block_write_size = end_current_block - start; + get_block_cache( + self.get_block_id(start_block as u32, block_device) as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |data_block: &mut DataBlock| { + let src = &buf[write_size..write_size + block_write_size]; + let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size]; + dst.copy_from_slice(src); + }); + write_size += block_write_size; + // move to next block + if end_current_block == end { break; } + start_block += 1; + start = end_current_block; + } + write_size + } +} + +#[repr(C)] +pub struct DirEntry { + name: [u8; NAME_LENGTH_LIMIT + 1], + inode_number: u32, +} + +pub const DIRENT_SZ: usize = 32; + +impl DirEntry { + pub fn empty() -> Self { + Self { + name: [0u8; NAME_LENGTH_LIMIT + 1], + inode_number: 0, + } + } + pub fn new(name: &str, inode_number: u32) -> Self { + let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1]; + bytes[..name.len()].copy_from_slice(name.as_bytes()); + Self { + name: bytes, + inode_number, + } + } + pub fn as_bytes(&self) -> &[u8] { + unsafe { + core::slice::from_raw_parts( + self as *const _ as usize as *const u8, + DIRENT_SZ, + ) + } + } + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + unsafe { + core::slice::from_raw_parts_mut( + self as *mut _ as usize as *mut u8, + DIRENT_SZ, + ) + } + } + pub fn name(&self) -> &str { + let len = (0usize..).find(|i| self.name[*i] == 0).unwrap(); + core::str::from_utf8(&self.name[..len]).unwrap() + } + pub fn inode_number(&self) -> u32 { + self.inode_number + } +} \ No newline at end of file diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs new file mode 100644 index 00000000..afb957ac --- /dev/null +++ b/easy-fs/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] + +extern crate alloc; + +mod block_dev; +mod layout; +mod efs; +mod bitmap; +mod vfs; +mod block_cache; + +pub const BLOCK_SZ: usize = 512; +pub use block_dev::BlockDevice; +pub use efs::EasyFileSystem; +pub use vfs::Inode; +use layout::*; +use bitmap::Bitmap; +use block_cache::{get_block_cache, block_cache_sync_all}; \ No newline at end of file diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs new file mode 100644 index 00000000..9534c39a --- /dev/null +++ b/easy-fs/src/vfs.rs @@ -0,0 +1,210 @@ +use super::{ + BlockDevice, + DiskInode, + DiskInodeType, + DirEntry, + EasyFileSystem, + DIRENT_SZ, + get_block_cache, + block_cache_sync_all, +}; +use alloc::sync::Arc; +use alloc::string::String; +use alloc::vec::Vec; +use spin::{Mutex, MutexGuard}; + +pub struct Inode { + block_id: usize, + block_offset: usize, + fs: Arc>, + block_device: Arc, +} + +impl Inode { + /// We should not acquire efs lock here. + pub fn new( + block_id: u32, + block_offset: usize, + fs: Arc>, + block_device: Arc, + ) -> Self { + Self { + block_id: block_id as usize, + block_offset, + fs, + block_device, + } + } + + fn read_disk_inode(&self, f: impl FnOnce(&DiskInode) -> V) -> V { + get_block_cache( + self.block_id, + Arc::clone(&self.block_device) + ).lock().read(self.block_offset, f) + } + + fn modify_disk_inode(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V { + get_block_cache( + self.block_id, + Arc::clone(&self.block_device) + ).lock().modify(self.block_offset, f) + } + + fn find_inode_id( + &self, + name: &str, + disk_inode: &DiskInode, + ) -> Option { + // assert it is a directory + assert!(disk_inode.is_dir()); + let file_count = (disk_inode.size as usize) / DIRENT_SZ; + let mut dirent = DirEntry::empty(); + for i in 0..file_count { + assert_eq!( + disk_inode.read_at( + DIRENT_SZ * i, + dirent.as_bytes_mut(), + &self.block_device, + ), + DIRENT_SZ, + ); + if dirent.name() == name { + return Some(dirent.inode_number() as u32); + } + } + None + } + + pub fn find(&self, name: &str) -> Option> { + let fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| { + self.find_inode_id(name, disk_inode) + .map(|inode_id| { + let (block_id, block_offset) = fs.get_disk_inode_pos(inode_id); + Arc::new(Self::new( + block_id, + block_offset, + self.fs.clone(), + self.block_device.clone(), + )) + }) + }) + } + + fn increase_size( + &self, + new_size: u32, + disk_inode: &mut DiskInode, + fs: &mut MutexGuard, + ) { + if new_size < disk_inode.size { + return; + } + let blocks_needed = disk_inode.blocks_num_needed(new_size); + let mut v: Vec = Vec::new(); + for _ in 0..blocks_needed { + v.push(fs.alloc_data()); + } + disk_inode.increase_size(new_size, v, &self.block_device); + } + + pub fn create(&self, name: &str) -> Option> { + let mut fs = self.fs.lock(); + if self.modify_disk_inode(|root_inode| { + // assert it is a directory + assert!(root_inode.is_dir()); + // has the file been created? + self.find_inode_id(name, root_inode) + }).is_some() { + return None; + } + // create a new file + // alloc a inode with an indirect block + let new_inode_id = fs.alloc_inode(); + // initialize inode + let (new_inode_block_id, new_inode_block_offset) + = fs.get_disk_inode_pos(new_inode_id); + get_block_cache( + new_inode_block_id as usize, + Arc::clone(&self.block_device) + ).lock().modify(new_inode_block_offset, |new_inode: &mut DiskInode| { + new_inode.initialize(DiskInodeType::File); + }); + self.modify_disk_inode(|root_inode| { + // append file in the dirent + let file_count = (root_inode.size as usize) / DIRENT_SZ; + let new_size = (file_count + 1) * DIRENT_SZ; + // increase size + self.increase_size(new_size as u32, root_inode, &mut fs); + // write dirent + let dirent = DirEntry::new(name, new_inode_id); + root_inode.write_at( + file_count * DIRENT_SZ, + dirent.as_bytes(), + &self.block_device, + ); + }); + + let (block_id, block_offset) = fs.get_disk_inode_pos(new_inode_id); + block_cache_sync_all(); + // return inode + Some(Arc::new(Self::new( + block_id, + block_offset, + self.fs.clone(), + self.block_device.clone(), + ))) + // release efs lock automatically by compiler + } + + pub fn ls(&self) -> Vec { + let _fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| { + let file_count = (disk_inode.size as usize) / DIRENT_SZ; + let mut v: Vec = Vec::new(); + for i in 0..file_count { + let mut dirent = DirEntry::empty(); + assert_eq!( + disk_inode.read_at( + i * DIRENT_SZ, + dirent.as_bytes_mut(), + &self.block_device, + ), + DIRENT_SZ, + ); + v.push(String::from(dirent.name())); + } + v + }) + } + + pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize { + let _fs = self.fs.lock(); + self.read_disk_inode(|disk_inode| { + disk_inode.read_at(offset, buf, &self.block_device) + }) + } + + pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize { + let mut fs = self.fs.lock(); + let size = self.modify_disk_inode(|disk_inode| { + self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs); + disk_inode.write_at(offset, buf, &self.block_device) + }); + block_cache_sync_all(); + size + } + + pub fn clear(&self) { + let mut fs = self.fs.lock(); + self.modify_disk_inode(|disk_inode| { + let size = disk_inode.size; + let data_blocks_dealloc = disk_inode.clear_size(&self.block_device); + assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize); + for data_block in data_blocks_dealloc.into_iter() { + fs.dealloc_data(data_block); + } + }); + block_cache_sync_all(); + } +} diff --git a/os/Cargo.toml b/os/Cargo.toml index f8cbacdc..b07fa034 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -12,6 +12,11 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] } buddy_system_allocator = "0.6" bitflags = "1.2.1" xmas-elf = "0.7.0" +virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" } +k210-pac = { git = "https://github.com/wyfcyx/k210-pac" } +k210-hal = { git = "https://github.com/wyfcyx/k210-hal" } +k210-soc = { git = "https://github.com/wyfcyx/k210-soc" } +easy-fs = { path = "../easy-fs" } [features] board_qemu = [] diff --git a/os/Makefile b/os/Makefile index 505d37ec..0c1fb7c7 100644 --- a/os/Makefile +++ b/os/Makefile @@ -4,6 +4,9 @@ MODE := release KERNEL_ELF := target/$(TARGET)/$(MODE)/os KERNEL_BIN := $(KERNEL_ELF).bin DISASM_TMP := target/$(TARGET)/$(MODE)/asm +FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img +SDCARD := /dev/sdb +APPS := ../user/src/bin/* # BOARD BOARD ?= qemu @@ -20,7 +23,7 @@ endif # Run K210 K210-SERIALPORT = /dev/ttyUSB0 -K210-BURNER = ../tools/kflash.py +K210-BURNER = ../tools/kflash.py # Binutils OBJDUMP := rust-objdump --arch-name=riscv64 @@ -29,7 +32,7 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64 # Disassembly DISASM ?= -x -build: env switch-check $(KERNEL_BIN) +build: env switch-check $(KERNEL_BIN) fs-img switch-check: ifeq ($(BOARD), qemu) @@ -44,11 +47,22 @@ env: rustup component add rust-src rustup component add llvm-tools-preview +sdcard: fs-img + @echo "Are you sure write to $(SDCARD) ? [y/N] " && read ans && [ $${ans:-N} = y ] + @sudo dd if=/dev/zero of=$(SDCARD) bs=1048576 count=32 + @sudo dd if=$(FS_IMG) of=$(SDCARD) + $(KERNEL_BIN): kernel @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ -kernel: +fs-img: $(APPS) @cd ../user && make build + @rm -f $(FS_IMG) + @cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/ + +$(APPS): + +kernel: @echo Platform: $(BOARD) @cp src/linker-$(BOARD).ld src/linker.ld @cargo build --release --features "board_$(BOARD)" @@ -67,15 +81,15 @@ disasm-vim: kernel run: run-inner - - run-inner: build ifeq ($(BOARD),qemu) @qemu-system-riscv64 \ -machine virt \ -nographic \ -bios $(BOOTLOADER) \ - -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) + -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ + -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 else (which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools) @cp $(BOOTLOADER) $(BOOTLOADER).copy @@ -92,4 +106,4 @@ debug: build tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ tmux -2 attach-session -d -.PHONY: build env kernel clean disasm disasm-vim run-inner switch-check +.PHONY: build env kernel clean disasm disasm-vim run-inner switch-check fs-img diff --git a/os/build.rs b/os/build.rs index e32fa7ab..5529b4fe 100644 --- a/os/build.rs +++ b/os/build.rs @@ -1,56 +1,6 @@ -use std::io::{Result, Write}; -use std::fs::{File, read_dir}; +static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/"; fn main() { println!("cargo:rerun-if-changed=../user/src/"); println!("cargo:rerun-if-changed={}", TARGET_PATH); - insert_app_data().unwrap(); } - -static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/"; - -fn insert_app_data() -> Result<()> { - let mut f = File::create("src/link_app.S").unwrap(); - let mut apps: Vec<_> = read_dir("../user/src/bin") - .unwrap() - .into_iter() - .map(|dir_entry| { - let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap(); - name_with_ext.drain(name_with_ext.find('.').unwrap()..name_with_ext.len()); - name_with_ext - }) - .collect(); - apps.sort(); - - writeln!(f, r#" - .align 3 - .section .data - .global _num_app -_num_app: - .quad {}"#, apps.len())?; - - for i in 0..apps.len() { - writeln!(f, r#" .quad app_{}_start"#, i)?; - } - writeln!(f, r#" .quad app_{}_end"#, apps.len() - 1)?; - - writeln!(f, r#" - .global _app_names -_app_names:"#)?; - for app in apps.iter() { - writeln!(f, r#" .string "{}""#, app)?; - } - - for (idx, app) in apps.iter().enumerate() { - println!("app_{}: {}", idx, app); - writeln!(f, r#" - .section .data - .global app_{0}_start - .global app_{0}_end - .align 3 -app_{0}_start: - .incbin "{2}{1}" -app_{0}_end:"#, idx, app, TARGET_PATH)?; - } - Ok(()) -} \ No newline at end of file diff --git a/os/src/config.rs b/os/src/config.rs index 852800be..dce95ec4 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -1,13 +1,9 @@ +#[allow(unused)] + pub const USER_STACK_SIZE: usize = 4096 * 2; pub const KERNEL_STACK_SIZE: usize = 4096 * 2; pub const KERNEL_HEAP_SIZE: usize = 0x20_0000; - -#[cfg(feature = "board_k210")] -pub const MEMORY_END: usize = 0x80600000; - -#[cfg(feature = "board_qemu")] pub const MEMORY_END: usize = 0x80800000; - pub const PAGE_SIZE: usize = 0x1000; pub const PAGE_SIZE_BITS: usize = 0xc; @@ -19,3 +15,28 @@ pub const CLOCK_FREQ: usize = 403000000 / 62; #[cfg(feature = "board_qemu")] pub const CLOCK_FREQ: usize = 12500000; + +#[cfg(feature = "board_qemu")] +pub const MMIO: &[(usize, usize)] = &[ + (0x10001000, 0x1000), +]; + +#[cfg(feature = "board_k210")] +pub const MMIO: &[(usize, usize)] = &[ + // we don't need clint in S priv when running + // we only need claim/complete for target0 after initializing + (0x0C00_0000, 0x3000), /* PLIC */ + (0x0C20_0000, 0x1000), /* PLIC */ + (0x3800_0000, 0x1000), /* UARTHS */ + (0x3800_1000, 0x1000), /* GPIOHS */ + (0x5020_0000, 0x1000), /* GPIO */ + (0x5024_0000, 0x1000), /* SPI_SLAVE */ + (0x502B_0000, 0x1000), /* FPIOA */ + (0x502D_0000, 0x1000), /* TIMER0 */ + (0x502E_0000, 0x1000), /* TIMER1 */ + (0x502F_0000, 0x1000), /* TIMER2 */ + (0x5044_0000, 0x1000), /* SYSCTL */ + (0x5200_0000, 0x1000), /* SPI0 */ + (0x5300_0000, 0x1000), /* SPI1 */ + (0x5400_0000, 0x1000), /* SPI2 */ +]; \ No newline at end of file diff --git a/os/src/drivers/block/mod.rs b/os/src/drivers/block/mod.rs new file mode 100644 index 00000000..f79b8b81 --- /dev/null +++ b/os/src/drivers/block/mod.rs @@ -0,0 +1,30 @@ +mod virtio_blk; +mod sdcard; + +use lazy_static::*; +use alloc::sync::Arc; +use easy_fs::BlockDevice; + +#[cfg(feature = "board_qemu")] +type BlockDeviceImpl = virtio_blk::VirtIOBlock; + +#[cfg(feature = "board_k210")] +type BlockDeviceImpl = sdcard::SDCardWrapper; + +lazy_static! { + pub static ref BLOCK_DEVICE: Arc = Arc::new(BlockDeviceImpl::new()); +} + +#[allow(unused)] +pub fn block_device_test() { + let block_device = BLOCK_DEVICE.clone(); + let mut write_buffer = [0u8; 512]; + let mut read_buffer = [0u8; 512]; + for i in 0..512 { + for byte in write_buffer.iter_mut() { *byte = i as u8; } + block_device.write_block(i as usize, &write_buffer); + block_device.read_block(i as usize, &mut read_buffer); + assert_eq!(write_buffer, read_buffer); + } + println!("block device test passed!"); +} \ No newline at end of file diff --git a/os/src/drivers/block/sdcard.rs b/os/src/drivers/block/sdcard.rs new file mode 100644 index 00000000..e550cc01 --- /dev/null +++ b/os/src/drivers/block/sdcard.rs @@ -0,0 +1,755 @@ +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(unused)] + +use k210_pac::{Peripherals, SPI0}; +use k210_hal::prelude::*; +use k210_soc::{ + //dmac::{dma_channel, DMAC, DMACExt}, + gpio, + gpiohs, + spi::{aitm, frame_format, tmod, work_mode, SPI, SPIExt, SPIImpl}, + fpioa::{self, io}, + sysctl, + sleep::usleep, +}; +use crate::sync::UPSafeCell; +use lazy_static::*; +use super::BlockDevice; +use core::convert::TryInto; + +pub struct SDCard { + spi: SPI, + spi_cs: u32, + cs_gpionum: u8, + //dmac: &'a DMAC, + //channel: dma_channel, +} + +/* + * Start Data tokens: + * Tokens (necessary because at nop/idle (and CS active) only 0xff is + * on the data/command line) + */ +/** Data token start byte, Start Single Block Read */ +pub const SD_START_DATA_SINGLE_BLOCK_READ: u8 = 0xFE; +/** Data token start byte, Start Multiple Block Read */ +pub const SD_START_DATA_MULTIPLE_BLOCK_READ: u8 = 0xFE; +/** Data token start byte, Start Single Block Write */ +pub const SD_START_DATA_SINGLE_BLOCK_WRITE: u8 = 0xFE; +/** Data token start byte, Start Multiple Block Write */ +pub const SD_START_DATA_MULTIPLE_BLOCK_WRITE: u8 = 0xFC; + +pub const SEC_LEN: usize = 512; + +/** SD commands */ +#[repr(u8)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[allow(unused)] +pub enum CMD { + /** Software reset */ + CMD0 = 0, + /** Check voltage range (SDC V2) */ + CMD8 = 8, + /** Read CSD register */ + CMD9 = 9, + /** Read CID register */ + CMD10 = 10, + /** Stop to read data */ + CMD12 = 12, + /** Change R/W block size */ + CMD16 = 16, + /** Read block */ + CMD17 = 17, + /** Read multiple blocks */ + CMD18 = 18, + /** Number of blocks to erase (SDC) */ + ACMD23 = 23, + /** Write a block */ + CMD24 = 24, + /** Write multiple blocks */ + CMD25 = 25, + /** Initiate initialization process (SDC) */ + ACMD41 = 41, + /** Leading command for ACMD* */ + CMD55 = 55, + /** Read OCR */ + CMD58 = 58, + /** Enable/disable CRC check */ + CMD59 = 59, +} + +#[allow(unused)] +#[derive(Debug, Copy, Clone)] +pub enum InitError { + CMDFailed(CMD, u8), + CardCapacityStatusNotSet([u8; 4]), + CannotGetCardInfo, +} + +/** + * Card Specific Data: CSD Register + */ +#[derive(Debug, Copy, Clone)] +pub struct SDCardCSD { + pub CSDStruct: u8, /* CSD structure */ + pub SysSpecVersion: u8, /* System specification version */ + pub Reserved1: u8, /* Reserved */ + pub TAAC: u8, /* Data read access-time 1 */ + pub NSAC: u8, /* Data read access-time 2 in CLK cycles */ + pub MaxBusClkFrec: u8, /* Max. bus clock frequency */ + pub CardComdClasses: u16, /* Card command classes */ + pub RdBlockLen: u8, /* Max. read data block length */ + pub PartBlockRead: u8, /* Partial blocks for read allowed */ + pub WrBlockMisalign: u8, /* Write block misalignment */ + pub RdBlockMisalign: u8, /* Read block misalignment */ + pub DSRImpl: u8, /* DSR implemented */ + pub Reserved2: u8, /* Reserved */ + pub DeviceSize: u32, /* Device Size */ + //MaxRdCurrentVDDMin: u8, /* Max. read current @ VDD min */ + //MaxRdCurrentVDDMax: u8, /* Max. read current @ VDD max */ + //MaxWrCurrentVDDMin: u8, /* Max. write current @ VDD min */ + //MaxWrCurrentVDDMax: u8, /* Max. write current @ VDD max */ + //DeviceSizeMul: u8, /* Device size multiplier */ + pub EraseGrSize: u8, /* Erase group size */ + pub EraseGrMul: u8, /* Erase group size multiplier */ + pub WrProtectGrSize: u8, /* Write protect group size */ + pub WrProtectGrEnable: u8, /* Write protect group enable */ + pub ManDeflECC: u8, /* Manufacturer default ECC */ + pub WrSpeedFact: u8, /* Write speed factor */ + pub MaxWrBlockLen: u8, /* Max. write data block length */ + pub WriteBlockPaPartial: u8, /* Partial blocks for write allowed */ + pub Reserved3: u8, /* Reserded */ + pub ContentProtectAppli: u8, /* Content protection application */ + pub FileFormatGroup: u8, /* File format group */ + pub CopyFlag: u8, /* Copy flag (OTP) */ + pub PermWrProtect: u8, /* Permanent write protection */ + pub TempWrProtect: u8, /* Temporary write protection */ + pub FileFormat: u8, /* File Format */ + pub ECC: u8, /* ECC code */ + pub CSD_CRC: u8, /* CSD CRC */ + pub Reserved4: u8, /* always 1*/ +} + +/** + * Card Identification Data: CID Register + */ +#[derive(Debug, Copy, Clone)] +pub struct SDCardCID { + pub ManufacturerID: u8, /* ManufacturerID */ + pub OEM_AppliID: u16, /* OEM/Application ID */ + pub ProdName1: u32, /* Product Name part1 */ + pub ProdName2: u8, /* Product Name part2*/ + pub ProdRev: u8, /* Product Revision */ + pub ProdSN: u32, /* Product Serial Number */ + pub Reserved1: u8, /* Reserved1 */ + pub ManufactDate: u16, /* Manufacturing Date */ + pub CID_CRC: u8, /* CID CRC */ + pub Reserved2: u8, /* always 1 */ +} + +/** + * Card information + */ +#[derive(Debug, Copy, Clone)] +pub struct SDCardInfo { + pub SD_csd: SDCardCSD, + pub SD_cid: SDCardCID, + pub CardCapacity: u64, /* Card Capacity */ + pub CardBlockSize: u64, /* Card Block Size */ +} + +impl SDCard { + pub fn new(spi: X, spi_cs: u32, cs_gpionum: u8/*, dmac: &'a DMAC, channel: dma_channel*/) -> Self { + Self { + spi, + spi_cs, + cs_gpionum, + /* + dmac, + channel, + */ + } + } + + fn CS_HIGH(&self) { + gpiohs::set_pin(self.cs_gpionum, true); + } + + fn CS_LOW(&self) { + gpiohs::set_pin(self.cs_gpionum, false); + } + + fn HIGH_SPEED_ENABLE(&self) { + self.spi.set_clk_rate(10000000); + } + + fn lowlevel_init(&self) { + gpiohs::set_direction(self.cs_gpionum, gpio::direction::OUTPUT); + self.spi.set_clk_rate(200000); + } + + fn write_data(&self, data: &[u8]) { + self.spi.configure( + work_mode::MODE0, + frame_format::STANDARD, + 8, /* data bits */ + 0, /* endian */ + 0, /*instruction length*/ + 0, /*address length*/ + 0, /*wait cycles*/ + aitm::STANDARD, + tmod::TRANS, + ); + self.spi.send_data(self.spi_cs, data); + } + + /* + fn write_data_dma(&self, data: &[u32]) { + self.spi.configure( + work_mode::MODE0, + frame_format::STANDARD, + 8, /* data bits */ + 0, /* endian */ + 0, /*instruction length*/ + 0, /*address length*/ + 0, /*wait cycles*/ + aitm::STANDARD, + tmod::TRANS, + ); + self.spi + .send_data_dma(self.dmac, self.channel, self.spi_cs, data); + } + */ + + fn read_data(&self, data: &mut [u8]) { + self.spi.configure( + work_mode::MODE0, + frame_format::STANDARD, + 8, /* data bits */ + 0, /* endian */ + 0, /*instruction length*/ + 0, /*address length*/ + 0, /*wait cycles*/ + aitm::STANDARD, + tmod::RECV, + ); + self.spi.recv_data(self.spi_cs, data); + } + + /* + fn read_data_dma(&self, data: &mut [u32]) { + self.spi.configure( + work_mode::MODE0, + frame_format::STANDARD, + 8, /* data bits */ + 0, /* endian */ + 0, /*instruction length*/ + 0, /*address length*/ + 0, /*wait cycles*/ + aitm::STANDARD, + tmod::RECV, + ); + self.spi + .recv_data_dma(self.dmac, self.channel, self.spi_cs, data); + } + */ + + /* + * Send 5 bytes command to the SD card. + * @param cmd: The user expected command to send to SD card. + * @param arg: The command argument. + * @param crc: The CRC. + * @retval None + */ + fn send_cmd(&self, cmd: CMD, arg: u32, crc: u8) { + /* SD chip select low */ + self.CS_LOW(); + /* Send the Cmd bytes */ + self.write_data(&[ + /* Construct byte 1 */ + ((cmd as u8) | 0x40), + /* Construct byte 2 */ + (arg >> 24) as u8, + /* Construct byte 3 */ + ((arg >> 16) & 0xff) as u8, + /* Construct byte 4 */ + ((arg >> 8) & 0xff) as u8, + /* Construct byte 5 */ + (arg & 0xff) as u8, + /* Construct CRC: byte 6 */ + crc, + ]); + } + + /* Send end-command sequence to SD card */ + fn end_cmd(&self) { + /* SD chip select high */ + self.CS_HIGH(); + /* Send the cmd byte */ + self.write_data(&[0xff]); + } + + /* + * Returns the SD response. + * @param None + * @retval The SD Response: + * - 0xFF: Sequence failed + * - 0: Sequence succeed + */ + fn get_response(&self) -> u8 { + let result = &mut [0u8]; + let mut timeout = 0x0FFF; + /* Check if response is got or a timeout is happen */ + while timeout != 0 { + self.read_data(result); + /* Right response got */ + if result[0] != 0xFF { + return result[0]; + } + timeout -= 1; + } + /* After time out */ + return 0xFF; + } + + /* + * Get SD card data response. + * @param None + * @retval The SD status: Read data response xxx01 + * - status 010: Data accecpted + * - status 101: Data rejected due to a crc error + * - status 110: Data rejected due to a Write error. + * - status 111: Data rejected due to other error. + */ + fn get_dataresponse(&self) -> u8 { + let response = &mut [0u8]; + /* Read resonse */ + self.read_data(response); + /* Mask unused bits */ + response[0] &= 0x1F; + if response[0] != 0x05 { + return 0xFF; + } + /* Wait null data */ + self.read_data(response); + while response[0] == 0 { + self.read_data(response); + } + /* Return response */ + return 0; + } + + /* + * Read the CSD card register + * Reading the contents of the CSD register in SPI mode is a simple + * read-block transaction. + * @param SD_csd: pointer on an SCD register structure + * @retval The SD Response: + * - `Err()`: Sequence failed + * - `Ok(info)`: Sequence succeed + */ + fn get_csdregister(&self) -> Result { + let mut csd_tab = [0u8; 18]; + /* Send CMD9 (CSD register) */ + self.send_cmd(CMD::CMD9, 0, 0); + /* Wait for response in the R1 format (0x00 is no errors) */ + if self.get_response() != 0x00 { + self.end_cmd(); + return Err(()); + } + if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { + self.end_cmd(); + return Err(()); + } + /* Store CSD register value on csd_tab */ + /* Get CRC bytes (not really needed by us, but required by SD) */ + self.read_data(&mut csd_tab); + self.end_cmd(); + /* see also: https://cdn-shop.adafruit.com/datasheets/TS16GUSDHC6.pdf */ + return Ok(SDCardCSD { + /* Byte 0 */ + CSDStruct: (csd_tab[0] & 0xC0) >> 6, + SysSpecVersion: (csd_tab[0] & 0x3C) >> 2, + Reserved1: csd_tab[0] & 0x03, + /* Byte 1 */ + TAAC: csd_tab[1], + /* Byte 2 */ + NSAC: csd_tab[2], + /* Byte 3 */ + MaxBusClkFrec: csd_tab[3], + /* Byte 4, 5 */ + CardComdClasses: (u16::from(csd_tab[4]) << 4) | ((u16::from(csd_tab[5]) & 0xF0) >> 4), + /* Byte 5 */ + RdBlockLen: csd_tab[5] & 0x0F, + /* Byte 6 */ + PartBlockRead: (csd_tab[6] & 0x80) >> 7, + WrBlockMisalign: (csd_tab[6] & 0x40) >> 6, + RdBlockMisalign: (csd_tab[6] & 0x20) >> 5, + DSRImpl: (csd_tab[6] & 0x10) >> 4, + Reserved2: 0, + // DeviceSize: (csd_tab[6] & 0x03) << 10, + /* Byte 7, 8, 9 */ + DeviceSize: ((u32::from(csd_tab[7]) & 0x3F) << 16) + | (u32::from(csd_tab[8]) << 8) + | u32::from(csd_tab[9]), + /* Byte 10 */ + EraseGrSize: (csd_tab[10] & 0x40) >> 6, + /* Byte 10, 11 */ + EraseGrMul: ((csd_tab[10] & 0x3F) << 1) | ((csd_tab[11] & 0x80) >> 7), + /* Byte 11 */ + WrProtectGrSize: (csd_tab[11] & 0x7F), + /* Byte 12 */ + WrProtectGrEnable: (csd_tab[12] & 0x80) >> 7, + ManDeflECC: (csd_tab[12] & 0x60) >> 5, + WrSpeedFact: (csd_tab[12] & 0x1C) >> 2, + /* Byte 12,13 */ + MaxWrBlockLen: ((csd_tab[12] & 0x03) << 2) | ((csd_tab[13] & 0xC0) >> 6), + /* Byte 13 */ + WriteBlockPaPartial: (csd_tab[13] & 0x20) >> 5, + Reserved3: 0, + ContentProtectAppli: (csd_tab[13] & 0x01), + /* Byte 14 */ + FileFormatGroup: (csd_tab[14] & 0x80) >> 7, + CopyFlag: (csd_tab[14] & 0x40) >> 6, + PermWrProtect: (csd_tab[14] & 0x20) >> 5, + TempWrProtect: (csd_tab[14] & 0x10) >> 4, + FileFormat: (csd_tab[14] & 0x0C) >> 2, + ECC: (csd_tab[14] & 0x03), + /* Byte 15 */ + CSD_CRC: (csd_tab[15] & 0xFE) >> 1, + Reserved4: 1, + /* Return the reponse */ + }); + } + + /* + * Read the CID card register. + * Reading the contents of the CID register in SPI mode is a simple + * read-block transaction. + * @param SD_cid: pointer on an CID register structure + * @retval The SD Response: + * - `Err()`: Sequence failed + * - `Ok(info)`: Sequence succeed + */ + fn get_cidregister(&self) -> Result { + let mut cid_tab = [0u8; 18]; + /* Send CMD10 (CID register) */ + self.send_cmd(CMD::CMD10, 0, 0); + /* Wait for response in the R1 format (0x00 is no errors) */ + if self.get_response() != 0x00 { + self.end_cmd(); + return Err(()); + } + if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { + self.end_cmd(); + return Err(()); + } + /* Store CID register value on cid_tab */ + /* Get CRC bytes (not really needed by us, but required by SD) */ + self.read_data(&mut cid_tab); + self.end_cmd(); + return Ok(SDCardCID { + /* Byte 0 */ + ManufacturerID: cid_tab[0], + /* Byte 1, 2 */ + OEM_AppliID: (u16::from(cid_tab[1]) << 8) | u16::from(cid_tab[2]), + /* Byte 3, 4, 5, 6 */ + ProdName1: (u32::from(cid_tab[3]) << 24) + | (u32::from(cid_tab[4]) << 16) + | (u32::from(cid_tab[5]) << 8) + | u32::from(cid_tab[6]), + /* Byte 7 */ + ProdName2: cid_tab[7], + /* Byte 8 */ + ProdRev: cid_tab[8], + /* Byte 9, 10, 11, 12 */ + ProdSN: (u32::from(cid_tab[9]) << 24) + | (u32::from(cid_tab[10]) << 16) + | (u32::from(cid_tab[11]) << 8) + | u32::from(cid_tab[12]), + /* Byte 13, 14 */ + Reserved1: (cid_tab[13] & 0xF0) >> 4, + ManufactDate: ((u16::from(cid_tab[13]) & 0x0F) << 8) | u16::from(cid_tab[14]), + /* Byte 15 */ + CID_CRC: (cid_tab[15] & 0xFE) >> 1, + Reserved2: 1, + }); + } + + /* + * Returns information about specific card. + * @param cardinfo: pointer to a SD_CardInfo structure that contains all SD + * card information. + * @retval The SD Response: + * - `Err(())`: Sequence failed + * - `Ok(info)`: Sequence succeed + */ + fn get_cardinfo(&self) -> Result { + let mut info = SDCardInfo { + SD_csd: self.get_csdregister()?, + SD_cid: self.get_cidregister()?, + CardCapacity: 0, + CardBlockSize: 0, + }; + info.CardBlockSize = 1 << u64::from(info.SD_csd.RdBlockLen); + info.CardCapacity = (u64::from(info.SD_csd.DeviceSize) + 1) * 1024 * info.CardBlockSize; + + Ok(info) + } + + /* + * Initializes the SD/SD communication in SPI mode. + * @param None + * @retval The SD Response info if succeeeded, otherwise Err + */ + pub fn init(&self) -> Result { + /* Initialize SD_SPI */ + self.lowlevel_init(); + /* SD chip select high */ + self.CS_HIGH(); + /* NOTE: this reset doesn't always seem to work if the SD access was broken off in the + * middle of an operation: CMDFailed(CMD0, 127). */ + + /* Send dummy byte 0xFF, 10 times with CS high */ + /* Rise CS and MOSI for 80 clocks cycles */ + /* Send dummy byte 0xFF */ + self.write_data(&[0xff; 10]); + /*------------Put SD in SPI mode--------------*/ + /* SD initialized and set to SPI mode properly */ + + /* Send software reset */ + self.send_cmd(CMD::CMD0, 0, 0x95); + let result = self.get_response(); + self.end_cmd(); + if result != 0x01 { + return Err(InitError::CMDFailed(CMD::CMD0, result)); + } + + /* Check voltage range */ + self.send_cmd(CMD::CMD8, 0x01AA, 0x87); + /* 0x01 or 0x05 */ + let result = self.get_response(); + let mut frame = [0u8; 4]; + self.read_data(&mut frame); + self.end_cmd(); + if result != 0x01 { + return Err(InitError::CMDFailed(CMD::CMD8, result)); + } + let mut index = 255; + while index != 0 { + /* */ + self.send_cmd(CMD::CMD55, 0, 0); + let result = self.get_response(); + self.end_cmd(); + if result != 0x01 { + return Err(InitError::CMDFailed(CMD::CMD55, result)); + } + /* Initiate SDC initialization process */ + self.send_cmd(CMD::ACMD41, 0x40000000, 0); + let result = self.get_response(); + self.end_cmd(); + if result == 0x00 { + break; + } + index -= 1; + } + if index == 0 { + return Err(InitError::CMDFailed(CMD::ACMD41, result)); + } + index = 255; + let mut frame = [0u8; 4]; + while index != 0 { + /* Read OCR */ + self.send_cmd(CMD::CMD58, 0, 1); + let result = self.get_response(); + self.read_data(&mut frame); + self.end_cmd(); + if result == 0 { + break; + } + index -= 1; + } + if index == 0 { + return Err(InitError::CMDFailed(CMD::CMD58, result)); + } + if (frame[0] & 0x40) == 0 { + return Err(InitError::CardCapacityStatusNotSet(frame)); + } + self.HIGH_SPEED_ENABLE(); + self.get_cardinfo() + .map_err(|_| InitError::CannotGetCardInfo) + } + + /* + * Reads a block of data from the SD. + * @param data_buf: slice that receives the data read from the SD. + * @param sector: SD's internal address to read from. + * @retval The SD Response: + * - `Err(())`: Sequence failed + * - `Ok(())`: Sequence succeed + */ + pub fn read_sector(&self, data_buf: &mut [u8], sector: u32) -> Result<(), ()> { + assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0); + /* Send CMD17 to read one block, or CMD18 for multiple */ + let flag = if data_buf.len() == SEC_LEN { + self.send_cmd(CMD::CMD17, sector, 0); + false + } else { + self.send_cmd(CMD::CMD18, sector, 0); + true + }; + /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ + if self.get_response() != 0x00 { + self.end_cmd(); + return Err(()); + } + let mut error = false; + //let mut dma_chunk = [0u32; SEC_LEN]; + let mut tmp_chunk= [0u8; SEC_LEN]; + for chunk in data_buf.chunks_mut(SEC_LEN) { + if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ { + error = true; + break; + } + /* Read the SD block data : read NumByteToRead data */ + //self.read_data_dma(&mut dma_chunk); + self.read_data(&mut tmp_chunk); + /* Place the data received as u32 units from DMA into the u8 target buffer */ + for (a, b) in chunk.iter_mut().zip(/*dma_chunk*/tmp_chunk.iter()) { + //*a = (b & 0xff) as u8; + *a = *b; + } + /* Get CRC bytes (not really needed by us, but required by SD) */ + let mut frame = [0u8; 2]; + self.read_data(&mut frame); + } + self.end_cmd(); + if flag { + self.send_cmd(CMD::CMD12, 0, 0); + self.get_response(); + self.end_cmd(); + self.end_cmd(); + } + /* It is an error if not everything requested was read */ + if error { + Err(()) + } else { + Ok(()) + } + } + + /* + * Writes a block to the SD + * @param data_buf: slice containing the data to be written to the SD. + * @param sector: address to write on. + * @retval The SD Response: + * - `Err(())`: Sequence failed + * - `Ok(())`: Sequence succeed + */ + pub fn write_sector(&self, data_buf: &[u8], sector: u32) -> Result<(), ()> { + assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0); + let mut frame = [0xff, 0x00]; + if data_buf.len() == SEC_LEN { + frame[1] = SD_START_DATA_SINGLE_BLOCK_WRITE; + self.send_cmd(CMD::CMD24, sector, 0); + } else { + frame[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE; + self.send_cmd( + CMD::ACMD23, + (data_buf.len() / SEC_LEN).try_into().unwrap(), + 0, + ); + self.get_response(); + self.end_cmd(); + self.send_cmd(CMD::CMD25, sector, 0); + } + /* Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ + if self.get_response() != 0x00 { + self.end_cmd(); + return Err(()); + } + //let mut dma_chunk = [0u32; SEC_LEN]; + let mut tmp_chunk = [0u8; SEC_LEN]; + for chunk in data_buf.chunks(SEC_LEN) { + /* Send the data token to signify the start of the data */ + self.write_data(&frame); + /* Write the block data to SD : write count data by block */ + for (a, &b) in /*dma_chunk*/tmp_chunk.iter_mut().zip(chunk.iter()) { + //*a = b.into(); + *a = b; + } + //self.write_data_dma(&mut dma_chunk); + self.write_data(&mut tmp_chunk); + /* Put dummy CRC bytes */ + self.write_data(&[0xff, 0xff]); + /* Read data response */ + if self.get_dataresponse() != 0x00 { + self.end_cmd(); + return Err(()); + } + } + self.end_cmd(); + self.end_cmd(); + Ok(()) + } +} + +/** GPIOHS GPIO number to use for controlling the SD card CS pin */ +const SD_CS_GPIONUM: u8 = 7; +/** CS value passed to SPI controller, this is a dummy value as SPI0_CS3 is not mapping to anything + * in the FPIOA */ +const SD_CS: u32 = 3; + +/** Connect pins to internal functions */ +fn io_init() { + fpioa::set_function(io::SPI0_SCLK, fpioa::function::SPI0_SCLK); + fpioa::set_function(io::SPI0_MOSI, fpioa::function::SPI0_D0); + fpioa::set_function(io::SPI0_MISO, fpioa::function::SPI0_D1); + fpioa::set_function(io::SPI0_CS0, fpioa::function::gpiohs(SD_CS_GPIONUM)); + fpioa::set_io_pull(io::SPI0_CS0, fpioa::pull::DOWN); // GPIO output=pull down +} + +lazy_static! { + static ref PERIPHERALS: UPSafeCell = unsafe { + UPSafeCell::new(Peripherals::take().unwrap()) + }; +} + +fn init_sdcard() -> SDCard> { + // wait previous output + usleep(100000); + let peripherals = unsafe { Peripherals::steal() }; + sysctl::pll_set_freq(sysctl::pll::PLL0, 800_000_000).unwrap(); + sysctl::pll_set_freq(sysctl::pll::PLL1, 300_000_000).unwrap(); + sysctl::pll_set_freq(sysctl::pll::PLL2, 45_158_400).unwrap(); + let clocks = k210_hal::clock::Clocks::new(); + peripherals.UARTHS.configure(115_200.bps(), &clocks); + io_init(); + + let spi = peripherals.SPI0.constrain(); + let sd = SDCard::new(spi, SD_CS, SD_CS_GPIONUM); + let info = sd.init().unwrap(); + let num_sectors = info.CardCapacity / 512; + assert!(num_sectors > 0); + + println!("init sdcard!"); + sd +} + +pub struct SDCardWrapper(UPSafeCell>>); + +impl SDCardWrapper { + pub fn new() -> Self { + unsafe { Self(UPSafeCell::new(init_sdcard())) } + } +} + +impl BlockDevice for SDCardWrapper { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + self.0.exclusive_access().read_sector(buf,block_id as u32).unwrap(); + } + fn write_block(&self, block_id: usize, buf: &[u8]) { + self.0.exclusive_access().write_sector(buf,block_id as u32).unwrap(); + } +} \ No newline at end of file diff --git a/os/src/drivers/block/virtio_blk.rs b/os/src/drivers/block/virtio_blk.rs new file mode 100644 index 00000000..6b386190 --- /dev/null +++ b/os/src/drivers/block/virtio_blk.rs @@ -0,0 +1,84 @@ + +use virtio_drivers::{VirtIOBlk, VirtIOHeader}; +use crate::mm::{ + PhysAddr, + VirtAddr, + frame_alloc, + frame_dealloc, + PhysPageNum, + FrameTracker, + StepByOne, + PageTable, + kernel_token, +}; +use super::BlockDevice; +use crate::sync::UPSafeCell; +use alloc::vec::Vec; +use lazy_static::*; + +#[allow(unused)] +const VIRTIO0: usize = 0x10001000; + +pub struct VirtIOBlock(UPSafeCell>); + +lazy_static! { + static ref QUEUE_FRAMES: UPSafeCell> = unsafe { + UPSafeCell::new(Vec::new()) + }; +} + +impl BlockDevice for VirtIOBlock { + fn read_block(&self, block_id: usize, buf: &mut [u8]) { + self.0.exclusive_access() + .read_block(block_id, buf) + .expect("Error when reading VirtIOBlk"); + } + fn write_block(&self, block_id: usize, buf: &[u8]) { + self.0.exclusive_access() + .write_block(block_id, buf) + .expect("Error when writing VirtIOBlk"); + } +} + +impl VirtIOBlock { + #[allow(unused)] + pub fn new() -> Self { + unsafe { + Self(UPSafeCell::new(VirtIOBlk::new( + &mut *(VIRTIO0 as *mut VirtIOHeader) + ).unwrap())) + } + } +} + +#[no_mangle] +pub extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr { + let mut ppn_base = PhysPageNum(0); + for i in 0..pages { + let frame = frame_alloc().unwrap(); + if i == 0 { ppn_base = frame.ppn; } + assert_eq!(frame.ppn.0, ppn_base.0 + i); + QUEUE_FRAMES.exclusive_access().push(frame); + } + ppn_base.into() +} + +#[no_mangle] +pub extern "C" fn virtio_dma_dealloc(pa: PhysAddr, pages: usize) -> i32 { + let mut ppn_base: PhysPageNum = pa.into(); + for _ in 0..pages { + frame_dealloc(ppn_base); + ppn_base.step(); + } + 0 +} + +#[no_mangle] +pub extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr { + VirtAddr(paddr.0) +} + +#[no_mangle] +pub extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { + PageTable::from_token(kernel_token()).translate_va(vaddr).unwrap() +} diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs new file mode 100644 index 00000000..54c0a2cb --- /dev/null +++ b/os/src/drivers/mod.rs @@ -0,0 +1,3 @@ +mod block; + +pub use block::BLOCK_DEVICE; \ No newline at end of file diff --git a/os/src/fs/inode.rs b/os/src/fs/inode.rs new file mode 100644 index 00000000..8e644066 --- /dev/null +++ b/os/src/fs/inode.rs @@ -0,0 +1,159 @@ +use easy_fs::{ + EasyFileSystem, + Inode, +}; +use crate::drivers::BLOCK_DEVICE; +use crate::sync::UPSafeCell; +use alloc::sync::Arc; +use lazy_static::*; +use bitflags::*; +use alloc::vec::Vec; +use super::File; +use crate::mm::UserBuffer; + +pub struct OSInode { + readable: bool, + writable: bool, + inner: UPSafeCell, +} + +pub struct OSInodeInner { + offset: usize, + inode: Arc, +} + +impl OSInode { + pub fn new( + readable: bool, + writable: bool, + inode: Arc, + ) -> Self { + Self { + readable, + writable, + inner: unsafe { UPSafeCell::new(OSInodeInner { + offset: 0, + inode, + })}, + } + } + pub fn read_all(&self) -> Vec { + let mut inner = self.inner.exclusive_access(); + let mut buffer = [0u8; 512]; + let mut v: Vec = Vec::new(); + loop { + let len = inner.inode.read_at(inner.offset, &mut buffer); + if len == 0 { + break; + } + inner.offset += len; + v.extend_from_slice(&buffer[..len]); + } + v + } +} + +lazy_static! { + pub static ref ROOT_INODE: Arc = { + let efs = EasyFileSystem::open(BLOCK_DEVICE.clone()); + Arc::new(EasyFileSystem::root_inode(&efs)) + }; +} + +pub fn list_apps() { + println!("/**** APPS ****"); + for app in ROOT_INODE.ls() { + println!("{}", app); + } + println!("**************/"); +} + +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 { + /// Do not check validity for simplicity + /// Return (readable, writable) + 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 fn open_file(name: &str, flags: OpenFlags) -> Option> { + let (readable, writable) = flags.read_write(); + if flags.contains(OpenFlags::CREATE) { + if let Some(inode) = ROOT_INODE.find(name) { + // clear size + inode.clear(); + Some(Arc::new(OSInode::new( + readable, + writable, + inode, + ))) + } else { + // create file + 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 + )) + }) + } +} + +impl File for OSInode { + fn readable(&self) -> bool { self.readable } + fn writable(&self) -> bool { self.writable } + fn read(&self, mut buf: UserBuffer) -> usize { + let mut inner = self.inner.exclusive_access(); + let mut total_read_size = 0usize; + for slice in buf.buffers.iter_mut() { + let read_size = inner.inode.read_at(inner.offset, *slice); + if read_size == 0 { + break; + } + inner.offset += read_size; + total_read_size += read_size; + } + total_read_size + } + fn write(&self, buf: UserBuffer) -> usize { + let mut inner = self.inner.exclusive_access(); + let mut total_write_size = 0usize; + for slice in buf.buffers.iter() { + let write_size = inner.inode.write_at(inner.offset, *slice); + assert_eq!(write_size, slice.len()); + inner.offset += write_size; + total_write_size += write_size; + } + total_write_size + } +} diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs index 60c89bd3..c015702d 100644 --- a/os/src/fs/mod.rs +++ b/os/src/fs/mod.rs @@ -1,11 +1,16 @@ mod pipe; mod stdio; +mod inode; use crate::mm::UserBuffer; + pub trait File : Send + Sync { + fn readable(&self) -> bool; + fn writable(&self) -> bool; fn read(&self, buf: UserBuffer) -> usize; fn write(&self, buf: UserBuffer) -> usize; } pub use pipe::{Pipe, make_pipe}; -pub use stdio::{Stdin, Stdout}; \ No newline at end of file +pub use stdio::{Stdin, Stdout}; +pub use inode::{OSInode, open_file, OpenFlags, list_apps}; \ No newline at end of file diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs index e725bbe2..ed2dde15 100644 --- a/os/src/fs/pipe.rs +++ b/os/src/fs/pipe.rs @@ -114,8 +114,10 @@ pub fn make_pipe() -> (Arc, Arc) { } impl File for Pipe { + fn readable(&self) -> bool { self.readable } + fn writable(&self) -> bool { self.writable } fn read(&self, buf: UserBuffer) -> usize { - assert_eq!(self.readable, true); + assert_eq!(self.readable(), true); let mut buf_iter = buf.into_iter(); let mut read_size = 0usize; loop { @@ -141,7 +143,7 @@ impl File for Pipe { } } fn write(&self, buf: UserBuffer) -> usize { - assert_eq!(self.writable, true); + assert_eq!(self.writable(), true); let mut buf_iter = buf.into_iter(); let mut write_size = 0usize; loop { diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs index b87d185b..e8df7950 100644 --- a/os/src/fs/stdio.rs +++ b/os/src/fs/stdio.rs @@ -8,6 +8,8 @@ pub struct Stdin; pub struct Stdout; impl File for Stdin { + fn readable(&self) -> bool { true } + fn writable(&self) -> bool { false } fn read(&self, mut user_buf: UserBuffer) -> usize { assert_eq!(user_buf.len(), 1); // busy loop @@ -31,6 +33,8 @@ impl File for Stdin { } impl File for Stdout { + fn readable(&self) -> bool { false } + fn writable(&self) -> bool { true } fn read(&self, _user_buf: UserBuffer) -> usize{ panic!("Cannot read from stdout!"); } diff --git a/os/src/main.rs b/os/src/main.rs index dd16ddb1..3a960a65 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -14,18 +14,17 @@ mod lang_items; mod sbi; mod syscall; mod trap; -mod loader; mod config; mod task; mod timer; mod sync; mod mm; mod fs; +mod drivers; use core::arch::global_asm; global_asm!(include_str!("entry.asm")); -global_asm!(include_str!("link_app.S")); fn clear_bss() { extern "C" { @@ -46,12 +45,11 @@ pub fn rust_main() -> ! { println!("[kernel] Hello, world!"); mm::init(); mm::remap_test(); - task::add_initproc(); - println!("after initproc!"); trap::init(); trap::enable_timer_interrupt(); timer::set_next_trigger(); - loader::list_apps(); + fs::list_apps(); + task::add_initproc(); task::run_tasks(); panic!("Unreachable in rust_main!"); } diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs index 1be9ae74..107f35c6 100644 --- a/os/src/mm/address.rs +++ b/os/src/mm/address.rs @@ -8,15 +8,19 @@ const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; /// Definitions +#[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysAddr(pub usize); +#[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtAddr(pub usize); +#[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysPageNum(pub usize); +#[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtPageNum(pub usize); @@ -116,6 +120,11 @@ impl VirtPageNum { } impl PhysAddr { + pub fn get_ref(&self) -> &'static T { + unsafe { + (self.0 as *const T).as_ref().unwrap() + } + } pub fn get_mut(&self) -> &'static mut T { unsafe { (self.0 as *mut T).as_mut().unwrap() @@ -149,6 +158,11 @@ impl StepByOne for VirtPageNum { self.0 += 1; } } +impl StepByOne for PhysPageNum { + fn step(&mut self) { + self.0 += 1; + } +} #[derive(Copy, Clone)] pub struct SimpleRange where diff --git a/os/src/mm/frame_allocator.rs b/os/src/mm/frame_allocator.rs index 85d06cce..357db707 100644 --- a/os/src/mm/frame_allocator.rs +++ b/os/src/mm/frame_allocator.rs @@ -109,7 +109,7 @@ pub fn frame_alloc() -> Option { .map(|ppn| FrameTracker::new(ppn)) } -fn frame_dealloc(ppn: PhysPageNum) { +pub fn frame_dealloc(ppn: PhysPageNum) { FRAME_ALLOCATOR .exclusive_access() .dealloc(ppn); diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 84262537..31092442 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -13,7 +13,8 @@ use crate::config::{ PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, - USER_STACK_SIZE + USER_STACK_SIZE, + MMIO, }; use core::arch::asm; @@ -36,6 +37,10 @@ lazy_static! { }); } +pub fn kernel_token() -> usize { + KERNEL_SPACE.exclusive_access().token() +} + pub struct MemorySet { page_table: PageTable, areas: Vec, @@ -127,6 +132,15 @@ impl MemorySet { MapType::Identical, MapPermission::R | MapPermission::W, ), None); + println!("mapping memory-mapped registers"); + for pair in MMIO { + memory_set.push(MapArea::new( + (*pair).0.into(), + ((*pair).0 + (*pair).1).into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), None); + } memory_set } /// Include sections in elf and trampoline and TrapContext and user stack, diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 7b654e1d..85e8c16a 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -4,23 +4,25 @@ mod frame_allocator; mod page_table; mod memory_set; -use page_table::{PageTable, PTEFlags}; -use address::{VPNRange, StepByOne}; -pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum}; -pub use frame_allocator::{FrameTracker, frame_alloc}; +use page_table::PTEFlags; +use address::VPNRange; +pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum, StepByOne}; +pub use frame_allocator::{FrameTracker, frame_alloc, frame_dealloc,}; pub use page_table::{ + PageTable, PageTableEntry, translated_byte_buffer, translated_str, + translated_ref, translated_refmut, UserBuffer, UserBufferIterator, }; -pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission}; +pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission, kernel_token}; pub use memory_set::remap_test; pub fn init() { heap_allocator::init_heap(); frame_allocator::init_frame_allocator(); KERNEL_SPACE.exclusive_access().activate(); -} +} \ No newline at end of file diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index 06f2a4d3..e0a1bc6b 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -174,6 +174,7 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& v } +/// Load a string from other address spaces into kernel space without an end `\0`. pub fn translated_str(token: usize, ptr: *const u8) -> String { let page_table = PageTable::from_token(token); let mut string = String::new(); @@ -182,14 +183,18 @@ pub fn translated_str(token: usize, ptr: *const u8) -> String { let ch: u8 = *(page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut()); if ch == 0 { break; - } else { - string.push(ch as char); - va += 1; } + string.push(ch as char); + va += 1; } string } +pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { + let page_table = PageTable::from_token(token); + page_table.translate_va(VirtAddr::from(ptr as usize)).unwrap().get_ref() +} + pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { let page_table = PageTable::from_token(token); let va = ptr as usize; @@ -247,5 +252,4 @@ impl Iterator for UserBufferIterator { Some(r) } } -} - +} \ No newline at end of file diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 78301936..9e7c1857 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,6 +1,12 @@ -use crate::mm::{UserBuffer, translated_byte_buffer, translated_refmut}; +use crate::mm::{ + UserBuffer, + translated_byte_buffer, + translated_refmut, + translated_str, +}; use crate::task::{current_user_token, current_task}; -use crate::fs::make_pipe; +use crate::fs::{make_pipe, OpenFlags, open_file}; +use alloc::sync::Arc; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); @@ -10,6 +16,9 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { return -1; } if let Some(file) = &inner.fd_table[fd] { + if !file.writable() { + return -1; + } let file = file.clone(); // release current task TCB manually to avoid multi-borrow drop(inner); @@ -30,6 +39,9 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { } if let Some(file) = &inner.fd_table[fd] { let file = file.clone(); + if !file.readable() { + return -1; + } // release current task TCB manually to avoid multi-borrow drop(inner); file.read( @@ -40,6 +52,23 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { } } +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.inner_exclusive_access(); + 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(); @@ -65,4 +94,18 @@ pub fn sys_pipe(pipe: *mut usize) -> isize { *translated_refmut(token, pipe) = read_fd; *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd; 0 +} + +pub fn sys_dup(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; + } + let new_fd = inner.alloc_fd(); + inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); + new_fd as isize } \ No newline at end of file diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 1699e8ca..4683d055 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,3 +1,5 @@ +const SYSCALL_DUP: usize = 24; +const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; const SYSCALL_READ: usize = 63; @@ -18,6 +20,8 @@ use process::*; pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { + SYSCALL_DUP=> sys_dup(args[0]), + SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), SYSCALL_CLOSE => sys_close(args[0]), SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]), @@ -27,7 +31,7 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GETPID => sys_getpid(), SYSCALL_FORK => sys_fork(), - SYSCALL_EXEC => sys_exec(args[0] as *const u8), + SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), _ => panic!("Unsupported syscall_id: {}", syscall_id), } diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index b425d87d..cf3cbcb4 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -9,9 +9,15 @@ use crate::timer::get_time_ms; use crate::mm::{ translated_str, translated_refmut, + translated_ref, +}; +use crate::fs::{ + open_file, + OpenFlags, }; -use crate::loader::get_app_data_by_name; use alloc::sync::Arc; +use alloc::vec::Vec; +use alloc::string::String; pub fn sys_exit(exit_code: i32) -> ! { exit_current_and_run_next(exit_code); @@ -45,13 +51,25 @@ pub fn sys_fork() -> isize { new_pid as isize } -pub fn sys_exec(path: *const u8) -> isize { +pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { let token = current_user_token(); let path = translated_str(token, path); - if let Some(data) = get_app_data_by_name(path.as_str()) { + let mut args_vec: Vec = Vec::new(); + loop { + let arg_str_ptr = *translated_ref(token, args); + if arg_str_ptr == 0 { + break; + } + args_vec.push(translated_str(token, arg_str_ptr as *const u8)); + unsafe { args = args.add(1); } + } + if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) { + let all_data = app_inode.read_all(); let task = current_task().unwrap(); - task.exec(data); - 0 + let argc = args_vec.len(); + task.exec(all_data.as_slice(), args_vec); + // return argc because cx.x[10] will be covered with it later + argc as isize } else { -1 } @@ -63,7 +81,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { let task = current_task().unwrap(); // find a child process - // ---- access current TCB exclusively + // ---- access current PCB exclusively let mut inner = task.inner_exclusive_access(); if inner.children .iter() @@ -76,16 +94,16 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { .iter() .enumerate() .find(|(_, p)| { - // ++++ temporarily access child PCB lock exclusively + // ++++ temporarily access child PCB exclusively p.inner_exclusive_access().is_zombie() && (pid == -1 || pid as usize == p.getpid()) // ++++ release child PCB }); if let Some((idx, _)) = pair { let child = inner.children.remove(idx); - // confirm that child will be deallocated after removing from children list + // confirm that child will be deallocated after being removed from children list assert_eq!(Arc::strong_count(&child), 1); let found_pid = child.getpid(); - // ++++ temporarily access child TCB exclusively + // ++++ temporarily access child PCB exclusively let exit_code = child.inner_exclusive_access().exit_code; // ++++ release child PCB *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code; @@ -93,5 +111,5 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } else { -2 } - // ---- release current PCB lock automatically + // ---- release current PCB automatically } \ No newline at end of file diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index d5301040..81d6a09b 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -5,14 +5,14 @@ mod manager; mod processor; mod pid; -use crate::loader::get_app_data_by_name; +use crate::fs::{open_file, OpenFlags}; use switch::__switch; use task::{TaskControlBlock, TaskStatus}; use alloc::sync::Arc; use manager::fetch_task; use lazy_static::*; - pub use context::TaskContext; + pub use processor::{ run_tasks, current_task, @@ -76,9 +76,11 @@ pub fn exit_current_and_run_next(exit_code: i32) { } lazy_static! { - pub static ref INITPROC: Arc = Arc::new( - TaskControlBlock::new(get_app_data_by_name("initproc").unwrap()) - ); + pub static ref INITPROC: Arc = Arc::new({ + let inode = open_file("initproc", OpenFlags::RDONLY).unwrap(); + let v = inode.read_all(); + TaskControlBlock::new(v.as_slice()) + }); } pub fn add_initproc() { diff --git a/os/src/task/task.rs b/os/src/task/task.rs index b4c6f0eb..25b2226d 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,4 +1,10 @@ -use crate::mm::{MemorySet, PhysPageNum, KERNEL_SPACE, VirtAddr}; +use crate::mm::{ + MemorySet, + PhysPageNum, + KERNEL_SPACE, + VirtAddr, + translated_refmut, +}; use crate::trap::{TrapContext, trap_handler}; use crate::config::TRAP_CONTEXT; use crate::sync::UPSafeCell; @@ -8,6 +14,7 @@ use super::{PidHandle, pid_alloc, KernelStack}; use alloc::sync::{Weak, Arc}; use alloc::vec; use alloc::vec::Vec; +use alloc::string::String; use crate::fs::{File, Stdin, Stdout}; pub struct TaskControlBlock { @@ -102,33 +109,59 @@ impl TaskControlBlock { ); task_control_block } - pub fn exec(&self, elf_data: &[u8]) { + pub fn exec(&self, elf_data: &[u8], args: Vec) { // memory_set with elf program headers/trampoline/trap context/user stack - let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); + let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data); let trap_cx_ppn = memory_set .translate(VirtAddr::from(TRAP_CONTEXT).into()) .unwrap() .ppn(); + // push arguments on user stack + user_sp -= (args.len() + 1) * core::mem::size_of::(); + let argv_base = user_sp; + let mut argv: Vec<_> = (0..=args.len()) + .map(|arg| { + translated_refmut( + memory_set.token(), + (argv_base + arg * core::mem::size_of::()) as *mut usize + ) + }) + .collect(); + *argv[args.len()] = 0; + for i in 0..args.len() { + user_sp -= args[i].len() + 1; + *argv[i] = user_sp; + let mut p = user_sp; + for c in args[i].as_bytes() { + *translated_refmut(memory_set.token(), p as *mut u8) = *c; + p += 1; + } + *translated_refmut(memory_set.token(), p as *mut u8) = 0; + } + // make the user_sp aligned to 8B for k210 platform + user_sp -= user_sp % core::mem::size_of::(); - // **** access inner exclusively + // **** access current TCB exclusively let mut inner = self.inner_exclusive_access(); // substitute memory_set inner.memory_set = memory_set; // update trap_cx ppn inner.trap_cx_ppn = trap_cx_ppn; // initialize trap_cx - let trap_cx = inner.get_trap_cx(); - *trap_cx = TrapContext::app_init_context( + let mut trap_cx = TrapContext::app_init_context( entry_point, user_sp, KERNEL_SPACE.exclusive_access().token(), self.kernel_stack.get_top(), trap_handler as usize, ); - // **** release inner automatically + trap_cx.x[10] = args.len(); + trap_cx.x[11] = argv_base; + *inner.get_trap_cx() = trap_cx; + // **** release current PCB } pub fn fork(self: &Arc) -> Arc { - // ---- access parent PCB exclusively + // ---- hold parent PCB lock let mut parent_inner = self.inner_exclusive_access(); // copy user space(include trap context) let memory_set = MemorySet::from_existed_user( diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index 8c5175f7..16f81415 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -1,6 +1,7 @@ use riscv::register::sstatus::{Sstatus, self, SPP}; #[repr(C)] +#[derive(Debug)] pub struct TrapContext { pub x: [usize; 32], pub sstatus: Sstatus, diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 541ff305..66d403f7 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -89,6 +89,7 @@ pub fn trap_handler() -> ! { panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval); } } + //println!("before trap_return"); trap_return(); } @@ -116,7 +117,9 @@ pub fn trap_return() -> ! { #[no_mangle] pub fn trap_from_kernel() -> ! { + use riscv::register::sepc; + println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read()); panic!("a trap {:?} from kernel!", scause::read().cause()); } -pub use context::{TrapContext}; +pub use context::TrapContext; diff --git a/user/Cargo.toml b/user/Cargo.toml index aa6e20fe..d50a0ba0 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -7,4 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -buddy_system_allocator = "0.6" \ No newline at end of file +buddy_system_allocator = "0.6" +bitflags = "1.2.1" \ No newline at end of file diff --git a/user/Makefile b/user/Makefile index 5e0f87c3..e2eaf994 100644 --- a/user/Makefile +++ b/user/Makefile @@ -20,4 +20,4 @@ build: binary clean: @cargo clean -.PHONY: elf binary build clean \ No newline at end of file +.PHONY: elf binary build clean diff --git a/user/src/bin/cat.rs b/user/src/bin/cat.rs new file mode 100644 index 00000000..988164d3 --- /dev/null +++ b/user/src/bin/cat.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + open, + OpenFlags, + close, + read, +}; +use alloc::string::String; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert!(argc == 2); + let fd = open(argv[1], OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occured when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 16]; + let mut s = String::new(); + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { break; } + s.push_str(core::str::from_utf8(&buf[..size]).unwrap()); + } + println!("{}", s); + close(fd); + 0 +} \ No newline at end of file diff --git a/user/src/bin/cmdline_args.rs b/user/src/bin/cmdline_args.rs new file mode 100644 index 00000000..b49ec332 --- /dev/null +++ b/user/src/bin/cmdline_args.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for i in 0..argc { + println!("argv[{}] = {}", i, argv[i]); + } + 0 +} \ No newline at end of file diff --git a/user/src/bin/filetest_simple.rs b/user/src/bin/filetest_simple.rs new file mode 100644 index 00000000..60fda6aa --- /dev/null +++ b/user/src/bin/filetest_simple.rs @@ -0,0 +1,38 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{ + open, + close, + read, + write, + OpenFlags, +}; + +#[no_mangle] +pub fn main() -> i32 { + let test_str = "Hello, world!"; + let filea = "filea\0"; + let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY); + assert!(fd > 0); + let fd = fd as usize; + write(fd, test_str.as_bytes()); + close(fd); + + let fd = open(filea, OpenFlags::RDONLY); + assert!(fd > 0); + let fd = fd as usize; + let mut buffer = [0u8; 100]; + let read_len = read(fd, &mut buffer) as usize; + close(fd); + + assert_eq!( + test_str, + core::str::from_utf8(&buffer[..read_len]).unwrap(), + ); + println!("file_test passed!"); + 0 +} \ No newline at end of file diff --git a/user/src/bin/huge_write.rs b/user/src/bin/huge_write.rs new file mode 100644 index 00000000..f9dafa17 --- /dev/null +++ b/user/src/bin/huge_write.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{ + OpenFlags, + open, + close, + write, + get_time, +}; + +#[no_mangle] +pub fn main() -> i32 { + let mut buffer = [0u8; 1024]; // 1KiB + for i in 0..buffer.len() { + buffer[i] = i as u8; + } + let f = open("testf", OpenFlags::CREATE | OpenFlags::WRONLY); + if f < 0 { + panic!("Open test file failed!"); + } + let f = f as usize; + let start = get_time(); + let size_mb = 1usize; + for _ in 0..1024*size_mb { + write(f, &buffer); + } + close(f); + let time_ms = (get_time() - start) as usize; + let speed_kbs = size_mb * 1000000 / time_ms; + println!("{}MiB written, time cost = {}ms, write speed = {}KiB/s", size_mb, time_ms, speed_kbs); + 0 +} \ No newline at end of file diff --git a/user/src/bin/initproc.rs b/user/src/bin/initproc.rs index 6d30fdb6..99563f47 100644 --- a/user/src/bin/initproc.rs +++ b/user/src/bin/initproc.rs @@ -14,7 +14,7 @@ use user_lib::{ #[no_mangle] fn main() -> i32 { if fork() == 0 { - exec("user_shell\0"); + exec("user_shell\0", &[0 as *const u8]); } else { loop { let mut exit_code: i32 = 0; diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs index ca2afd15..000b82d5 100644 --- a/user/src/bin/run_pipe_test.rs +++ b/user/src/bin/run_pipe_test.rs @@ -10,7 +10,7 @@ use user_lib::{fork, exec, wait}; pub fn main() -> i32 { for i in 0..1000 { if fork() == 0 { - exec("pipe_large_test\0"); + exec("pipe_large_test\0", &[0 as *const u8]); } else { let mut _unused: i32 = 0; wait(&mut _unused); diff --git a/user/src/bin/sleep.rs b/user/src/bin/sleep.rs index beba78c6..bd1e2204 100644 --- a/user/src/bin/sleep.rs +++ b/user/src/bin/sleep.rs @@ -7,7 +7,7 @@ extern crate user_lib; use user_lib::{sleep, exit, get_time, fork, waitpid}; fn sleepy() { - let time: usize = 100; + let time: usize = 1000; for i in 0..5 { sleep(time); println!("sleep {} x {} msecs.", i + 1, time); diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs index 796bddb5..b2c33f2d 100644 --- a/user/src/bin/user_shell.rs +++ b/user/src/bin/user_shell.rs @@ -12,7 +12,16 @@ const DL: u8 = 0x7fu8; const BS: u8 = 0x08u8; use alloc::string::String; -use user_lib::{fork, exec, waitpid}; +use alloc::vec::Vec; +use user_lib::{ + fork, + exec, + waitpid, + open, + OpenFlags, + close, + dup, +}; use user_lib::console::getchar; #[no_mangle] @@ -26,11 +35,78 @@ pub fn main() -> i32 { LF | CR => { println!(""); if !line.is_empty() { - line.push('\0'); + let args: Vec<_> = line.as_str().split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string + }) + .collect(); + + args_copy + .iter_mut() + .for_each(|string| { + string.push('\0'); + }); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy + .iter() + .map(|arg| arg.as_ptr()) + .collect(); + args_addr.push(0 as *const u8); let pid = fork(); if pid == 0 { + // input redirection + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // output redirection + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } // child process - if exec(line.as_str()) == -1 { + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { println!("Error when executing!"); return -4; } diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs index 7a4a6d7b..e8be6c45 100644 --- a/user/src/bin/usertests.rs +++ b/user/src/bin/usertests.rs @@ -26,7 +26,7 @@ pub fn main() -> i32 { println!("Usertests: Running {}", test); let pid = fork(); if pid == 0 { - exec(*test); + exec(*test, &[0 as *const u8]); panic!("unreachable!"); } else { let mut exit_code: i32 = Default::default(); diff --git a/user/src/lib.rs b/user/src/lib.rs index f52b9afa..daaaf8df 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -9,11 +9,14 @@ mod syscall; mod lang_items; extern crate alloc; +#[macro_use] +extern crate bitflags; use syscall::*; use buddy_system_allocator::LockedHeap; +use alloc::vec::Vec; -const USER_HEAP_SIZE: usize = 16384; +const USER_HEAP_SIZE: usize = 32768; static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; @@ -27,20 +30,46 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { #[no_mangle] #[link_section = ".text.entry"] -pub extern "C" fn _start() -> ! { +pub extern "C" fn _start(argc: usize, argv: usize) -> ! { unsafe { HEAP.lock() .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); } - exit(main()); + let mut v: Vec<&'static str> = Vec::new(); + for i in 0..argc { + let str_start = unsafe { + ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() + }; + let len = (0usize..).find(|i| unsafe { + ((str_start + *i) as *const u8).read_volatile() == 0 + }).unwrap(); + v.push( + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(str_start as *const u8, len) + }).unwrap() + ); + } + exit(main(argc, v.as_slice())); } #[linkage = "weak"] #[no_mangle] -fn main() -> i32 { +fn main(_argc: usize, _argv: &[&str]) -> i32 { panic!("Cannot find main!"); } +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +pub fn dup(fd: usize) -> isize { sys_dup(fd) } +pub fn open(path: &str, flags: OpenFlags) -> isize { sys_open(path, flags.bits) } pub fn close(fd: usize) -> isize { sys_close(fd) } pub fn pipe(pipe_fd: &mut [usize]) -> isize { sys_pipe(pipe_fd) } pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) } @@ -50,7 +79,7 @@ pub fn yield_() -> isize { sys_yield() } pub fn get_time() -> isize { sys_get_time() } pub fn getpid() -> isize { sys_getpid() } pub fn fork() -> isize { sys_fork() } -pub fn exec(path: &str) -> isize { sys_exec(path) } +pub fn exec(path: &str, args: &[*const u8]) -> isize { sys_exec(path, args) } pub fn wait(exit_code: &mut i32) -> isize { loop { match sys_waitpid(-1, exit_code as *mut _) { diff --git a/user/src/syscall.rs b/user/src/syscall.rs index f0a5b490..9c30020c 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,5 +1,7 @@ use core::arch::asm; +const SYSCALL_DUP: usize = 24; +const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; const SYSCALL_READ: usize = 63; @@ -26,6 +28,14 @@ fn syscall(id: usize, args: [usize; 3]) -> isize { ret } +pub fn sys_dup(fd: usize) -> isize { + syscall(SYSCALL_DUP, [fd, 0, 0]) +} + +pub fn sys_open(path: &str, flags: u32) -> isize { + syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) +} + pub fn sys_close(fd: usize) -> isize { syscall(SYSCALL_CLOSE, [fd, 0, 0]) } @@ -63,8 +73,8 @@ pub fn sys_fork() -> isize { syscall(SYSCALL_FORK, [0, 0, 0]) } -pub fn sys_exec(path: &str) -> isize { - syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) +pub fn sys_exec(path: &str, args: &[*const u8]) -> isize { + syscall(SYSCALL_EXEC, [path.as_ptr() as usize, args.as_ptr() as usize, 0]) } pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize {